"""OldClientRepository module: contains the OldClientRepository class""" from ClientRepositoryBase import * class OldClientRepository(ClientRepositoryBase): """ This is the open-source ClientRepository as provided by CMU. It communicates with the ServerRepository in this same directory. If you are looking for the VR Studio's implementation of the client repository, look to OTPClientRepository (elsewhere). """ notify = DirectNotifyGlobal.directNotify.newCategory("ClientRepository") def __init__(self, dcFileNames = None): ClientRepositoryBase.__init__(self, dcFileNames = dcFileNames) # The DOID allocator. The CMU LAN server may choose to # send us a block of DOIDs. If it chooses to do so, then we # may create objects, using those DOIDs. self.DOIDbase = 0 self.DOIDnext = 0 self.DOIDlast = 0 def handleSetDOIDrange(self, di): self.DOIDbase = di.getUint32() self.DOIDlast = self.DOIDbase + di.getUint32() self.DOIDnext = self.DOIDbase def handleRequestGenerates(self, di): # When new clients join the zone of an object, they need to hear # about it, so we send out all of our information about objects in # that particular zone. assert self.DOIDnext < self.DOIDlast zone = di.getUint32() for obj in self.doId2do.values(): if obj.zone == zone: id = obj.doId if (self.isLocalId(id)): self.send(obj.dclass.clientFormatGenerate(obj, id, zone, [])) def createWithRequired(self, className, zoneId = 0, optionalFields=None): if self.DOIDnext >= self.DOIDlast: self.notify.error( "Cannot allocate a distributed object ID: all IDs used up.") return None id = self.DOIDnext self.DOIDnext = self.DOIDnext + 1 dclass = self.dclassesByName[className] classDef = dclass.getClassDef() if classDef == None: self.notify.error("Could not create an undefined %s object." % ( dclass.getName())) obj = classDef(self) obj.dclass = dclass obj.zone = zoneId obj.doId = id self.doId2do[id] = obj obj.generateInit() obj._retrieveCachedData() obj.generate() obj.announceGenerate() datagram = dclass.clientFormatGenerate(obj, id, zoneId, optionalFields) self.send(datagram) return obj def sendDisableMsg(self, doId): datagram = PyDatagram() datagram.addUint16(CLIENT_OBJECT_DISABLE) datagram.addUint32(doId) self.send(datagram) def sendDeleteMsg(self, doId): datagram = PyDatagram() datagram.addUint16(CLIENT_OBJECT_DELETE) datagram.addUint32(doId) self.send(datagram) def sendRemoveZoneMsg(self, zoneId, visibleZoneList=None): datagram = PyDatagram() datagram.addUint16(CLIENT_REMOVE_ZONE) datagram.addUint32(zoneId) # if we have an explicit list of visible zones, add them if visibleZoneList is not None: vzl = list(visibleZoneList) vzl.sort() assert PythonUtil.uniqueElements(vzl) for zone in vzl: datagram.addUint32(zone) # send the message self.send(datagram) def sendUpdateZone(self, obj, zoneId): id = obj.doId assert self.isLocalId(id) self.sendDeleteMsg(id, 1) obj.zone = zoneId self.send(obj.dclass.clientFormatGenerate(obj, id, zoneId, [])) def sendSetZoneMsg(self, zoneId, visibleZoneList=None): datagram = PyDatagram() # Add message type datagram.addUint16(CLIENT_SET_ZONE_CMU) # Add zone id datagram.addUint32(zoneId) # if we have an explicit list of visible zones, add them if visibleZoneList is not None: vzl = list(visibleZoneList) vzl.sort() assert PythonUtil.uniqueElements(vzl) for zone in vzl: datagram.addUint32(zone) # send the message self.send(datagram) def isLocalId(self, id): return ((id >= self.DOIDbase) and (id < self.DOIDlast)) def haveCreateAuthority(self): return (self.DOIDlast > self.DOIDnext) def handleDatagram(self, di): if self.notify.getDebug(): print "ClientRepository received datagram:" di.getDatagram().dumpHex(ostream) msgType = self.getMsgType() # These are the sort of messages we may expect from the public # Panda server. if msgType == CLIENT_SET_DOID_RANGE: self.handleSetDOIDrange(di) elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_RESP: self.handleGenerateWithRequired(di) elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER_RESP: self.handleGenerateWithRequiredOther(di) elif msgType == CLIENT_OBJECT_UPDATE_FIELD_RESP: self.handleUpdateField(di) elif msgType == CLIENT_OBJECT_DELETE_RESP: self.handleDelete(di) elif msgType == CLIENT_OBJECT_DISABLE_RESP: self.handleDisable(di) elif msgType == CLIENT_REQUEST_GENERATES: self.handleRequestGenerates(di) else: self.handleMessageType(msgType, di) # If we're processing a lot of datagrams within one frame, we # may forget to send heartbeats. Keep them coming! self.considerHeartbeat() def handleGenerateWithRequired(self, di): # Get the class Id classId = di.getUint16() # Get the DO Id doId = di.getUint32() # Look up the dclass dclass = self.dclassesByNumber[classId] dclass.startGenerate() # Create a new distributed object, and put it in the dictionary distObj = self.generateWithRequiredFields(dclass, doId, di) dclass.stopGenerate() def generateWithRequiredFields(self, dclass, doId, di): if doId in self.doId2do: # ...it is in our dictionary. # Just update it. distObj = self.doId2do[doId] assert distObj.dclass == dclass distObj.generate() distObj.updateRequiredFields(dclass, di) # updateRequiredFields calls announceGenerate elif self.cache.contains(doId): # ...it is in the cache. # Pull it out of the cache: distObj = self.cache.retrieve(doId) assert distObj.dclass == dclass # put it in the dictionary: self.doId2do[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.getClassDef() if classDef == None: self.notify.error("Could not create an undefined %s object." % ( dclass.getName())) distObj = classDef(self) distObj.dclass = dclass # Assign it an Id distObj.doId = doId # Put the new do in the dictionary self.doId2do[doId] = distObj # Update the required fields distObj.generateInit() # Only called when constructed distObj.generate() distObj.updateRequiredFields(dclass, di) # updateRequiredFields calls announceGenerate return distObj