Переглянути джерело

OTPServer: client gets zone changes

Joe Shochet 21 роки тому
батько
коміт
506fe267ba

+ 136 - 34
direct/src/distributed/ClientRepository.py

@@ -22,8 +22,12 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
         self.setClientDatagram(1)
         self.setClientDatagram(1)
 
 
         self.recorder = base.recorder
         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.readDCFile()
         self.cache=CRCache.CRCache()
         self.cache=CRCache.CRCache()
         self.serverDelta = 0
         self.serverDelta = 0
@@ -138,52 +142,132 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
         it should be accurate plus or minus a couple of seconds.
         it should be accurate plus or minus a couple of seconds.
         """
         """
         return time.time() + self.serverDelta
         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):
     def handleGenerateWithRequired(self, di):
-        if  wantOtpServer:        
-            TheParent = di.getUint32()
-            TheZone = di.getUint32()    
+        if wantOtpServer:        
+            parentId = di.getUint32()
+            zoneId = di.getUint32()
         # Get the class Id
         # Get the class Id
-        classId = di.getUint16();
+        classId = di.getUint16()
         # Get the DO Id
         # Get the DO Id
         doId = di.getUint32()
         doId = di.getUint32()
         # Look up the dclass                       
         # Look up the dclass                       
         dclass = self.dclassesByNumber[classId]
         dclass = self.dclassesByNumber[classId]
         dclass.startGenerate()
         dclass.startGenerate()
         # Create a new distributed object, and put it in the dictionary
         # 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()
         dclass.stopGenerate()
 
 
     def handleGenerateWithRequiredOther(self, di):
     def handleGenerateWithRequiredOther(self, di):
         if wantOtpServer:        
         if wantOtpServer:        
-            TheParent = di.getUint32()
-            TheZone = di.getUint32()            
+            parentId = di.getUint32()
+            zoneId = di.getUint32()            
         # Get the class Id
         # Get the class Id
-        classId = di.getUint16();
+        classId = di.getUint16()
         # Get the DO Id
         # Get the DO Id
         doId = di.getUint32()
         doId = di.getUint32()
         # Look up the dclass
         # Look up the dclass
         dclass = self.dclassesByNumber[classId]
         dclass = self.dclassesByNumber[classId]
         dclass.startGenerate()
         dclass.startGenerate()
         # Create a new distributed object, and put it in the dictionary
         # 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()
         dclass.stopGenerate()
 
 
     def handleQuietZoneGenerateWithRequired(self, di):
     def handleQuietZoneGenerateWithRequired(self, di):
         # Special handler for quiet zone generates -- we need to filter
         # Special handler for quiet zone generates -- we need to filter
         if wantOtpServer:        
         if wantOtpServer:        
-            TheParent = di.getUint32()
-            TheZone = di.getUint32()                    
+            parentId = di.getUint32()
+            zoneId = di.getUint32()                    
         # Get the class Id
         # Get the class Id
-        classId = di.getUint16();
+        classId = di.getUint16()
         # Get the DO Id
         # Get the DO Id
         doId = di.getUint32()
         doId = di.getUint32()
         # Look up the dclass
         # Look up the dclass
@@ -196,16 +280,16 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
                 # Create a new distributed object, and put it in the dictionary
                 # Create a new distributed object, and put it in the dictionary
                 distObj = self.generateWithRequiredFields(dclass, doId, di)
                 distObj = self.generateWithRequiredFields(dclass, doId, di)
         else:
         else:
-            distObj = self.generateWithRequiredFields(dclass, doId, di)
+            distObj = self.generateWithRequiredFields(dclass, doId, di, parentId, zoneId)
         dclass.stopGenerate()
         dclass.stopGenerate()
 
 
     def handleQuietZoneGenerateWithRequiredOther(self, di):
     def handleQuietZoneGenerateWithRequiredOther(self, di):
         # Special handler for quiet zone generates -- we need to filter
         # Special handler for quiet zone generates -- we need to filter
         if wantOtpServer:        
         if wantOtpServer:        
-            TheParent = di.getUint32()
-            TheZone = di.getUint32()                    
+            parentId = di.getUint32()
+            zoneId = di.getUint32()                    
         # Get the class Id
         # Get the class Id
-        classId = di.getUint16();
+        classId = di.getUint16()
         # Get the DO Id
         # Get the DO Id
         doId = di.getUint32()
         doId = di.getUint32()
         # Look up the dclass
         # Look up the dclass
