DoCollectionManager.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. #hack:
  2. BAD_DO_ID = BAD_ZONE_ID = 0 # 0xFFFFFFFF
  3. BAD_CHANNEL_ID = 0 # 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}
  12. # for 'owner' views of objects
  13. self.doId2ownerView = {}
  14. # Dict of {
  15. # parent DistributedObject id:
  16. # { zoneIds: [child DistributedObject ids] }}
  17. self.__doHierarchy = {}
  18. def getDo(self, doId):
  19. return self.doId2do.get(doId)
  20. def callbackWithDo(self, doId, callback):
  21. do = self.doId2do.get(doId)
  22. if do is not None:
  23. callback(do)
  24. else:
  25. relatedObjectMgr(doId, allCallback=callback)
  26. def getOwnerView(self, doId):
  27. assert self.hasOwnerView()
  28. return self.doId2ownerView.get(doId)
  29. def callbackWithOwnerView(self, doId, callback):
  30. assert self.hasOwnerView()
  31. do = self.doId2ownerView.get(doId)
  32. if do is not None:
  33. callback(do)
  34. else:
  35. pass #relatedObjectMgr(doId, allCallback=callback)
  36. def getDoTable(self, ownerView):
  37. if ownerView:
  38. assert self.hasOwnerView()
  39. return self.doId2ownerView
  40. else:
  41. return self.doId2do
  42. def doFind(self, str):
  43. """
  44. Returns list of distributed objects with matching str in value.
  45. """
  46. for value in self.doId2do.values():
  47. if `value`.find(str) >= 0:
  48. return value
  49. def doFindAll(self, str):
  50. """
  51. Returns list of distributed objects with matching str in value.
  52. """
  53. matches = []
  54. for value in self.doId2do.values():
  55. if `value`.find(str) >= 0:
  56. matches.append(value)
  57. return matches
  58. def getDoHierarchy(self):
  59. return self.__doHierarchy
  60. if __debug__:
  61. def printObjects(self):
  62. format="%10s %10s %10s %30s %20s"
  63. title=format%("parentId", "zoneId", "doId", "dclass", "name")
  64. print title
  65. print '-'*len(title)
  66. for distObj in self.doId2do.values():
  67. print format%(
  68. distObj.__dict__.get("parentId"),
  69. distObj.__dict__.get("zoneId"),
  70. distObj.__dict__.get("doId"),
  71. distObj.dclass.getName(),
  72. distObj.__dict__.get("name"))
  73. def getDoList(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. return [self.doId2do.get(i)
  86. for i in self.getDoIdList(parentId, zoneId, classType)]
  87. def getDoIdList(self, parentId, zoneId=None, classType=None):
  88. """
  89. parentId is any distributed object id.
  90. zoneId is a uint32, defaults to None (all zones). Try zone 2 if
  91. you're not sure which zone to use (0 is a bad/null zone and
  92. 1 has had reserved use in the past as a no messages zone, while
  93. 2 has traditionally been a global, uber, misc stuff zone).
  94. dclassType is a distributed class type filter, defaults
  95. to None (no filter).
  96. If dclassName is None then all objects in the zone are returned;
  97. otherwise the list is filtered to only include objects of that type.
  98. """
  99. parent=self.__doHierarchy.get(parentId)
  100. if parent is None:
  101. return []
  102. if zoneId is None:
  103. r = []
  104. for zone in parent.values():
  105. for obj in zone:
  106. r.append(obj)
  107. else:
  108. r = parent.get(zoneId, [])
  109. if classType is not None:
  110. a = []
  111. for doId in r:
  112. obj = self.getDo(doId)
  113. if isinstance(obj, classType):
  114. a.append(doId)
  115. r = a
  116. return r
  117. def getOwnerViewDoList(self, classType):
  118. assert self.hasOwnerView()
  119. l = []
  120. for obj in self.doId2ownerView.values():
  121. if isinstance(obj, classType):
  122. l.append(obj)
  123. return l
  124. def getOwnerViewDoIdList(self, classType):
  125. assert self.hasOwnerView()
  126. l = []
  127. for doId, obj in self.doId2ownerView.items():
  128. if isinstance(obj, classType):
  129. l.append(doId)
  130. return l
  131. def countObjects(self, classType):
  132. """
  133. Counts the number of objects of the given type in the
  134. repository (for testing purposes)
  135. """
  136. count = 0
  137. for dobj in self.doId2do.values():
  138. if isinstance(dobj, classType):
  139. count += 1
  140. return count
  141. def getAllOfType(self, type):
  142. # Returns a list of all DistributedObjects in the repository
  143. # of a particular type.
  144. result = []
  145. for obj in self.doId2do.values():
  146. if isinstance(obj, type):
  147. result.append(obj)
  148. return result
  149. def findAnyOfType(self, type):
  150. # Searches the repository for any object of the given type.
  151. for obj in self.doId2do.values():
  152. if isinstance(obj, type):
  153. return obj
  154. return None
  155. #----------------------------------
  156. def deleteDistributedObjects(self):
  157. # Get rid of all the distributed objects
  158. for doId in self.doId2do.keys():
  159. # Look up the object
  160. do = self.doId2do[doId]
  161. self.deleteDistObject(do)
  162. # Get rid of everything that manages distributed objects
  163. self.deleteObjects()
  164. # the zoneId2doIds table should be empty now
  165. if len(self.__doHierarchy) > 0:
  166. self.notify.warning(
  167. '__doHierarchy table not empty: %s' % self.__doHierarchy)
  168. self.__doHierarchy = {}
  169. def handleObjectLocation(self, di):
  170. # CLIENT_OBJECT_LOCATION
  171. doId = di.getUint32()
  172. parentId = di.getUint32()
  173. zoneId = di.getUint32()
  174. obj = self.doId2do.get(doId)
  175. if obj is not None:
  176. self.notify.debug(
  177. "handleObjectLocation: doId: %s parentId: %s zoneId: %s"%
  178. (doId, parentId, zoneId))
  179. # Let the object finish the job
  180. obj.setLocation(parentId, zoneId)
  181. self.storeObjectLocation(doId, parentId, zoneId)
  182. else:
  183. self.notify.warning(
  184. "handleObjectLocation: Asked to update non-existent obj: %s" % (doId))
  185. def handleSetLocation(self, di):
  186. # This was initially added because creating a distributed quest
  187. # object would cause a message like this to be generated.
  188. assert self.notify.debugStateCall(self)
  189. parentId = di.getUint32()
  190. zoneId = di.getUint32()
  191. distObj = self.doId2do.get(self.getMsgChannel())
  192. if distObj is not None:
  193. distObj.setLocation(parentId, zoneId)
  194. def storeObjectLocation(self, doId, parentId, zoneId):
  195. obj = self.doId2do.get(doId)
  196. if obj is not None:
  197. oldParentId = obj.parentId
  198. oldZoneId = obj.zoneId
  199. if oldParentId != parentId:
  200. # Remove old location
  201. self.deleteObjectLocation(doId, oldParentId, oldZoneId)
  202. if oldParentId == parentId and oldZoneId == zoneId:
  203. # object is already at that parent and zone
  204. return
  205. if (parentId is None) or (zoneId is None):
  206. # Do not store null values
  207. return
  208. # Add to new location
  209. parentZoneDict = self.__doHierarchy.setdefault(parentId, {})
  210. zoneDoSet = parentZoneDict.setdefault(zoneId, set())
  211. zoneDoSet.add(doId)
  212. # Set the new parent and zone on the object
  213. obj.parentId = parentId
  214. obj.zoneId = zoneId
  215. if 1:
  216. # Do we still need this
  217. if oldParentId != parentId:
  218. # Give the parent a chance to run code when a new child
  219. # sets location to it. For example, the parent may want to
  220. # scene graph reparent the child to some subnode it owns.
  221. parentObj = self.doId2do.get(parentId)
  222. if parentObj is not None:
  223. parentObj.handleChildArrive(obj, zoneId)
  224. def deleteObjectLocation(self, doId, parentId, zoneId):
  225. # Do not worry about null values
  226. if (parentId is None) or (zoneId is None):
  227. return
  228. if 1:
  229. # Do we still need this
  230. # notify any existing parent that we're moving away
  231. oldParentObj = self.doId2do.get(parentId)
  232. obj = self.doId2do.get(doId)
  233. if oldParentObj is not None and obj is not None:
  234. oldParentObj.handleChildLeave(obj, zoneId)
  235. parentZoneDict = self.__doHierarchy.get(parentId)
  236. if parentZoneDict is not None:
  237. zoneDoSet = parentZoneDict.get(zoneId)
  238. if zoneDoSet is not None:
  239. if doId in zoneDoSet:
  240. zoneDoSet.remove(doId)
  241. if len(zoneDoSet) == 0:
  242. del parentZoneDict[zoneId]
  243. else:
  244. self.notify.warning(
  245. "deleteObjectLocation: objId: %s not found"%(doId,))
  246. else:
  247. self.notify.warning(
  248. "deleteObjectLocation: zoneId: %s not found"%(zoneId,))
  249. else:
  250. self.notify.warning(
  251. "deleteObjectLocation: parentId: %s not found"%(parentId,))
  252. def addDOToTables(self, do, location=None, ownerView=False):
  253. assert self.notify.debugStateCall(self)
  254. #assert not hasattr(do, "isQueryAllResponse") or not do.isQueryAllResponse
  255. if not ownerView:
  256. if location is None:
  257. location = (do.parentId, do.zoneId)
  258. doTable = self.getDoTable(ownerView)
  259. # make sure the object is not already present
  260. if do.doId in doTable:
  261. if ownerView:
  262. tableName = 'doId2ownerView'
  263. else:
  264. tableName = 'doId2do'
  265. self.notify.error('doId %s already in %s [%s stomping %s]' % (
  266. do.doId, tableName, do.__class__.__name__,
  267. doTable[do.doId].__class__.__name__))
  268. doTable[do.doId]=do
  269. if not ownerView:
  270. if self.isValidLocationTuple(location):
  271. self.storeObjectLocation(do.doId, location[0], location[1])
  272. ##assert do.doId not in self.zoneId2doIds.get(location,{})
  273. ##self.zoneId2doIds.setdefault(location, {})
  274. ##self.zoneId2doIds[location][do.doId]=do
  275. def isValidLocationTuple(self, location):
  276. return (location is not None
  277. and location != (0xffffffff, 0xffffffff)
  278. and location != (0, 0))
  279. if __debug__:
  280. def isInDoTables(self, doId):
  281. assert self.notify.debugStateCall(self)
  282. inDoHierarchy = False
  283. for parentId, parentZoneDict in self.__doHierarchy.items():
  284. for zoneId, zoneDoSet in parentZoneDict.items():
  285. if doId in zoneDoSet:
  286. inDoHierarchy = True
  287. print "isInDoTables found " \
  288. "%s in parentId:%s zoneId:%s"%(
  289. doId, parentId, zoneId)
  290. return inDoHierarchy or (doId in self.doId2do)
  291. def removeDOFromTables(self, do):
  292. assert self.notify.debugStateCall(self)
  293. #assert not hasattr(do, "isQueryAllResponse") or not do.isQueryAllResponse
  294. #assert do.doId in self.doId2do
  295. self.deleteObjectLocation(do.doId, do.parentId, do.zoneId)
  296. ## location = do.getLocation()
  297. ## if location is not None:
  298. ## if location not in self.zoneId2doIds:
  299. ## self.notify.warning(
  300. ## 'dobj %s (%s) has invalid location: %s' %
  301. ## (do, do.doId, location))
  302. ## else:
  303. ## assert do.doId in self.zoneId2doIds[location]
  304. ## del self.zoneId2doIds[location][do.doId]
  305. ## if len(self.zoneId2doIds[location]) == 0:
  306. ## del self.zoneId2doIds[location]
  307. if do.doId in self.doId2do:
  308. del self.doId2do[do.doId]
  309. ## def changeDOZoneInTables(self, do, newParentId, newZoneId, oldParentId, oldZoneId):
  310. ## if 1:
  311. ## self.storeObjectLocation(do.doId, newParentId, newZoneId)
  312. ## else:
  313. ## #assert not hasattr(do, "isQueryAllResponse") or not do.isQueryAllResponse
  314. ## oldLocation = (oldParentId, oldZoneId)
  315. ## newLocation = (newParentId, newZoneId)
  316. ## # HACK: DistributedGuildMemberUD starts in -1,-1, which isnt ever put in the
  317. ## # zoneId2doIds table
  318. ## if self.isValidLocationTuple(oldLocation):
  319. ## assert self.notify.debugStateCall(self)
  320. ## assert oldLocation in self.zoneId2doIds
  321. ## assert do.doId in self.zoneId2doIds[oldLocation]
  322. ## assert do.doId not in self.zoneId2doIds.get(newLocation,{})
  323. ## # remove from old zone
  324. ## del(self.zoneId2doIds[oldLocation][do.doId])
  325. ## if len(self.zoneId2doIds[oldLocation]) == 0:
  326. ## del self.zoneId2doIds[oldLocation]
  327. ## if self.isValidLocationTuple(newLocation):
  328. ## # add to new zone
  329. ## self.zoneId2doIds.setdefault(newLocation, {})
  330. ## self.zoneId2doIds[newLocation][do.doId]=do
  331. ## def getObjectsInZone(self, parentId, zoneId):
  332. ## """ call this to get a dict of doId:distObj for a zone.
  333. ## Creates a shallow copy, so you can do whatever you want with the
  334. ## dict. """
  335. ## assert self.notify.debugStateCall(self)
  336. ## return copy.copy(self.zoneId2doIds.get((parentId, zoneId), {}))
  337. def getObjectsOfClassInZone(self, parentId, zoneId, objClass):
  338. """
  339. returns dict of doId:object for a zone, containing all objects
  340. that inherit from 'class'. returned dict is safely mutable.
  341. """
  342. assert self.notify.debugStateCall(self)
  343. doDict = {}
  344. for doId in self.getDoIdList(parentId, zoneId, objClass):
  345. doDict[doId] = self.getDo(doId)
  346. return doDict