From f74dc9b35d3d9a4af7008f8a97b4c61b8f2b18e6 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Tue, 15 Oct 2013 04:10:40 -0600 Subject: [PATCH] Add AstronDatabaseInterface, which can generate objects into an Astron DB. --- .../distributed/AstronDatabaseInterface.py | 79 +++++++++++++++++++ .../distributed/AstronInternalRepository.py | 8 +- 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 direct/src/distributed/AstronDatabaseInterface.py diff --git a/direct/src/distributed/AstronDatabaseInterface.py b/direct/src/distributed/AstronDatabaseInterface.py new file mode 100644 index 0000000000..6bde5c20d0 --- /dev/null +++ b/direct/src/distributed/AstronDatabaseInterface.py @@ -0,0 +1,79 @@ +from pandac.PandaModules import * +from MsgTypes import * +from direct.directnotify import DirectNotifyGlobal +from ConnectionRepository import ConnectionRepository +from PyDatagram import PyDatagram +from PyDatagramIterator import PyDatagramIterator + +class AstronDatabaseInterface: + """ + This class is part of Panda3D's new MMO networking framework. + It interfaces with Astron database(s) to manage objects directly, rather than + via DB-StateServers. + + Do not create this class directly; instead, use AstronInternalRepository's + dbInterface attribute. + """ + notify = DirectNotifyGlobal.directNotify.newCategory("AstronDatabaseInterface") + + def __init__(self, air): + self.air = air + + self._callbacks = {} + + def createObject(self, databaseId, dclass, fields={}, callback=None): + """ + Create an object in the specified database. + + databaseId specifies the control channel of the target database. + dclass specifies the class of the object to be created. + fields is a dict with any fields that should be stored in the object on creation. + callback will be called with callback(doId) if specified. On failure, doId is 0. + """ + + # Save the callback: + ctx = self.air.contextAllocator.allocate() + self._callbacks[ctx] = callback + + # Pack up/count valid fields. + fieldPacker = DCPacker() + fieldCount = 0 + for k,v in fields.items(): + field = dclass.getFieldByName(k) + if not field: + self.notify.warning('Creation request for %s object contains ' + 'invalid field named %s' % (dclass.getName(), k)) + continue + + fieldPacker.rawPackUint16(field.getNumber()) + fieldPacker.beginPack(field) + field.packArgs(fieldPacker, v) + fieldPacker.endPack() + fieldCount += 1 + + # Now generate and send the datagram: + dg = PyDatagram() + dg.addServerHeader(databaseId, self.air.ourChannel, DBSERVER_OBJECT_CREATE) + dg.addUint32(ctx) + dg.addUint16(dclass.getNumber()) + dg.addUint16(fieldCount) + dg.appendData(fieldPacker.getString()) + self.air.send(dg) + + def handleCreateObjectResp(self, di): + ctx = di.getUint32() + doId = di.getUint32() + + if ctx not in self._callbacks: + self.notify.warning('Received unexpected DBSERVER_OBJECT_CREATE_RESP' + ' (ctx %d, doId %d)' % (ctx, doId)) + return + + self._callbacks[ctx](doId) + + del self._callbacks[ctx] + self.air.contextAllocator.free(ctx) + + def handleDatagram(self, msgType, di): + if msgType == DBSERVER_OBJECT_CREATE_RESP: + self.handleCreateObjectResp(di) diff --git a/direct/src/distributed/AstronInternalRepository.py b/direct/src/distributed/AstronInternalRepository.py index 178cf1e994..60fd903aad 100644 --- a/direct/src/distributed/AstronInternalRepository.py +++ b/direct/src/distributed/AstronInternalRepository.py @@ -4,6 +4,7 @@ from direct.directnotify import DirectNotifyGlobal from ConnectionRepository import ConnectionRepository from PyDatagram import PyDatagram from PyDatagramIterator import PyDatagramIterator +from AstronDatabaseInterface import AstronDatabaseInterface class AstronInternalRepository(ConnectionRepository): """ @@ -40,6 +41,10 @@ class AstronInternalRepository(ConnectionRepository): self.channelAllocator = UniqueIdAllocator(baseChannel, baseChannel+maxChannels-1) self._registeredChannels = set() + self.contextAllocator = UniqueIdAllocator(0, 100) + + self.dbInterface = AstronDatabaseInterface(self) + self.ourChannel = self.allocateChannel() self.eventLogId = self.config.GetString('eventlog-id', 'AIR:%d' % self.ourChannel) @@ -137,7 +142,8 @@ class AstronInternalRepository(ConnectionRepository): self.handleObjEntry(di) elif msgType == STATESERVER_OBJECT_LEAVING_AI_INTEREST: self.handleObjExit(di) - + elif msgType in (DBSERVER_OBJECT_CREATE_RESP,): + self.dbInterface.handleDatagram(msgType, di) def handleObjEntry(self, di): parentId = di.getUint32()