From e4a83aaa6ef344f3315ebf84c8ec8e1fd526b2fc Mon Sep 17 00:00:00 2001 From: Sebastian Hoffmann Date: Fri, 28 Feb 2014 05:43:27 +0100 Subject: [PATCH] Message types. Skeleton of AstronClientRepository.py. --- .../src/distributed/AstronClientRepository.py | 192 ++++++++++++++++++ direct/src/distributed/MsgTypesAstron.py | 32 +++ 2 files changed, 224 insertions(+) create mode 100644 direct/src/distributed/AstronClientRepository.py create mode 100644 direct/src/distributed/MsgTypesAstron.py diff --git a/direct/src/distributed/AstronClientRepository.py b/direct/src/distributed/AstronClientRepository.py new file mode 100644 index 0000000000..85a5938ae9 --- /dev/null +++ b/direct/src/distributed/AstronClientRepository.py @@ -0,0 +1,192 @@ +"""AstronClientRepository module: contains the AstronClientRepository class""" + +from ClientRepositoryBase import ClientRepositoryBase +from MsgTypesAstron import * + +class AstronClientRepository(ClientRepositoryBase): + """ + The Astron implementation of a clients repository for + communication with an Astron ClientAgent. + """ + + def __init__(self, *args, **kwargs): + ClientRepositoryBase.__init__(self, *args, **kwargs) + base.finalExitCallbacks.append(self.shutdown) + # FIXME: Partial code from ClientRepository.py may be required here. + + # + # Message Handling + # + + def handleDatagram(self, di): + + msgType = self.getMsgType() + + # These are the messages to a client that Astron specifies. + + if msgType == CLIENT_HELLO_RESP: + messenger.send("CLIENT_HELLO_RESP", []) + elif msgType == CLIENT_EJECT: + error_code = di.get_uint16() + reason = di.get_string() + messenger.send("CLIENT_EJECT", [error_code, reason]) + elif msgType == CLIENT_ENTER_OBJECT_REQUIRED: + self.handleEnterObjectRequired(di) + elif msgType == CLIENT_ENTER_OBJECT_REQUIRED_OWNER: + self.handleEnterObjectRequiredOwner(di) + # FIXME: These are supposedly handled in cConnectionRepository + #elif msgType == CLIENT_OBJECT_SET_FIELD: + # self.handleObjectSetField(di) + #elif msgType == CLIENT_OBJECT_SET_FIELDS: + # self.handleObjectSetFields(di) + elif msgType == CLIENT_OBJECT_LEAVING: + self.handleObjectLeaving(di) + elif msgType == CLIENT_OBJECT_LOCATION: + self.handleObjectLocation(di) + elif msgType == CLIENT_ADD_INTEREST: + self.handleAddInterest(di) + elif msgType == CLIENT_ADD_INTEREST_MULTIPLE: + self.handleAddInterestMultiple(di) + elif msgType == CLIENT_REMOVE_INTEREST: + self.handleRemoveInterest(di) + else: + self.handleMessageType(msgType, di) + + self.considerHeartbeat() + + def handleEnterObjectRequired(di) + do_id = di.getArg(STUint32) + parent_id = di.getArg(STUint32) + zone_id = di.getArg(STUint32) + dclass_id = di.getArg(STUint16) + dclass = self.dclassesByNumber[dclass_id] + self.generateWithRequiredFields(dclass, do_id, di, parent_id, zone_id) + + def handleEnterObjectRequiredOwner(di) + avatar_doId = di.getArg(STUint32) + parentId = di.getArg(STUint32) + zoneId = di.getArg(STUint32) + dclass_id = di.getArg(STUint16) + dclass = self.dclassesByNumber[dclass_id] + self.generateWithRequiredFieldsOwner(dclass, avatar_doId, di) + + def generateWithRequiredFieldsOwner(self, dclass, doId, di): + if doId in self.doId2ownerView: + # ...it is in our dictionary. + # Just update it. + self.notify.error('duplicate owner generate for %s (%s)' % ( + doId, dclass.getName())) + distObj = self.doId2ownerView[doId] + assert distObj.dclass == dclass + distObj.generate() + distObj.updateRequiredFields(dclass, di) + # updateRequiredFields calls announceGenerate + elif self.cacheOwner.contains(doId): + # ...it is in the cache. + # Pull it out of the cache: + distObj = self.cacheOwner.retrieve(doId) + assert distObj.dclass == dclass + # put it in the dictionary: + self.doId2ownerView[doId] = distObj + # and update it. + distObj.generate() + distObj.updateRequiredFields(dclass, di) + # updateRequiredFields calls announceGenerate + else: + # ...it is not in the dictionary or the cache. + # Construct a new one + classDef = dclass.getOwnerClassDef() + if classDef == None: + self.notify.error("Could not create an undefined %s object. Have you created an owner view?" % (dclass.getName())) + distObj = classDef(self) + distObj.dclass = dclass + # Assign it an Id + distObj.doId = doId + # Put the new do in the dictionary + self.doId2ownerView[doId] = distObj + # Update the required fields + distObj.generateInit() # Only called when constructed + distObj.generate() + distObj.updateRequiredFields(dclass, di) + # updateRequiredFields calls announceGenerate + return distObj + + def handleObjectSetField(di): + do_id = di.getArg(STUint32) + field_id = di.getArg(STUint16) + # FIXME: Is this really the best way? + do = self.doId2do[do_id] + # FIXME: Figure out field types and unpack them, or use + # some code that auto-unpacks them. + field_name = self.get_dc_file().get_field_by_index(field_id).get_name() + # FIXME: You actually need to figure out the fields types and unpack them! + self.sendUpdate(field_name, [fieldArguments]) + + def handleObjectSetFields(di): + # FIXME: Implement this! + pass + + def handleObjectLeaving(di): + # FIXME: Implement this! + pass + + def handleObjectLocation(di): + # FIXME: Implement this! + pass + + def handleAddInterest(di): + # FIXME: Implement this! + pass + + def handleAddInterestMultiple(di): + # FIXME: Implement this! + pass + + def handleRemoveInterest(di): + # FIXME: Implement this! + pass + + # + # Sending messages + # + + # FIXME: The version string should default to a .prc variable. + def sendHello(self, version_string): + dg = PyDatagram() + dg.add_uint16(MsgTypes.CLIENT_HELLO) + dg.add_uint32(self.get_dc_file().get_hash()) + dg.add_string(version_string) + self.send(dg) + + def sendHeartbeat(self): + datagram = PyDatagram() + datagram.addUint16(CLIENT_HEARTBEAT) + self.send(datagram) + + def sendAddInterest(self, context, interest_id, parent_id, zone_id): + dg = PyDatagram() + dg.add_uint16(MsgTypes.CLIENT_ADD_INTEREST) + dg.add_uint32(context) + dg.add_uint16(interest_id) + dg.add_uint32(parent_id) + dg.add_uint32(zone_id) + self.send(dg) + + def sendAddInterestMultiple(self, context, interest_id, parent_id, zone_ids): + dg = PyDatagram() + dg.add_uint16(MsgTypes.CLIENT_ADD_INTEREST_MULTIPLE) + dg.add_uint32(context) + dg.add_uint16(interest_id) + dg.add_uint32(parent_id) + dg.add_uint16(len(zone_ids)) + for zone_id in zone_ids: + dg.add_uint32(zone_id) + self.send(dg) + + def sendRemoveInterest(self, context, interest_id): + dg = PyDatagram() + dg.add_uint16(MsgTypes.CLIENT_REMOVE_INTEREST) + dg.add_uint32(context) + dg.add_uint16(interest_id) + self.send(dg) + diff --git a/direct/src/distributed/MsgTypesAstron.py b/direct/src/distributed/MsgTypesAstron.py new file mode 100644 index 0000000000..5e94797361 --- /dev/null +++ b/direct/src/distributed/MsgTypesAstron.py @@ -0,0 +1,32 @@ +"""MsgTypesAstron module: contains distributed object message types""" + +from direct.showbase.PythonUtil import invertDictLossless + +MsgName2Id = { + 'CLIENT_HELLO': 1, + 'CLIENT_HELLO_RESP': 2, + 'CLIENT_DISCONNECT': 3, + 'CLIENT_EJECT': 4, + 'CLIENT_HEARTBEAT': 5, + 'CLIENT_ENTER_OBJECT_REQUIRED': 142, + 'CLIENT_ENTER_OBJECT_REQUIRED_OTHER': 143, + 'CLIENT_ENTER_OBJECT_REQUIRED_OWNER': 172, + 'CLIENT_ENTER_OBJECT_REQUIRED_OTHER_OWNER': 173, + 'CLIENT_OBJECT_SET_FIELD': 120, + 'CLIENT_OBJECT_SET_FIELDS': 121, + 'CLIENT_OBJECT_LEAVING': 132, + 'CLIENT_OBJECT_LOCATION': 140, + 'CLIENT_ADD_INTEREST': 200, + 'CLIENT_ADD_INTEREST_MULTIPLE': 201, + 'CLIENT_REMOVE_INTEREST': 203, + 'CLIENT_DONE_INTEREST_RESP': 204, + } + +# create id->name table for debugging +MsgId2Names = invertDictLossless(MsgName2Id) + +# put msg names in module scope, assigned to msg value +for name, value in MsgName2Id.items(): + exec '%s = %s' % (name, value) +del name, value +