@@ -218,15 +302,18 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
                 # Create a new distributed object, and put it in the dictionary
                 # Create a new distributed object, and put it in the dictionary
                 distObj = self.generateWithRequiredOtherFields(dclass, doId, di)
                 distObj = self.generateWithRequiredOtherFields(dclass, doId, di)
         else:
         else:
-            distObj = self.generateWithRequiredOtherFields(dclass, doId, di)
+            distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId)
         dclass.stopGenerate()
         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):
         if self.doId2do.has_key(doId):
             # ...it is in our dictionary.
             # ...it is in our dictionary.
             # Just update it.
             # Just update it.
             distObj = self.doId2do[doId]
             distObj = self.doId2do[doId]
             assert(distObj.dclass == dclass)
             assert(distObj.dclass == dclass)
+            if wantOtpServer:
+                distObj.setLocation(parentId, zoneId)
             distObj.generate()
             distObj.generate()
             distObj.updateRequiredFields(dclass, di)
             distObj.updateRequiredFields(dclass, di)
             # updateRequiredFields calls announceGenerate
             # updateRequiredFields calls announceGenerate
@@ -238,6 +325,8 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
             # put it in the dictionary:
             # put it in the dictionary:
             self.doId2do[doId] = distObj
             self.doId2do[doId] = distObj
             # and update it.
             # and update it.
+            if wantOtpServer:
+                distObj.setLocation(parentId, zoneId)
             distObj.generate()
             distObj.generate()
             distObj.updateRequiredFields(dclass, di)
             distObj.updateRequiredFields(dclass, di)
             # updateRequiredFields calls announceGenerate
             # updateRequiredFields calls announceGenerate
@@ -254,6 +343,8 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
             # Put the new do in the dictionary
             # Put the new do in the dictionary
             self.doId2do[doId] = distObj
             self.doId2do[doId] = distObj
             # Update the required fields
             # Update the required fields
+            if wantOtpServer:
+                distObj.setLocation(parentId, zoneId)
             distObj.generateInit()  # Only called when constructed
             distObj.generateInit()  # Only called when constructed
             distObj.generate()
             distObj.generate()
             distObj.updateRequiredFields(dclass, di)
             distObj.updateRequiredFields(dclass, di)
@@ -279,17 +370,24 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
         # Put the new do in the dictionary
         # Put the new do in the dictionary
         self.doId2do[doId] = distObj
         self.doId2do[doId] = distObj
         # Update the required fields
         # 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.generateInit()  # Only called when constructed
         distObj.generate()
         distObj.generate()
         # updateRequiredFields calls announceGenerate
         # updateRequiredFields calls announceGenerate
         return  distObj
         return  distObj
 
 
-    def generateWithRequiredOtherFields(self, dclass, doId, di):
+    def generateWithRequiredOtherFields(self, dclass, doId, di, parentId = None, zoneId = None):
         if self.doId2do.has_key(doId):
         if self.doId2do.has_key(doId):
             # ...it is in our dictionary.
             # ...it is in our dictionary.
             # Just update it.
             # Just update it.
             distObj = self.doId2do[doId]
             distObj = self.doId2do[doId]
             assert(distObj.dclass == dclass)
             assert(distObj.dclass == dclass)
+            if wantOtpServer:
+                distObj.setLocation(parentId, zoneId)
             distObj.generate()
             distObj.generate()
             distObj.updateRequiredOtherFields(dclass, di)
             distObj.updateRequiredOtherFields(dclass, di)
             # updateRequiredOtherFields calls announceGenerate
             # updateRequiredOtherFields calls announceGenerate
@@ -301,6 +399,8 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
             # put it in the dictionary:
             # put it in the dictionary:
             self.doId2do[doId] = distObj
             self.doId2do[doId] = distObj
             # and update it.
             # and update it.
+            if wantOtpServer:
+                distObj.setLocation(parentId, zoneId)
             distObj.generate()
             distObj.generate()
             distObj.updateRequiredOtherFields(dclass, di)
             distObj.updateRequiredOtherFields(dclass, di)
             # updateRequiredOtherFields calls announceGenerate
             # updateRequiredOtherFields calls announceGenerate
@@ -317,6 +417,8 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
             # Put the new do in the dictionary
             # Put the new do in the dictionary
             self.doId2do[doId] = distObj
             self.doId2do[doId] = distObj
             # Update the required fields
             # Update the required fields
