diff --git a/direct/src/distributed/ClientRepository.py b/direct/src/distributed/ClientRepository.py index 7d5a321205..7bb13ac5d9 100644 --- a/direct/src/distributed/ClientRepository.py +++ b/direct/src/distributed/ClientRepository.py @@ -22,8 +22,12 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): self.setClientDatagram(1) self.recorder = base.recorder - - self.doId2do={} + + # Dict of {DistributedObject ids : DistributedObjects} + self.doId2do = {} + if wantOtpServer: + # Dict of {parent DistributedObject id : {zoneIds : [child DistributedObject ids]}} + self.__doHierarchy = {} self.readDCFile() self.cache=CRCache.CRCache() self.serverDelta = 0 @@ -138,52 +142,132 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): it should be accurate plus or minus a couple of seconds. """ return time.time() + self.serverDelta - - def handleObjectLocation(self, di): - # CLEINT_OBJECT_LOCATION - ThedoId = di.getUint32() - TheParent = di.getUint32() - TheZone = di.getUint32() - print "Object Location->Id=%s Parent=%s Zone=%s"%(ThedoId,TheParent, TheZone) - + + if wantOtpServer: + def handleObjectLocation(self, di): + # CLIENT_OBJECT_LOCATION + doId = di.getUint32() + parentId = di.getUint32() + zoneId = di.getUint32() + obj = self.doId2do.get(doId) + if (obj != None): + self.notify.info("handleObjectLocation: doId: %s parentId: %s zoneId: %s" % + (doId, parentId, zoneId)) + # Let the object finish the job + obj.setLocation(parentId, zoneId) + else: + ClientRepository.notify.warning( + "handleObjectLocation: Asked to update non-existent obj: %s" % (doId)) + + def storeObjectLocation(self, objId, parentId, zoneId): + # Do not store null values + if ((parentId is None) or (zoneId is None)): + return + + # TODO: check current location + obj = self.doId2do.get(objId) + oldParentId, oldZoneId = obj.getLocation() + + # Case 1: Same parent, new zone + if (oldParentId == parentId): + parentZoneDict = self.__doHierarchy.get(parentId) + # Remove this objId from the old zone list + oldObjList = parentZoneDict.get(oldZoneId) + oldObjList.remove(objId) + # Add it to the new zone list + objList = parentZoneDict.get(zoneId) + if objList is None: + # No existing objList for this zone, let's make a new one + parentZoneDict[zoneId] = [objId] + return + else: + # Just add this objId to the existing list + assert(objId not in objList) + objList.append(objId) + return + + # Case 2: New parent, valid old parent + # First delete the old location + if ((oldParentId is not None) and (oldZoneId is not None)): + self.deleteObjectLocation(objId, oldParentId, oldZoneId) + # Do not return because we still need to add to the new location + + # Case 2: continued, already deleted from old location + # Case 3: New parent - no old parent + parentZoneDict = self.__doHierarchy.get(parentId) + if parentZoneDict is None: + # This parent is not here, just fill the whole entry in + self.__doHierarchy[parentId] = {zoneId : [objId]} + else: + objList = parentZoneDict.get(zoneId) + if objList is None: + # This parent has no objects in this zone before + # create a new entry for this zone and list this objId + parentZoneDict[zoneId] = [objId] + else: + # Just add this objId to the existing list + objList.append(objId) + + def deleteObjectLocation(self, objId, parentId, zoneId): + # Do not worry about null values + if ((parentId is None) or (zoneId is None)): + return + parentZoneDict = self.__doHierarchy.get(parentId) + assert(parentZoneDict is not None, "deleteObjectLocation: parentId: %s not found" % (parentId)) + objList = parentZoneDict.get(zoneId) + assert(objList is not None, "deleteObjectLocation: zoneId: %s not found" % (zoneId)) + assert(objId in objList, "deleteObjectLocation: objId: %s not found" % (objId)) + if len(objList) == 1: + # If this is the last obj in this zone, delete the entire entry + del parentZoneDict[zoneId] + else: + # Just remove the object + objList.remove(objId) + def handleGenerateWithRequired(self, di): - if wantOtpServer: - TheParent = di.getUint32() - TheZone = di.getUint32() + if wantOtpServer: + parentId = di.getUint32() + zoneId = di.getUint32() # Get the class Id - classId = di.getUint16(); + 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) + if wantOtpServer: + distObj = self.generateWithRequiredFields(dclass, doId, di, parentId, zoneId) + else: + distObj = self.generateWithRequiredFields(dclass, doId, di) dclass.stopGenerate() def handleGenerateWithRequiredOther(self, di): if wantOtpServer: - TheParent = di.getUint32() - TheZone = di.getUint32() + parentId = di.getUint32() + zoneId = di.getUint32() # Get the class Id - classId = di.getUint16(); + 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.generateWithRequiredOtherFields(dclass, doId, di) + if wantOtpServer: + distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId) + else: + distObj = self.generateWithRequiredOtherFields(dclass, doId, di) dclass.stopGenerate() def handleQuietZoneGenerateWithRequired(self, di): # Special handler for quiet zone generates -- we need to filter if wantOtpServer: - TheParent = di.getUint32() - TheZone = di.getUint32() + parentId = di.getUint32() + zoneId = di.getUint32() # Get the class Id - classId = di.getUint16(); + classId = di.getUint16() # Get the DO Id doId = di.getUint32() # Look up the dclass @@ -196,16 +280,16 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): # Create a new distributed object, and put it in the dictionary distObj = self.generateWithRequiredFields(dclass, doId, di) else: - distObj = self.generateWithRequiredFields(dclass, doId, di) + distObj = self.generateWithRequiredFields(dclass, doId, di, parentId, zoneId) dclass.stopGenerate() def handleQuietZoneGenerateWithRequiredOther(self, di): # Special handler for quiet zone generates -- we need to filter if wantOtpServer: - TheParent = di.getUint32() - TheZone = di.getUint32() + parentId = di.getUint32() + zoneId = di.getUint32() # Get the class Id - classId = di.getUint16(); + classId = di.getUint16() # Get the DO Id doId = di.getUint32() # Look up the dclass @@ -218,15 +302,18 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): # Create a new distributed object, and put it in the dictionary distObj = self.generateWithRequiredOtherFields(dclass, doId, di) else: - distObj = self.generateWithRequiredOtherFields(dclass, doId, di) + distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId) dclass.stopGenerate() - def generateWithRequiredFields(self, dclass, doId, di): + # wantOtpServer: remove the None defaults when we remove this config variable + def generateWithRequiredFields(self, dclass, doId, di, parentId = None, zoneId = None): if self.doId2do.has_key(doId): # ...it is in our dictionary. # Just update it. distObj = self.doId2do[doId] assert(distObj.dclass == dclass) + if wantOtpServer: + distObj.setLocation(parentId, zoneId) distObj.generate() distObj.updateRequiredFields(dclass, di) # updateRequiredFields calls announceGenerate @@ -238,6 +325,8 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): # put it in the dictionary: self.doId2do[doId] = distObj # and update it. + if wantOtpServer: + distObj.setLocation(parentId, zoneId) distObj.generate() distObj.updateRequiredFields(dclass, di) # updateRequiredFields calls announceGenerate @@ -254,6 +343,8 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): # Put the new do in the dictionary self.doId2do[doId] = distObj # Update the required fields + if wantOtpServer: + distObj.setLocation(parentId, zoneId) distObj.generateInit() # Only called when constructed distObj.generate() distObj.updateRequiredFields(dclass, di) @@ -279,17 +370,24 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): # Put the new do in the dictionary self.doId2do[doId] = distObj # Update the required fields + if wantOtpServer: + # TODO: ROGER: where should we get parentId and zoneId? + parentId = None + zoneId = None + distObj.setLocation(parentId, zoneId) distObj.generateInit() # Only called when constructed distObj.generate() # updateRequiredFields calls announceGenerate return distObj - def generateWithRequiredOtherFields(self, dclass, doId, di): + def generateWithRequiredOtherFields(self, dclass, doId, di, parentId = None, zoneId = None): if self.doId2do.has_key(doId): # ...it is in our dictionary. # Just update it. distObj = self.doId2do[doId] assert(distObj.dclass == dclass) + if wantOtpServer: + distObj.setLocation(parentId, zoneId) distObj.generate() distObj.updateRequiredOtherFields(dclass, di) # updateRequiredOtherFields calls announceGenerate @@ -301,6 +399,8 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): # put it in the dictionary: self.doId2do[doId] = distObj # and update it. + if wantOtpServer: + distObj.setLocation(parentId, zoneId) distObj.generate() distObj.updateRequiredOtherFields(dclass, di) # updateRequiredOtherFields calls announceGenerate @@ -317,6 +417,8 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): # Put the new do in the dictionary self.doId2do[doId] = distObj # Update the required fields + if wantOtpServer: + distObj.setLocation(parentId, zoneId) distObj.generateInit() # Only called when constructed distObj.generate() distObj.updateRequiredOtherFields(dclass, di) @@ -467,7 +569,7 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): self.handleGenerateWithRequiredOther(di) elif wantOtpServer and msgType == CLIENT_DONE_SET_ZONE_RESP: self.handleSetZoneDone() - elif wantOtpServer and msgType == CLEINT_OBJECT_LOCATION: + elif wantOtpServer and msgType == CLIENT_OBJECT_LOCATION: self.handleObjectLocation(di) else: currentLoginState = self.loginFSM.getCurrentState() @@ -541,22 +643,22 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): ## interest managment ## ## - def InterestAdd(self, parentId, zoneId, Description): + def InterestAdd(self, parentId, zoneId, Description): """ Part of the new otp-server code. """ self.__interest_id_assign += 1 self.__interesthash[self.__interest_id_assign] = Description - contextId = self.__interest_id_assign; + contextId = self.__interest_id_assign self.__sendAddInterest(contextId, parentId, zoneId) self.DumpInterests() - return contextId; + return contextId def InterestRemove(self, contextId): """ Part of the new otp-server code. """ - answer = 0; + answer = 0 if self.__interesthash.has_key(contextId): self.__sendRemoveInterest(contextId) del self.__interesthash[contextId] diff --git a/direct/src/distributed/DistributedObject.py b/direct/src/distributed/DistributedObject.py index 09a6a9bf2a..09c6e49b20 100644 --- a/direct/src/distributed/DistributedObject.py +++ b/direct/src/distributed/DistributedObject.py @@ -31,6 +31,9 @@ class DistributedObject(PandaObject): except: self.DistributedObject_initialized = 1 self.cr = cr + if wantOtpServer: + # Location stores the parentId, zoneId of this object + self.__location = (None, None) # Most DistributedObjects are simple and require no real # effort to load. Some, particularly actors, may take @@ -150,6 +153,10 @@ class DistributedObject(PandaObject): assert(self.notify.debug('disable(): %s' % (self.doId))) self.activeState = ESDisabled self.__callbacks = {} + if wantOtpServer: + self.cr.deleteObjectLocation(self.doId, self.__location[0], self.__location[1]) + self.__location = (None, None) + # TODO: disable my children def isDisabled(self): """ @@ -325,5 +332,13 @@ class DistributedObject(PandaObject): if self.__barrierContext != None: self.sendUpdate("setBarrierReady", [self.__barrierContext]) self.__barrierContext = None - - + + if wantOtpServer: + def setLocation(self, parentId, zoneId): + # The store must run first so we know the old location + self.cr.storeObjectLocation(self.doId, parentId, zoneId) + self.__location = (parentId, zoneId) + + def getLocation(self): + return self.__location + diff --git a/direct/src/distributed/MsgTypes.py b/direct/src/distributed/MsgTypes.py index db330eeaf9..07472690bf 100644 --- a/direct/src/distributed/MsgTypes.py +++ b/direct/src/distributed/MsgTypes.py @@ -72,12 +72,11 @@ CLIENT_GET_PET_DETAILS = 81 CLIENT_GET_PET_DETAILS_RESP = 82 # (Proposed new message): CLIENT_SET_WORLD_POS = 83 - -CLIENT_ADD_INTEREST = 97 -CLEINT_ALTER_INTEREST = 98 -CLEINT_REMOVE_INTEREST = 99 if wantOtpServer: - CLEINT_OBJECT_LOCATION = 102 + CLIENT_ADD_INTEREST = 97 + CLIENT_ALTER_INTEREST = 98 + CLIENT_REMOVE_INTEREST = 99 + CLIENT_OBJECT_LOCATION = 102 # These messages are ignored when the client is headed to the quiet zone QUIET_ZONE_IGNORED_LIST = [