DoCollectionManager.py 15 KB

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