+            if wantOtpServer:
+                distObj.setLocation(parentId, zoneId)
             distObj.generateInit()  # Only called when constructed
             distObj.generateInit()  # Only called when constructed
             distObj.generate()
             distObj.generate()
             distObj.updateRequiredOtherFields(dclass, di)
             distObj.updateRequiredOtherFields(dclass, di)
@@ -467,7 +569,7 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
             self.handleGenerateWithRequiredOther(di)
             self.handleGenerateWithRequiredOther(di)
         elif wantOtpServer and msgType == CLIENT_DONE_SET_ZONE_RESP:
         elif wantOtpServer and msgType == CLIENT_DONE_SET_ZONE_RESP:
             self.handleSetZoneDone()                    
             self.handleSetZoneDone()                    
-        elif  wantOtpServer and msgType ==  CLEINT_OBJECT_LOCATION:        
+        elif  wantOtpServer and msgType == CLIENT_OBJECT_LOCATION:        
             self.handleObjectLocation(di)
             self.handleObjectLocation(di)
         else:
         else:
             currentLoginState = self.loginFSM.getCurrentState()
             currentLoginState = self.loginFSM.getCurrentState()
@@ -541,22 +643,22 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
         ## interest managment 
         ## interest managment 
         ##
         ##
         ##
         ##
-        def InterestAdd(self,  parentId, zoneId, Description):        
+        def InterestAdd(self, parentId, zoneId, Description):        
             """
             """
             Part of the new otp-server code.
             Part of the new otp-server code.
             """
             """
             self.__interest_id_assign += 1
             self.__interest_id_assign += 1
             self.__interesthash[self.__interest_id_assign] = Description
             self.__interesthash[self.__interest_id_assign] = Description
-            contextId = self.__interest_id_assign;
+            contextId = self.__interest_id_assign
             self.__sendAddInterest(contextId, parentId, zoneId)
             self.__sendAddInterest(contextId, parentId, zoneId)
             self.DumpInterests()
             self.DumpInterests()
-            return contextId;
+            return contextId
             
             
         def InterestRemove(self,  contextId):        
         def InterestRemove(self,  contextId):        
             """
             """
             Part of the new otp-server code.
             Part of the new otp-server code.
             """
             """
-            answer = 0;
+            answer = 0
             if  self.__interesthash.has_key(contextId):
             if  self.__interesthash.has_key(contextId):
                 self.__sendRemoveInterest(contextId)
                 self.__sendRemoveInterest(contextId)
                 del self.__interesthash[contextId]
                 del self.__interesthash[contextId]

+ 17 - 2
direct/src/distributed/DistributedObject.py

@@ -31,6 +31,9 @@ class DistributedObject(PandaObject):
         except:
         except:
             self.DistributedObject_initialized = 1
             self.DistributedObject_initialized = 1
             self.cr = cr
             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
             # Most DistributedObjects are simple and require no real
             # effort to load.  Some, particularly actors, may take
             # effort to load.  Some, particularly actors, may take
@@ -150,6 +153,10 @@ class DistributedObject(PandaObject):
         assert(self.notify.debug('disable(): %s' % (self.doId)))
         assert(self.notify.debug('disable(): %s' % (self.doId)))
         self.activeState = ESDisabled
         self.activeState = ESDisabled
         self.__callbacks = {}
         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):
     def isDisabled(self):
         """
         """
@@ -325,5 +332,13 @@ class DistributedObject(PandaObject):
         if self.__barrierContext != None:
         if self.__barrierContext != None:
             self.sendUpdate("setBarrierReady", [self.__barrierContext])
             self.sendUpdate("setBarrierReady", [self.__barrierContext])
             self.__barrierContext = None
             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
+

+ 4 - 5
direct/src/distributed/MsgTypes.py

@@ -72,12 +72,11 @@ CLIENT_GET_PET_DETAILS =                     81
 CLIENT_GET_PET_DETAILS_RESP =                82
 CLIENT_GET_PET_DETAILS_RESP =                82
 
 
 # (Proposed new message): CLIENT_SET_WORLD_POS =                       83
 # (Proposed new message): CLIENT_SET_WORLD_POS =                       83
-
-CLIENT_ADD_INTEREST =                        97
-CLEINT_ALTER_INTEREST =                      98
-CLEINT_REMOVE_INTEREST =                     99
 if wantOtpServer:        
 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
 # These messages are ignored when the client is headed to the quiet zone
 QUIET_ZONE_IGNORED_LIST = [
 QUIET_ZONE_IGNORED_LIST = [