DoCollectionManager.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  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 getOwnerViewDoList(self, classType):
  103. assert self.hasOwnerView()
  104. l = []
  105. for obj in self.doId2ownerView.values():
  106. if isinstance(obj, classType):
  107. l.append(obj)
  108. return l
  109. def getOwnerViewDoIdList(self, classType):
  110. assert self.hasOwnerView()
  111. l = []
  112. for doId, obj in self.doId2ownerView.items():
  113. if isinstance(obj, classType):
  114. l.append(doId)
  115. return l
  116. def countObjects(self, classType):
  117. """
  118. Counts the number of objects of the given type in the
  119. repository (for testing purposes)
  120. """
  121. count = 0
  122. for dobj in self.doId2do.values():
  123. if isinstance(dobj, classType):
  124. count += 1
  125. return count
  126. def getAllOfType(self, type):
  127. # Returns a list of all DistributedObjects in the repository
  128. # of a particular type.
  129. result = []
  130. for obj in self.doId2do.values():
  131. if isinstance(obj, type):
  132. result.append(obj)
  133. return result
  134. def findAnyOfType(self, type):
  135. # Searches the repository for any object of the given type.
  136. for obj in self.doId2do.values():
  137. if isinstance(obj, type):
  138. return obj
  139. return None
  140. #----------------------------------
  141. def deleteDistributedObjects(self):
  142. # Get rid of all the distributed objects
  143. for doId in self.doId2do.keys():
  144. # Look up the object
  145. do = self.doId2do[doId]
  146. self.deleteDistObject(do)
  147. # Get rid of everything that manages distributed objects
  148. self.deleteObjects()
  149. # the zoneId2doIds table should be empty now
  150. if len(self.__doHierarchy) > 0:
  151. self.notify.warning(
  152. '__doHierarchy table not empty: %s' % self.__doHierarchy)
  153. self.__doHierarchy = {}
  154. def handleObjectLocation(self, di):
  155. # CLIENT_OBJECT_LOCATION
  156. doId = di.getUint32()
  157. parentId = di.getUint32()
  158. zoneId = di.getUint32()
  159. obj = self.doId2do.get(doId)
  160. if obj is not None:
  161. self.notify.debug(
  162. "handleObjectLocation: doId: %s parentId: %s zoneId: %s"%
  163. (doId, parentId, zoneId))
  164. # Let the object finish the job
  165. obj.setLocation(parentId, zoneId)
  166. self.storeObjectLocation(doId, parentId, zoneId)
  167. else:
  168. self.notify.warning(
  169. "handleObjectLocation: Asked to update non-existent obj: %s" % (doId))
  170. def handleSetLocation(self, di):
  171. # This was initially added because creating a distributed quest
  172. # object would cause a message like this to be generated.
  173. assert self.notify.debugStateCall(self)
  174. parentId = di.getUint32()
  175. zoneId = di.getUint32()
  176. distObj = self.doId2do.get(self.getMsgChannel())
  177. if distObj is not None:
  178. distObj.setLocation(parentId, zoneId)
  179. def storeObjectLocation(self, doId, parentId, zoneId):
  180. obj = self.doId2do.get(doId)
  181. if obj is not None:
  182. oldParentId = obj.parentId
  183. oldZoneId = obj.zoneId
  184. if oldParentId != parentId:
  185. # Remove old location
  186. self.deleteObjectLocation(doId, oldParentId, oldZoneId)
  187. if oldParentId == parentId and oldZoneId == zoneId:
  188. # object is already at that parent and zone
  189. return
  190. if (parentId is None) or (zoneId is None):
  191. # Do not store null values
  192. return
  193. # Add to new location
  194. parentZoneDict = self.__doHierarchy.setdefault(parentId, {})
  195. zoneDoSet = parentZoneDict.setdefault(zoneId, set())
  196. zoneDoSet.add(doId)
  197. # Set the new parent and zone on the object
  198. obj.parentId = parentId
  199. obj.zoneId = zoneId
  200. if 1:
  201. # Do we still need this
  202. if oldParentId != parentId:
  203. # Give the parent a chance to run code when a new child
  204. # sets location to it. For example, the parent may want to
  205. # scene graph reparent the child to some subnode it owns.
  206. parentObj = self.doId2do.get(parentId)
  207. if parentObj is not None:
  208. parentObj.handleChildArrive(obj, zoneId)
  209. def deleteObjectLocation(self, doId, parentId, zoneId):
  210. # Do not worry about null values
  211. if (parentId is None) or (zoneId is None):
  212. return
  213. if 1:
  214. # Do we still need this
  215. # notify any existing parent that we're moving away
  216. oldParentObj = self.doId2do.get(parentId)
  217. obj = self.doId2do.get(doId)
  218. if oldParentObj is not None and obj is not None:
  219. oldParentObj.handleChildLeave(obj, zoneId)
  220. parentZoneDict = self.__doHierarchy.get(parentId)
  221. if parentZoneDict is not None:
  222. zoneDoSet = parentZoneDict.get(zoneId)
  223. if zoneDoSet is not None:
  224. if doId in zoneDoSet:
  225. zoneDoSet.remove(doId)
  226. if len(zoneDoSet) == 0:
  227. del parentZoneDict[zoneId]
  228. else:
  229. self.notify.warning(
  230. "deleteObjectLocation: objId: %s not found"%(doId,))
  231. else:
  232. self.notify.warning(
  233. "deleteObjectLocation: zoneId: %s not found"%(zoneId,))
  234. else:
  235. self.notify.warning(
  236. "deleteObjectLocation: parentId: %s not found"%(parentId,))
  237. def addDOToTables(self, do, location=None, ownerView=False):
  238. assert self.notify.debugStateCall(self)
  239. #assert not hasattr(do, "isQueryAllResponse") or not do.isQueryAllResponse
  240. if not ownerView:
  241. if location is None:
  242. location = (do.parentId, do.zoneId)
  243. doTable = self.getDoTable(ownerView)
  244. # make sure the object is not already present
  245. if do.doId in doTable:
  246. if ownerView:
  247. tableName = 'doId2ownerView'
  248. else:
  249. tableName = 'doId2do'
  250. self.notify.error('doId %s already in %s [%s stomping %s]' % (
  251. do.doId, tableName, do.__class__.__name__,
  252. doTable[do.doId].__class__.__name__))
  253. doTable[do.doId]=do
  254. if not ownerView:
  255. if self.isValidLocationTuple(location):
  256. self.storeObjectLocation(do.doId, location[0], location[1])
  257. ##assert do.doId not in self.zoneId2doIds.get(location,{})
  258. ##self.zoneId2doIds.setdefault(location, {})
  259. ##self.zoneId2doIds[location][do.doId]=do
  260. def isValidLocationTuple(self, location):
  261. return (location is not None
  262. and location != (0xffffffff, 0xffffffff)
  263. and location != (0, 0))
  264. def removeDOFromTables(self, do):
  265. assert self.notify.debugStateCall(self)
  266. #assert not hasattr(do, "isQueryAllResponse") or not do.isQueryAllResponse
  267. #assert do.doId in self.doId2do
  268. self.deleteObjectLocation(do.doId, do.parentId, do.zoneId)
  269. ## location = do.getLocation()
  270. ## if location is not None:
  271. ## if location not in self.zoneId2doIds:
  272. ## self.notify.warning(
  273. ## 'dobj %s (%s) has invalid location: %s' %
  274. ## (do, do.doId, location))
  275. ## else:
  276. ## assert do.doId in self.zoneId2doIds[location]
  277. ## del self.zoneId2doIds[location][do.doId]
  278. ## if len(self.zoneId2doIds[location]) == 0:
  279. ## del self.zoneId2doIds[location]
  280. if do.doId in self.doId2do:
  281. del self.doId2do[do.doId]
  282. ## def changeDOZoneInTables(self, do, newParentId, newZoneId, oldParentId, oldZoneId):
  283. ## if 1:
  284. ## self.storeObjectLocation(do.doId, newParentId, newZoneId)
  285. ## else:
  286. ## #assert not hasattr(do, "isQueryAllResponse") or not do.isQueryAllResponse
  287. ## oldLocation = (oldParentId, oldZoneId)
  288. ## newLocation = (newParentId, newZoneId)
  289. ## # HACK: DistributedGuildMemberUD starts in -1,-1, which isnt ever put in the
  290. ## # zoneId2doIds table
  291. ## if self.isValidLocationTuple(oldLocation):
  292. ## assert self.notify.debugStateCall(self)
  293. ## assert oldLocation in self.zoneId2doIds
  294. ## assert do.doId in self.zoneId2doIds[oldLocation]
  295. ## assert do.doId not in self.zoneId2doIds.get(newLocation,{})
  296. ## # remove from old zone
  297. ## del(self.zoneId2doIds[oldLocation][do.doId])
  298. ## if len(self.zoneId2doIds[oldLocation]) == 0:
  299. ## del self.zoneId2doIds[oldLocation]
  300. ## if self.isValidLocationTuple(newLocation):
  301. ## # add to new zone
  302. ## self.zoneId2doIds.setdefault(newLocation, {})
  303. ## self.zoneId2doIds[newLocation][do.doId]=do
  304. ## def getObjectsInZone(self, parentId, zoneId):
  305. ## """ call this to get a dict of doId:distObj for a zone.
  306. ## Creates a shallow copy, so you can do whatever you want with the
  307. ## dict. """
  308. ## assert self.notify.debugStateCall(self)
  309. ## return copy.copy(self.zoneId2doIds.get((parentId, zoneId), {}))
  310. def getObjectsOfClassInZone(self, parentId, zoneId, objClass):
  311. """
  312. returns dict of doId:object for a zone, containing all objects
  313. that inherit from 'class'. returned dict is safely mutable.
  314. """
  315. assert self.notify.debugStateCall(self)
  316. doDict = {}
  317. for doId in self.getDoIdList(parentId, zoneId, objClass):
  318. if isinstance(do, objClass):
  319. doDict[doId] = self.doId2do.get(do)
  320. return doDict