DoCollectionManager.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. #hack:
  2. BAD_DO_ID = BAD_ZONE_ID = 0xFFFFFFFF
  3. BAD_CHANNEL_ID = 0xFFFFFFFFFFFFFFFF
  4. class DoCollectionManager:
  5. def __init__(self):
  6. # Dict of {DistributedObject ids : DistributedObjects}
  7. self.doId2do = {}
  8. # (parentId, zoneId) to dict of doId->DistributedObjectAI
  9. self.zoneId2doIds={}
  10. if self.hasOwnerView():
  11. # Dict of {DistributedObject ids : DistributedObjects} for 'owner' views of objects
  12. self.doId2ownerView = {}
  13. # Dict of {
  14. # parent DistributedObject id:
  15. # { zoneIds : [child DistributedObject ids] }}
  16. self.__doHierarchy = {}
  17. def getDo(self, doId):
  18. return self.doId2do.get(doId)
  19. def getOwnerView(self, doId):
  20. assert self.hasOwnerView()
  21. return self.doId2ownerView.get(doId)
  22. def getDoTable(self, ownerView):
  23. if ownerView:
  24. assert self.hasOwnerView()
  25. return self.doId2ownerView
  26. else:
  27. return self.doId2do
  28. def doFind(self, str):
  29. """
  30. Returns list of distributed objects with matching str in value.
  31. """
  32. for value in self.doId2do.values():
  33. if `value`.find(str) >= 0:
  34. return value
  35. def doFindAll(self, str):
  36. """
  37. Returns list of distributed objects with matching str in value.
  38. """
  39. matches = []
  40. for value in self.doId2do.values():
  41. if `value`.find(str) >= 0:
  42. matches.append(value)
  43. return matches
  44. def getDoHierarchy(self):
  45. return self.__doHierarchy
  46. if __debug__:
  47. def printObjects(self):
  48. format="%10s %10s %10s %30s %20s"
  49. title=format%("parentId", "zoneId", "doId", "dclass", "name")
  50. print title
  51. print '-'*len(title)
  52. for distObj in self.doId2do.values():
  53. print format%(
  54. distObj.__dict__.get("parentId"),
  55. distObj.__dict__.get("zoneId"),
  56. distObj.__dict__.get("doId"),
  57. distObj.dclass.getName(),
  58. distObj.__dict__.get("name"))
  59. def getDoList(self, parentId, zoneId=None, classType=None):
  60. """
  61. parentId is any distributed object id.
  62. zoneId is a uint32, defaults to None (all zones). Try zone 2 if
  63. you're not sure which zone to use (0 is a bad/null zone and
  64. 1 has had reserved use in the past as a no messages zone, while
  65. 2 has traditionally been a global, uber, misc stuff zone).
  66. dclassType is a distributed class type filter, defaults
  67. to None (no filter).
  68. If dclassName is None then all objects in the zone are returned;
  69. otherwise the list is filtered to only include objects of that type.
  70. """
  71. return [self.doId2do.get(i)
  72. for i in self.getDoIdList(parentId, zoneId, classType)]
  73. def getDoIdList(self, parentId, zoneId=None, classType=None):
  74. """
  75. parentId is any distributed object id.
  76. zoneId is a uint32, defaults to None (all zones). Try zone 2 if
  77. you're not sure which zone to use (0 is a bad/null zone and
  78. 1 has had reserved use in the past as a no messages zone, while
  79. 2 has traditionally been a global, uber, misc stuff zone).
  80. dclassType is a distributed class type filter, defaults
  81. to None (no filter).
  82. If dclassName is None then all objects in the zone are returned;
  83. otherwise the list is filtered to only include objects of that type.
  84. """
  85. parent=self.__doHierarchy.get(parentId)
  86. if parent is None:
  87. return []
  88. if zoneId is None:
  89. r = []
  90. for zone in parent.values():
  91. for obj in zone:
  92. r.append(obj)
  93. else:
  94. r = parent.get(zoneId, [])
  95. if classType is not None:
  96. a = []
  97. for obj in r:
  98. if isinstance(obj, classType):
  99. a.append(obj)
  100. r = a
  101. return r
  102. def countObjects(self, classType):
  103. """
  104. Counts the number of objects of the given type in the
  105. repository (for testing purposes)
  106. """
  107. count = 0
  108. for dobj in self.doId2do.values():
  109. if isinstance(dobj, classType):
  110. count += 1
  111. return count
  112. def getAllOfType(self, type):
  113. # Returns a list of all DistributedObjects in the repository
  114. # of a particular type.
  115. result = []
  116. for obj in self.doId2do.values():
  117. if isinstance(obj, type):
  118. result.append(obj)
  119. return result
  120. def findAnyOfType(self, type):
  121. # Searches the repository for any object of the given type.
  122. for obj in self.doId2do.values():
  123. if isinstance(obj, type):
  124. return obj
  125. return None
  126. #----------------------------------
  127. def deleteDistributedObjects(self):
  128. # Get rid of all the distributed objects
  129. for doId in self.doId2do.keys():
  130. # Look up the object
  131. do = self.doId2do[doId]
  132. self.deleteDistObject(do)
  133. # Get rid of everything that manages distributed objects
  134. self.deleteObjects()
  135. # the zoneId2doIds table should be empty now
  136. if len(self.zoneId2doIds) > 0:
  137. self.notify.warning(
  138. 'zoneId2doIds table not empty: %s' % self.zoneId2doIds)
  139. self.zoneId2doIds = {}
  140. def handleObjectLocation(self, di):
  141. # CLIENT_OBJECT_LOCATION
  142. doId = di.getUint32()
  143. parentId = di.getUint32()
  144. zoneId = di.getUint32()
  145. obj = self.doId2do.get(doId)
  146. if obj is not None:
  147. self.notify.debug(
  148. "handleObjectLocation: doId: %s parentId: %s zoneId: %s"%
  149. (doId, parentId, zoneId))
  150. # Let the object finish the job
  151. obj.setLocation(parentId, zoneId)
  152. self.storeObjectLocation(doId, parentId, zoneId)
  153. else:
  154. self.notify.warning(
  155. "handleObjectLocation: Asked to update non-existent obj: %s" % (doId))
  156. def handleSetLocation(self, di):
  157. # This was initially added because creating a distributed quest
  158. # object would cause a message like this to be generated.
  159. assert self.notify.debugStateCall(self)
  160. parentId = di.getUint32()
  161. zoneId = di.getUint32()
  162. distObj = self.doId2do.get(self.getMsgChannel())
  163. if distObj is not None:
  164. distObj.setLocation(parentId, zoneId)
  165. def storeObjectLocation(self, doId, parentId, zoneId):
  166. if (parentId is None) or (zoneId is None):
  167. # Do not store null values
  168. return
  169. # TODO: check current location
  170. obj = self.doId2do.get(doId)
  171. if obj is not None:
  172. oldParentId = obj.parentId
  173. oldZoneId = obj.zoneId
  174. if oldParentId != parentId:
  175. # Remove old location
  176. parentZoneDict = self.__doHierarchy.get(oldParentId)
  177. if parentZoneDict is not None:
  178. zoneDoSet = parentZoneDict.get(oldZoneId)
  179. if zoneDoSet is not None and doId in zoneDoSet:
  180. zoneDoSet.remove(doId)
  181. if len(zoneDoSet) == 0:
  182. del parentZoneDict[oldZoneId]
  183. # Add to new location
  184. parentZoneDict = self.__doHierarchy.setdefault(parentId, {})
  185. zoneDoSet = parentZoneDict.setdefault(zoneId, set())
  186. zoneDoSet.add(doId)
  187. def deleteObjectLocation(self, objId, parentId, zoneId):
  188. # Do not worry about null values
  189. if ((parentId is None) or (zoneId is None)):
  190. return
  191. parentZoneDict = self.__doHierarchy.get(parentId)
  192. assert(parentZoneDict is not None, "deleteObjectLocation: parentId: %s not found" % (parentId))
  193. objList = parentZoneDict.get(zoneId)
  194. assert(objList is not None, "deleteObjectLocation: zoneId: %s not found" % (zoneId))
  195. assert(objId in objList, "deleteObjectLocation: objId: %s not found" % (objId))
  196. if len(objList) == 1:
  197. # If this is the last obj in this zone, delete the entire entry
  198. del parentZoneDict[zoneId]
  199. else:
  200. # Just remove the object
  201. objList.remove(objId)
  202. def addDOToTables(self, do, location=None, ownerView=False):
  203. assert self.notify.debugStateCall(self)
  204. #assert not hasattr(do, "isQueryAllResponse") or not do.isQueryAllResponse
  205. if not ownerView:
  206. if location is None:
  207. location = (do.parentId, do.zoneId)
  208. doTable = self.getDoTable(ownerView)
  209. # make sure the object is not already present
  210. if do.doId in doTable:
  211. if ownerView:
  212. tableName = 'doId2ownerView'
  213. else:
  214. tableName = 'doId2do'
  215. self.notify.error('doId %s already in %s [%s stomping %s]' % (
  216. do.doId, tableName, do.__class__.__name__,
  217. doTable[do.doId].__class__.__name__))
  218. doTable[do.doId]=do
  219. if not ownerView:
  220. if self.isValidLocationTuple(location):
  221. assert do.doId not in self.zoneId2doIds.get(location,{})
  222. self.zoneId2doIds.setdefault(location, {})
  223. self.zoneId2doIds[location][do.doId]=do
  224. def isValidLocationTuple(self, location):
  225. return (location is not None
  226. and location != (0xffffffff, 0xffffffff)
  227. and location != (0, 0))
  228. def removeDOFromTables(self, do):
  229. assert self.notify.debugStateCall(self)
  230. #assert not hasattr(do, "isQueryAllResponse") or not do.isQueryAllResponse
  231. #assert do.doId in self.doId2do
  232. location = do.getLocation()
  233. if location is not None:
  234. if location not in self.zoneId2doIds:
  235. self.notify.warning(
  236. 'dobj %s (%s) has invalid location: %s' %
  237. (do, do.doId, location))
  238. else:
  239. assert do.doId in self.zoneId2doIds[location]
  240. del self.zoneId2doIds[location][do.doId]
  241. if len(self.zoneId2doIds[location]) == 0:
  242. del self.zoneId2doIds[location]
  243. if do.doId in self.doId2do:
  244. del self.doId2do[do.doId]
  245. def changeDOZoneInTables(self, do, newParentId, newZoneId, oldParentId, oldZoneId):
  246. #assert not hasattr(do, "isQueryAllResponse") or not do.isQueryAllResponse
  247. oldLocation = (oldParentId, oldZoneId)
  248. newLocation = (newParentId, newZoneId)
  249. # HACK: DistributedGuildMemberUD starts in -1,-1, which isnt ever put in the
  250. # zoneId2doIds table
  251. if self.isValidLocationTuple(oldLocation):
  252. assert self.notify.debugStateCall(self)
  253. assert oldLocation in self.zoneId2doIds
  254. assert do.doId in self.zoneId2doIds[oldLocation]
  255. assert do.doId not in self.zoneId2doIds.get(newLocation,{})
  256. # remove from old zone
  257. del(self.zoneId2doIds[oldLocation][do.doId])
  258. if len(self.zoneId2doIds[oldLocation]) == 0:
  259. del self.zoneId2doIds[oldLocation]
  260. if self.isValidLocationTuple(newLocation):
  261. # add to new zone
  262. self.zoneId2doIds.setdefault(newLocation, {})
  263. self.zoneId2doIds[newLocation][do.doId]=do
  264. def getObjectsInZone(self, parentId, zoneId):
  265. """ call this to get a dict of doId:distObj for a zone.
  266. Creates a shallow copy, so you can do whatever you want with the
  267. dict. """
  268. assert self.notify.debugStateCall(self)
  269. return copy.copy(self.zoneId2doIds.get((parentId, zoneId), {}))
  270. def getObjectsOfClassInZone(self, parentId, zoneId, objClass):
  271. """ returns dict of doId:object for a zone, containing all objects
  272. that inherit from 'class'. returned dict is safely mutable. """
  273. assert self.notify.debugStateCall(self)
  274. doDict = {}
  275. for doId, do in self.zoneId2doIds.get((parentId, zoneId), {}).items():
  276. if isinstance(do, objClass):
  277. doDict[doId] = do
  278. return doDict