handle updates without touching python

This commit is contained in:
David Rose 2004-05-19 23:16:37 +00:00
parent 260dbe60d7
commit 69e04be3af
8 changed files with 298 additions and 40 deletions

View File

@ -45,10 +45,25 @@ DCFile() {
////////////////////////////////////////////////////////////////////
DCFile::
~DCFile() {
clear();
}
////////////////////////////////////////////////////////////////////
// Function: DCFile::clear
// Access: Published
// Description: Removes all of the classes defined within the DCFile
// and prepares it for reading a new file.
////////////////////////////////////////////////////////////////////
void DCFile::
clear() {
Classes::iterator ci;
for (ci = _classes.begin(); ci != _classes.end(); ++ci) {
delete (*ci);
}
_classes.clear();
_imports.clear();
_classes_by_name.clear();
}
#ifdef WITHIN_PANDA

View File

@ -34,6 +34,8 @@ PUBLISHED:
DCFile();
~DCFile();
void clear();
#ifdef WITHIN_PANDA
bool read_all();
#endif

View File

@ -19,6 +19,7 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
def __init__(self):
ConnectionRepository.ConnectionRepository.__init__(self, base.config)
self.setClientDatagram(1)
self.recorder = base.recorder
@ -95,9 +96,9 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
def handleGenerateWithRequired(self, di):
# Get the class Id
classId = di.getArg(STUint16);
classId = di.getUint16();
# Get the DO Id
doId = di.getArg(STUint32)
doId = di.getUint32()
# Look up the dclass
dclass = self.dclassesByNumber[classId]
# Create a new distributed object, and put it in the dictionary
@ -105,9 +106,9 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
def handleGenerateWithRequiredOther(self, di):
# Get the class Id
classId = di.getArg(STUint16);
classId = di.getUint16();
# Get the DO Id
doId = di.getArg(STUint32)
doId = di.getUint32()
# Look up the dclass
dclass = self.dclassesByNumber[classId]
# Create a new distributed object, and put it in the dictionary
@ -116,9 +117,9 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
def handleQuietZoneGenerateWithRequired(self, di):
# Special handler for quiet zone generates -- we need to filter
# Get the class Id
classId = di.getArg(STUint16);
classId = di.getUint16();
# Get the DO Id
doId = di.getArg(STUint32)
doId = di.getUint32()
# Look up the dclass
dclass = self.dclassesByNumber[classId]
# If the class is a neverDisable class (which implies uberzone) we
@ -130,9 +131,9 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
def handleQuietZoneGenerateWithRequiredOther(self, di):
# Special handler for quiet zone generates -- we need to filter
# Get the class Id
classId = di.getArg(STUint16);
classId = di.getUint16();
# Get the DO Id
doId = di.getArg(STUint32)
doId = di.getUint32()
# Look up the dclass
dclass = self.dclassesByNumber[classId]
# If the class is a neverDisable class (which implies uberzone) we
@ -222,7 +223,7 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
def handleDisable(self, di):
# Get the DO Id
doId = di.getArg(STUint32)
doId = di.getUint32()
# disable it.
self.disableDoId(doId)
@ -249,7 +250,7 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
def handleDelete(self, di):
# Get the DO Id
doId = di.getArg(STUint32)
doId = di.getUint32()
self.deleteObject(doId)
def deleteObject(self, doId):
@ -285,7 +286,7 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
def handleUpdateField(self, di):
# Get the DO Id
doId = di.getArg(STUint32)
doId = di.getUint32()
#print("Updating " + str(doId))
# Find the DO
@ -368,14 +369,13 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
# send the message
self.send(datagram)
def handleDatagram(self, datagram):
def handleDatagram(self, di):
if self.notify.getDebug():
print "ClientRepository received datagram:"
datagram.dumpHex(ostream)
di = PyDatagramIterator(datagram)
msgType = di.getUint16()
if self.notify.getDebug():
self.notify.debug("handleDatagram: msgType: " + `msgType`)
di.getDatagram().dumpHex(ostream)
msgType = self.getMsgType()
# watch for setZoneDones
if msgType == CLIENT_DONE_SET_ZONE_RESP:
self.handleSetZoneDone()

View File

@ -3,6 +3,7 @@ import Task
import DirectNotifyGlobal
import DirectObject
from PyDatagram import PyDatagram
from PyDatagramIterator import PyDatagramIterator
import types
@ -20,6 +21,7 @@ class ConnectionRepository(DirectObject.DirectObject, CConnectionRepository):
def __init__(self, config):
DirectObject.DirectObject.__init__(self)
CConnectionRepository.__init__(self)
self.setPythonRepository(self)
self.config = config
@ -44,44 +46,48 @@ class ConnectionRepository(DirectObject.DirectObject, CConnectionRepository):
self.connectHttp = None
self.http = None
# This DatagramIterator is constructed once, and then re-used
# each time we read a datagram.
self.__di = PyDatagramIterator()
self.recorder = None
def readDCFile(self, dcFileNames = None):
""" Reads in the dc files listed in dcFileNames, or if
dcFileNames is None, reads in all of the dc files listed in
the Configrc file.
The resulting DCFile object is stored in self.dcFile. """
the Configrc file. """
self.dcFile = DCFile()
dcFile = self.getDcFile()
dcFile.clear()
self.dclassesByName = {}
self.dclassesByNumber = {}
self.hashVal = 0
dcImports = {}
if dcFileNames == None:
readResult = self.dcFile.readAll()
readResult = dcFile.readAll()
if not readResult:
self.notify.error("Could not read dc file.")
else:
for dcFileName in dcFileNames:
readResult = self.dcFile.read(Filename(dcFileName))
readResult = dcFile.read(Filename(dcFileName))
if not readResult:
self.notify.error("Could not read dc file: %s" % (dcFileName))
self.hashVal = self.dcFile.getHash()
self.hashVal = dcFile.getHash()
# Now import all of the modules required by the DC file.
for n in range(self.dcFile.getNumImportModules()):
moduleName = self.dcFile.getImportModule(n)
for n in range(dcFile.getNumImportModules()):
moduleName = dcFile.getImportModule(n)
moduleName = self.mangleDCName(moduleName)
module = __import__(moduleName, globals(), locals())
if self.dcFile.getNumImportSymbols(n) > 0:
if dcFile.getNumImportSymbols(n) > 0:
# "from moduleName import symbolName, symbolName, ..."
# Copy just the named symbols into the dictionary.
for i in range(self.dcFile.getNumImportSymbols(n)):
symbolName = self.dcFile.getImportSymbol(n, i)
for i in range(dcFile.getNumImportSymbols(n)):
symbolName = dcFile.getImportSymbol(n, i)
if symbolName == '*':
# Get all symbols.
dcImports.update(module.__dict__)
@ -105,8 +111,8 @@ class ConnectionRepository(DirectObject.DirectObject, CConnectionRepository):
# Now get the class definition for the classes named in the DC
# file.
for i in range(self.dcFile.getNumClasses()):
dclass = self.dcFile.getClass(i)
for i in range(dcFile.getNumClasses()):
dclass = dcFile.getClass(i)
number = dclass.getNumber()
className = dclass.getName()
className = self.mangleDCName(className)
@ -298,9 +304,8 @@ class ConnectionRepository(DirectObject.DirectObject, CConnectionRepository):
def readerPollOnce(self):
if self.checkDatagram():
dg = PyDatagram()
self.getDatagram(dg)
self.handleDatagram(dg)
self.getDatagramIterator(self.__di)
self.handleDatagram(self.__di)
return 1
# Unable to receive a datagram: did we lose the connection?
@ -314,7 +319,7 @@ class ConnectionRepository(DirectObject.DirectObject, CConnectionRepository):
# unexpectedly lost connection to the gameserver.
self.notify.warning("Lost connection to gameserver.")
def handleDatagram(self, datagram):
def handleDatagram(self, di):
# This class is meant to be pure virtual, and any classes that
# inherit from it need to make their own handleDatagram method
pass

View File

@ -1,10 +1,12 @@
#define C++FLAGS -DWITHIN_PANDA
#begin lib_target
#define BUILD_TARGET $[HAVE_PYTHON]
#define USE_PACKAGES ssl nspr
#define TARGET distributed
#define LOCAL_LIBS \
directbase
directbase dcparser
#define OTHER_LIBS \
downloader:c net:c panda:m express:c pandaexpress:m \
interrogatedb:c dconfig:c dtoolconfig:m \

View File

@ -17,6 +17,57 @@
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: CConnectionRepository::get_dc_file
// Access: Published
// Description: Returns the DCFile object associated with this
// repository.
////////////////////////////////////////////////////////////////////
INLINE DCFile &CConnectionRepository::
get_dc_file() {
return _dc_file;
}
////////////////////////////////////////////////////////////////////
// Function: CConnectionRepository::set_client_datagram
// Access: Published
// Description: Sets the client_datagram flag. If this is true,
// incoming datagrams are not expected to be prefixed
// with the server routing information like message
// sender, channel number, etc.; otherwise, these server
// fields are parsed and removed from each incoming
// datagram.
////////////////////////////////////////////////////////////////////
INLINE void CConnectionRepository::
set_client_datagram(bool client_datagram) {
_client_datagram = client_datagram;
}
////////////////////////////////////////////////////////////////////
// Function: CConnectionRepository::get_client_datagram
// Access: Published
// Description: Returns the client_datagram flag.
////////////////////////////////////////////////////////////////////
INLINE bool CConnectionRepository::
get_client_datagram() const {
return _client_datagram;
}
#ifdef HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: CConnectionRepository::set_python_repository
// Access: Published
// Description: Records the pointer to the Python class that derives
// from CConnectionRepository. This allows the C++
// implementation to directly manipulation some python
// structures on the repository.
////////////////////////////////////////////////////////////////////
INLINE void CConnectionRepository::
set_python_repository(PyObject *python_repository) {
_python_repository = python_repository;
}
#endif // HAVE_PYTHON
#ifdef HAVE_NSPR
////////////////////////////////////////////////////////////////////
// Function: CConnectionRepository::get_qcm
@ -70,6 +121,67 @@ get_datagram(Datagram &dg) {
dg = _dg;
}
////////////////////////////////////////////////////////////////////
// Function: CConnectionRepository::get_datagram_iterator
// Access: Published
// Description: Fills the DatagramIterator object with the iterator
// for the datagram most recently retrieved by
// check_datagram(). This iterator has already read
// past the datagram header and the message type, and is
// positioned at the beginning of data.
////////////////////////////////////////////////////////////////////
INLINE void CConnectionRepository::
get_datagram_iterator(DatagramIterator &di) {
di = _di;
}
////////////////////////////////////////////////////////////////////
// Function: CConnectionRepository::get_msg_channel
// Access: Published
// Description: Returns the channel from which the current message
// was sent, according to the datagram headers. This
// information is not available to the client.
////////////////////////////////////////////////////////////////////
INLINE unsigned int CConnectionRepository::
get_msg_channel() const {
return _msg_channel;
}
////////////////////////////////////////////////////////////////////
// Function: CConnectionRepository::get_msg_sender
// Access: Published
// Description: Returns the sender ID of the current message,
// according to the datagram headers. This information
// is not available to the client.
////////////////////////////////////////////////////////////////////
INLINE unsigned int CConnectionRepository::
get_msg_sender() const {
return _msg_sender;
}
////////////////////////////////////////////////////////////////////
// Function: CConnectionRepository::get_sec_code
// Access: Published
// Description: Returns the security code associated with the current
// message, according to the datagram headers. This
// information is not available to the client.
////////////////////////////////////////////////////////////////////
INLINE unsigned char CConnectionRepository::
get_sec_code() const {
return _sec_code;
}
////////////////////////////////////////////////////////////////////
// Function: CConnectionRepository::get_msg_type
// Access: Published
// Description: Returns the type ID of the current message,
// according to the datagram headers.
////////////////////////////////////////////////////////////////////
INLINE unsigned int CConnectionRepository::
get_msg_type() const {
return _msg_type;
}
////////////////////////////////////////////////////////////////////
// Function: CConnectionRepository::set_simulated_disconnect
// Access: Published

View File

@ -17,9 +17,13 @@
////////////////////////////////////////////////////////////////////
#include "cConnectionRepository.h"
#include "dcmsgtypes.h"
#include "dcClass.h"
#include "config_distributed.h"
#include "httpChannel.h"
#include "urlSpec.h"
#include "datagramIterator.h"
////////////////////////////////////////////////////////////////////
// Function: CConnectionRepository::Constructor
@ -28,6 +32,9 @@
////////////////////////////////////////////////////////////////////
CConnectionRepository::
CConnectionRepository() :
#ifdef HAVE_PYTHON
_python_repository(NULL),
#endif
#ifdef HAVE_SSL
_http_conn(NULL),
#endif
@ -35,7 +42,12 @@ CConnectionRepository() :
_cw(&_qcm, 0),
_qcr(&_qcm, 0),
#endif
_simulated_disconnect(false)
_client_datagram(true),
_simulated_disconnect(false),
_msg_channel(0),
_msg_sender(0),
_sec_code(0),
_msg_type(0)
{
#ifdef HAVE_NSPR
if (min_lag != 0.0 || max_lag != 0.0) {
@ -103,7 +115,9 @@ try_connect_nspr(const URLSpec &url) {
// 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().
// datagram may be retrieved via get_datagram(), or
// preferably, with get_datagram_iterator() and
// get_msg_type().
////////////////////////////////////////////////////////////////////
bool CConnectionRepository::
check_datagram() {
@ -111,7 +125,46 @@ check_datagram() {
return false;
}
return do_check_datagram();
while (do_check_datagram()) {
// Start breaking apart the datagram.
_di = DatagramIterator(_dg);
if (!_client_datagram) {
_msg_channel = _di.get_uint32();
_msg_sender = _di.get_uint32();
_sec_code = _di.get_uint8();
#ifdef HAVE_PYTHON
// For now, we need to stuff this field onto the Python
// structure, to support legacy code that expects to find it
// there.
if (_python_repository != (PyObject *)NULL) {
PyObject *value = PyInt_FromLong(_msg_sender);
PyObject_SetAttrString(_python_repository, "msgSender", value);
Py_DECREF(value);
}
#endif // HAVE_PYTHON
}
_msg_type = _di.get_uint16();
// Is this a message that we can process directly?
switch (_msg_type) {
#ifdef HAVE_PYTHON
case CLIENT_OBJECT_UPDATE_FIELD:
case STATESERVER_OBJECT_UPDATE_FIELD:
handle_update_field();
break;
#endif // HAVE_PYTHON
default:
// Some unknown message; let the caller deal with it.
return true;
}
}
// No datagrams available.
return false;
}
////////////////////////////////////////////////////////////////////
@ -303,3 +356,42 @@ do_check_datagram() {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: CConnectionRepository::handle_update_field
// Access: Private
// Description: Directly handles an update message on a field.
// Python never touches the datagram; it just gets its
// distributed method called with the appropriate
// parameters.
////////////////////////////////////////////////////////////////////
void CConnectionRepository::
handle_update_field() {
#ifdef HAVE_PYTHON
int do_id = _di.get_uint32();
if (_python_repository != (PyObject *)NULL) {
PyObject *doId2do =
PyObject_GetAttrString(_python_repository, "doId2do");
nassertv(doId2do != NULL);
PyObject *doId = PyInt_FromLong(do_id);
PyObject *distobj = PyDict_GetItem(doId2do, doId);
Py_DECREF(doId);
Py_DECREF(doId2do);
if (distobj != NULL) {
PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass");
nassertv(dclass_obj != NULL);
PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this");
Py_DECREF(dclass_obj);
nassertv(dclass_this != NULL);
DCClass *dclass = (DCClass *)PyInt_AsLong(dclass_this);
Py_DECREF(dclass_this);
dclass->receive_update(distobj, _di);
}
}
#endif // HAVE_PYTHON
}

View File

@ -21,6 +21,8 @@
#include "directbase.h"
#include "pointerTo.h"
#include "dcFile.h"
#include "dcField.h" // to pick up Python.h
#ifdef HAVE_NSPR
#include "queuedConnectionManager.h"
@ -53,6 +55,15 @@ PUBLISHED:
CConnectionRepository();
~CConnectionRepository();
INLINE DCFile &get_dc_file();
INLINE void set_client_datagram(bool client_datagram);
INLINE bool get_client_datagram() const;
#ifdef HAVE_PYTHON
INLINE void set_python_repository(PyObject *python_repository);
#endif
#ifdef HAVE_SSL
void set_connection_http(HTTPChannel *channel);
#endif
@ -66,6 +77,12 @@ PUBLISHED:
bool check_datagram();
INLINE void get_datagram(Datagram &dg);
INLINE void get_datagram_iterator(DatagramIterator &di);
INLINE unsigned int get_msg_channel() const;
INLINE unsigned int get_msg_sender() const;
INLINE unsigned char get_sec_code() const;
INLINE unsigned int get_msg_type() const;
bool is_connected();
bool send_datagram(const Datagram &dg);
@ -80,6 +97,11 @@ PUBLISHED:
private:
bool do_check_datagram();
void handle_update_field();
#ifdef HAVE_PYTHON
PyObject *_python_repository;
#endif
#ifdef HAVE_SSL
SocketStream *_http_conn;
@ -92,8 +114,16 @@ private:
PT(Connection) _nspr_conn;
#endif
Datagram _dg;
DCFile _dc_file;
bool _client_datagram;
bool _simulated_disconnect;
Datagram _dg;
DatagramIterator _di;
unsigned int _msg_channel;
unsigned int _msg_sender;
unsigned char _sec_code;
unsigned int _msg_type;
};
#include "cConnectionRepository.I"