2
0

DoInterestManager.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. """
  2. The DoInterestManager keeps track of which parent/zones that we currently
  3. have interest in. When you want to "look" into a zone you add an interest
  4. to that zone. When you want to get rid of, or ignore, the objects in that
  5. zone, remove interest in that zone.
  6. p.s. A great deal of this code is just code moved from ClientRepository.py.
  7. """
  8. from panda3d.core import *
  9. from panda3d.direct import *
  10. from .MsgTypes import *
  11. from direct.showbase.PythonUtil import *
  12. from direct.showbase import DirectObject
  13. from direct.showbase.MessengerGlobal import messenger
  14. from .PyDatagram import PyDatagram
  15. from direct.directnotify.DirectNotifyGlobal import directNotify
  16. import types
  17. from direct.showbase.PythonUtil import report
  18. class InterestState:
  19. StateActive = 'Active'
  20. StatePendingDel = 'PendingDel'
  21. def __init__(self, desc, state, context, event, parentId, zoneIdList,
  22. eventCounter, auto=False):
  23. self.desc = desc
  24. self.state = state
  25. self.context = context
  26. # We must be ready to keep track of multiple events. If somebody
  27. # requested an interest to be removed and we get a second request
  28. # for removal of the same interest before we get a response for the
  29. # first interest removal, we now have two parts of the codebase
  30. # waiting for a response on the removal of a single interest.
  31. self.events = []
  32. self.eventCounter = eventCounter
  33. if event:
  34. self.addEvent(event)
  35. self.parentId = parentId
  36. self.zoneIdList = zoneIdList
  37. self.auto = auto
  38. def addEvent(self, event):
  39. self.events.append(event)
  40. self.eventCounter.num += 1
  41. def getEvents(self):
  42. return list(self.events)
  43. def clearEvents(self):
  44. self.eventCounter.num -= len(self.events)
  45. assert self.eventCounter.num >= 0
  46. self.events = []
  47. def sendEvents(self):
  48. for event in self.events:
  49. messenger.send(event)
  50. self.clearEvents()
  51. def setDesc(self, desc):
  52. self.desc = desc
  53. def isPendingDelete(self):
  54. return self.state == InterestState.StatePendingDel
  55. def __repr__(self):
  56. return 'InterestState(desc=%s, state=%s, context=%s, event=%s, parentId=%s, zoneIdList=%s)' % (
  57. self.desc, self.state, self.context, self.events, self.parentId, self.zoneIdList)
  58. class InterestHandle:
  59. """This class helps to ensure that valid handles get passed in to DoInterestManager funcs"""
  60. def __init__(self, id):
  61. self._id = id
  62. def asInt(self):
  63. return self._id
  64. def __eq__(self, other):
  65. if type(self) == type(other):
  66. return self._id == other._id
  67. return self._id == other
  68. def __repr__(self):
  69. return '%s(%s)' % (self.__class__.__name__, self._id)
  70. # context value for interest changes that have no complete event
  71. NO_CONTEXT = 0
  72. class DoInterestManager(DirectObject.DirectObject):
  73. """
  74. Top level Interest Manager
  75. """
  76. notify = directNotify.newCategory("DoInterestManager")
  77. InterestDebug = ConfigVariableBool('interest-debug', False)
  78. # 'handle' is a number that represents a single interest set that the
  79. # client has requested; the interest set may be modified
  80. _HandleSerialNum = 0
  81. # high bit is reserved for server interests
  82. _HandleMask = 0x7FFF
  83. # 'context' refers to a single request to change an interest set
  84. _ContextIdSerialNum = 100
  85. _ContextIdMask = 0x3FFFFFFF # avoid making Python create a long
  86. _interests = {}
  87. if __debug__:
  88. _debug_interestHistory = []
  89. _debug_maxDescriptionLen = 40
  90. _SerialGen = SerialNumGen()
  91. _SerialNum = serialNum()
  92. def __init__(self):
  93. assert DoInterestManager.notify.debugCall()
  94. DirectObject.DirectObject.__init__(self)
  95. self._addInterestEvent = uniqueName('DoInterestManager-Add')
  96. self._removeInterestEvent = uniqueName('DoInterestManager-Remove')
  97. self._noNewInterests = False
  98. self._completeDelayedCallback = None
  99. # keep track of request contexts that have not completed
  100. self._completeEventCount = ScratchPad(num=0)
  101. self._allInterestsCompleteCallbacks = []
  102. def __verbose(self):
  103. return self.InterestDebug.getValue() or self.getVerbose()
  104. def _getAnonymousEvent(self, desc):
  105. return 'anonymous-%s-%s' % (desc, DoInterestManager._SerialGen.next())
  106. def setNoNewInterests(self, flag):
  107. self._noNewInterests = flag
  108. def noNewInterests(self):
  109. return self._noNewInterests
  110. def setAllInterestsCompleteCallback(self, callback):
  111. if ((self._completeEventCount.num == 0) and
  112. (self._completeDelayedCallback is None)):
  113. callback()
  114. else:
  115. self._allInterestsCompleteCallbacks.append(callback)
  116. def getAllInterestsCompleteEvent(self):
  117. return 'allInterestsComplete-%s' % DoInterestManager._SerialNum
  118. def resetInterestStateForConnectionLoss(self):
  119. DoInterestManager._interests.clear()
  120. self._completeEventCount = ScratchPad(num=0)
  121. if __debug__:
  122. self._addDebugInterestHistory("RESET", "", 0, 0, 0, [])
  123. def isValidInterestHandle(self, handle):
  124. # pass in a handle (or anything else) and this will return true if it is
  125. # still a valid interest handle
  126. if not isinstance(handle, InterestHandle):
  127. return False
  128. return handle.asInt() in DoInterestManager._interests
  129. def updateInterestDescription(self, handle, desc):
  130. iState = DoInterestManager._interests.get(handle.asInt())
  131. if iState:
  132. iState.setDesc(desc)
  133. def addInterest(self, parentId, zoneIdList, description, event=None):
  134. """
  135. Look into a (set of) zone(s).
  136. """
  137. assert DoInterestManager.notify.debugCall()
  138. handle = self._getNextHandle()
  139. # print 'base.cr.addInterest(',description,',',handle,'):',base.clock.getFrameCount()
  140. if self._noNewInterests:
  141. DoInterestManager.notify.warning(
  142. "addInterest: addingInterests on delete: %s" % (handle))
  143. return
  144. # make sure we've got parenting rules set in the DC
  145. if parentId not in (self.getGameDoId(),):
  146. parent = self.getDo(parentId)
  147. if not parent:
  148. DoInterestManager.notify.error(
  149. 'addInterest: attempting to add interest under unknown object %s' % parentId)
  150. else:
  151. if not parent.hasParentingRules():
  152. DoInterestManager.notify.error(
  153. 'addInterest: no setParentingRules defined in the DC for object %s (%s)'
  154. '' % (parentId, parent.__class__.__name__))
  155. if event:
  156. contextId = self._getNextContextId()
  157. else:
  158. contextId = 0
  159. # event = self._getAnonymousEvent('addInterest')
  160. DoInterestManager._interests[handle] = InterestState(
  161. description, InterestState.StateActive, contextId, event, parentId, zoneIdList, self._completeEventCount)
  162. if self.__verbose():
  163. print('CR::INTEREST.addInterest(handle=%s, parentId=%s, zoneIdList=%s, description=%s, event=%s)' % (
  164. handle, parentId, zoneIdList, description, event))
  165. self._sendAddInterest(handle, contextId, parentId, zoneIdList, description)
  166. if event:
  167. messenger.send(self._getAddInterestEvent(), [event])
  168. assert self.printInterestsIfDebug()
  169. return InterestHandle(handle)
  170. def addAutoInterest(self, parentId, zoneIdList, description):
  171. """
  172. Look into a (set of) zone(s).
  173. """
  174. assert DoInterestManager.notify.debugCall()
  175. handle = self._getNextHandle()
  176. if self._noNewInterests:
  177. DoInterestManager.notify.warning(
  178. "addInterest: addingInterests on delete: %s" % (handle))
  179. return
  180. # make sure we've got parenting rules set in the DC
  181. if parentId not in (self.getGameDoId(),):
  182. parent = self.getDo(parentId)
  183. if not parent:
  184. DoInterestManager.notify.error(
  185. 'addInterest: attempting to add interest under unknown object %s' % parentId)
  186. else:
  187. if not parent.hasParentingRules():
  188. DoInterestManager.notify.error(
  189. 'addInterest: no setParentingRules defined in the DC for object %s (%s)'
  190. '' % (parentId, parent.__class__.__name__))
  191. DoInterestManager._interests[handle] = InterestState(
  192. description, InterestState.StateActive, 0, None, parentId, zoneIdList, self._completeEventCount, True)
  193. if self.__verbose():
  194. print('CR::INTEREST.addInterest(handle=%s, parentId=%s, zoneIdList=%s, description=%s)' % (
  195. handle, parentId, zoneIdList, description))
  196. assert self.printInterestsIfDebug()
  197. return InterestHandle(handle)
  198. def removeInterest(self, handle, event = None):
  199. """
  200. Stop looking in a (set of) zone(s)
  201. """
  202. # print 'base.cr.removeInterest(',handle,'):',base.clock.getFrameCount()
  203. assert DoInterestManager.notify.debugCall()
  204. assert isinstance(handle, InterestHandle)
  205. existed = False
  206. if not event:
  207. event = self._getAnonymousEvent('removeInterest')
  208. handle = handle.asInt()
  209. if handle in DoInterestManager._interests:
  210. existed = True
  211. intState = DoInterestManager._interests[handle]
  212. if event:
  213. messenger.send(self._getRemoveInterestEvent(),
  214. [event, intState.parentId, intState.zoneIdList])
  215. if intState.isPendingDelete():
  216. self.notify.warning(
  217. 'removeInterest: interest %s already pending removal' %
  218. handle)
  219. # this interest is already pending delete, so let's just tack this
  220. # callback onto the list
  221. if event is not None:
  222. intState.addEvent(event)
  223. else:
  224. if len(intState.events) > 0:
  225. # we're not pending a removal, but we have outstanding events?
  226. # probably we are waiting for an add/alter complete.
  227. # should we send those events now?
  228. assert self.notify.warning('removeInterest: abandoning events: %s' %
  229. intState.events)
  230. intState.clearEvents()
  231. intState.state = InterestState.StatePendingDel
  232. contextId = self._getNextContextId()
  233. intState.context = contextId
  234. if event:
  235. intState.addEvent(event)
  236. self._sendRemoveInterest(handle, contextId)
  237. if not event:
  238. self._considerRemoveInterest(handle)
  239. if self.__verbose():
  240. print('CR::INTEREST.removeInterest(handle=%s, event=%s)' % (
  241. handle, event))
  242. else:
  243. DoInterestManager.notify.warning(
  244. "removeInterest: handle not found: %s" % (handle))
  245. assert self.printInterestsIfDebug()
  246. return existed
  247. def removeAutoInterest(self, handle):
  248. """
  249. Stop looking in a (set of) zone(s)
  250. """
  251. assert DoInterestManager.notify.debugCall()
  252. assert isinstance(handle, InterestHandle)
  253. existed = False
  254. handle = handle.asInt()
  255. if handle in DoInterestManager._interests:
  256. existed = True
  257. intState = DoInterestManager._interests[handle]
  258. if intState.isPendingDelete():
  259. self.notify.warning(
  260. 'removeInterest: interest %s already pending removal' %
  261. handle)
  262. # this interest is already pending delete, so let's just tack this
  263. # callback onto the list
  264. else:
  265. if len(intState.events) > 0:
  266. # we're not pending a removal, but we have outstanding events?
  267. # probably we are waiting for an add/alter complete.
  268. # should we send those events now?
  269. self.notify.warning('removeInterest: abandoning events: %s' %
  270. intState.events)
  271. intState.clearEvents()
  272. intState.state = InterestState.StatePendingDel
  273. self._considerRemoveInterest(handle)
  274. if self.__verbose():
  275. print('CR::INTEREST.removeAutoInterest(handle=%s)' % (handle))
  276. else:
  277. DoInterestManager.notify.warning(
  278. "removeInterest: handle not found: %s" % (handle))
  279. assert self.printInterestsIfDebug()
  280. return existed
  281. @report(types = ['args'], dConfigParam = 'guildmgr')
  282. def removeAIInterest(self, handle):
  283. """
  284. handle is NOT an InterestHandle. It's just a bare integer representing an
  285. AI opened interest. We're making the client close down this interest since
  286. the AI has trouble removing interests(that its opened) when the avatar goes
  287. offline. See GuildManager(UD) for how it's being used.
  288. """
  289. self._sendRemoveAIInterest(handle)
  290. def alterInterest(self, handle, parentId, zoneIdList, description=None,
  291. event=None):
  292. """
  293. Removes old interests and adds new interests.
  294. Note that when an interest is changed, only the most recent
  295. change's event will be triggered. Previous events are abandoned.
  296. If this is a problem, consider opening multiple interests.
  297. """
  298. assert DoInterestManager.notify.debugCall()
  299. assert isinstance(handle, InterestHandle)
  300. #assert not self._noNewInterests
  301. handle = handle.asInt()
  302. if self._noNewInterests:
  303. DoInterestManager.notify.warning(
  304. "alterInterest: addingInterests on delete: %s" % (handle))
  305. return
  306. exists = False
  307. if event is None:
  308. event = self._getAnonymousEvent('alterInterest')
  309. if handle in DoInterestManager._interests:
  310. if description is not None:
  311. DoInterestManager._interests[handle].desc = description
  312. else:
  313. description = DoInterestManager._interests[handle].desc
  314. # are we overriding an existing change?
  315. if DoInterestManager._interests[handle].context != NO_CONTEXT:
  316. DoInterestManager._interests[handle].clearEvents()
  317. contextId = self._getNextContextId()
  318. DoInterestManager._interests[handle].context = contextId
  319. DoInterestManager._interests[handle].parentId = parentId
  320. DoInterestManager._interests[handle].zoneIdList = zoneIdList
  321. DoInterestManager._interests[handle].addEvent(event)
  322. if self.__verbose():
  323. print('CR::INTEREST.alterInterest(handle=%s, parentId=%s, zoneIdList=%s, description=%s, event=%s)' % (
  324. handle, parentId, zoneIdList, description, event))
  325. self._sendAddInterest(handle, contextId, parentId, zoneIdList, description, action='modify')
  326. exists = True
  327. assert self.printInterestsIfDebug()
  328. else:
  329. DoInterestManager.notify.warning(
  330. "alterInterest: handle not found: %s" % (handle))
  331. return exists
  332. def openAutoInterests(self, obj):
  333. if hasattr(obj, '_autoInterestHandle'):
  334. # must be multiple inheritance
  335. self.notify.debug('openAutoInterests(%s): interests already open' % obj.__class__.__name__)
  336. return
  337. autoInterests = obj.getAutoInterests()
  338. obj._autoInterestHandle = None
  339. if len(autoInterests) == 0:
  340. return
  341. obj._autoInterestHandle = self.addAutoInterest(obj.doId, autoInterests, '%s-autoInterest' % obj.__class__.__name__)
  342. def closeAutoInterests(self, obj):
  343. if not hasattr(obj, '_autoInterestHandle'):
  344. # must be multiple inheritance
  345. self.notify.debug('closeAutoInterests(%s): interests already closed' % obj)
  346. return
  347. if obj._autoInterestHandle is not None:
  348. self.removeAutoInterest(obj._autoInterestHandle)
  349. del obj._autoInterestHandle
  350. # events for InterestWatcher
  351. def _getAddInterestEvent(self):
  352. return self._addInterestEvent
  353. def _getRemoveInterestEvent(self):
  354. return self._removeInterestEvent
  355. def _getInterestState(self, handle):
  356. return DoInterestManager._interests[handle]
  357. def _getNextHandle(self):
  358. handle = DoInterestManager._HandleSerialNum
  359. while True:
  360. handle = (handle + 1) & DoInterestManager._HandleMask
  361. # skip handles that are already in use
  362. if handle not in DoInterestManager._interests:
  363. break
  364. DoInterestManager.notify.warning(
  365. 'interest %s already in use' % handle)
  366. DoInterestManager._HandleSerialNum = handle
  367. return DoInterestManager._HandleSerialNum
  368. def _getNextContextId(self):
  369. contextId = DoInterestManager._ContextIdSerialNum
  370. while True:
  371. contextId = (contextId + 1) & DoInterestManager._ContextIdMask
  372. # skip over the 'no context' id
  373. if contextId != NO_CONTEXT:
  374. break
  375. DoInterestManager._ContextIdSerialNum = contextId
  376. return DoInterestManager._ContextIdSerialNum
  377. def _considerRemoveInterest(self, handle):
  378. """
  379. Consider whether we should cull the interest set.
  380. """
  381. assert DoInterestManager.notify.debugCall()
  382. if handle in DoInterestManager._interests:
  383. if DoInterestManager._interests[handle].isPendingDelete():
  384. # make sure there is no pending event for this interest
  385. if DoInterestManager._interests[handle].context == NO_CONTEXT:
  386. assert len(DoInterestManager._interests[handle].events) == 0
  387. del DoInterestManager._interests[handle]
  388. if __debug__:
  389. def printInterestsIfDebug(self):
  390. if DoInterestManager.notify.getDebug():
  391. self.printInterests()
  392. return 1 # for assert
  393. def _addDebugInterestHistory(self, action, description, handle,
  394. contextId, parentId, zoneIdList):
  395. if description is None:
  396. description = ''
  397. DoInterestManager._debug_interestHistory.append(
  398. (action, description, handle, contextId, parentId, zoneIdList))
  399. DoInterestManager._debug_maxDescriptionLen = max(
  400. DoInterestManager._debug_maxDescriptionLen, len(description))
  401. def printInterestHistory(self):
  402. print("***************** Interest History *************")
  403. format = '%9s %' + str(DoInterestManager._debug_maxDescriptionLen) + 's %6s %6s %9s %s'
  404. print(format % (
  405. "Action", "Description", "Handle", "Context", "ParentId",
  406. "ZoneIdList"))
  407. for i in DoInterestManager._debug_interestHistory:
  408. print(format % tuple(i))
  409. print("Note: interests with a Context of 0 do not get" \
  410. " done/finished notices.")
  411. def printInterestSets(self):
  412. print("******************* Interest Sets **************")
  413. format = '%6s %' + str(DoInterestManager._debug_maxDescriptionLen) + 's %11s %11s %8s %8s %8s'
  414. print(format % (
  415. "Handle", "Description",
  416. "ParentId", "ZoneIdList",
  417. "State", "Context",
  418. "Event"))
  419. for id, state in DoInterestManager._interests.items():
  420. if len(state.events) == 0:
  421. event = ''
  422. elif len(state.events) == 1:
  423. event = state.events[0]
  424. else:
  425. event = state.events
  426. print(format % (id, state.desc,
  427. state.parentId, state.zoneIdList,
  428. state.state, state.context,
  429. event))
  430. print("************************************************")
  431. def printInterests(self):
  432. self.printInterestHistory()
  433. self.printInterestSets()
  434. def _sendAddInterest(self, handle, contextId, parentId, zoneIdList, description,
  435. action=None):
  436. """
  437. Part of the new otp-server code.
  438. handle is a client-side created number that refers to
  439. a set of interests. The same handle number doesn't
  440. necessarily have any relationship to the same handle
  441. on another client.
  442. """
  443. assert DoInterestManager.notify.debugCall()
  444. if __debug__:
  445. if isinstance(zoneIdList, list):
  446. zoneIdList.sort()
  447. if action is None:
  448. action = 'add'
  449. self._addDebugInterestHistory(
  450. action, description, handle, contextId, parentId, zoneIdList)
  451. if parentId == 0:
  452. DoInterestManager.notify.error(
  453. 'trying to set interest to invalid parent: %s' % parentId)
  454. datagram = PyDatagram()
  455. # Add message type
  456. if isinstance(zoneIdList, list):
  457. vzl = list(zoneIdList)
  458. vzl.sort()
  459. uniqueElements(vzl)
  460. datagram.addUint16(CLIENT_ADD_INTEREST_MULTIPLE)
  461. datagram.addUint32(contextId)
  462. datagram.addUint16(handle)
  463. datagram.addUint32(parentId)
  464. datagram.addUint16(len(vzl))
  465. for zone in vzl:
  466. datagram.addUint32(zone)
  467. else:
  468. datagram.addUint16(CLIENT_ADD_INTEREST)
  469. datagram.addUint32(contextId)
  470. datagram.addUint16(handle)
  471. datagram.addUint32(parentId)
  472. datagram.addUint32(zoneIdList)
  473. self.send(datagram)
  474. def _sendRemoveInterest(self, handle, contextId):
  475. """
  476. handle is a client-side created number that refers to
  477. a set of interests. The same handle number doesn't
  478. necessarily have any relationship to the same handle
  479. on another client.
  480. """
  481. assert DoInterestManager.notify.debugCall()
  482. assert handle in DoInterestManager._interests
  483. datagram = PyDatagram()
  484. # Add message type
  485. datagram.addUint16(CLIENT_REMOVE_INTEREST)
  486. datagram.addUint32(contextId)
  487. datagram.addUint16(handle)
  488. self.send(datagram)
  489. if __debug__:
  490. state = DoInterestManager._interests[handle]
  491. self._addDebugInterestHistory(
  492. "remove", state.desc, handle, contextId,
  493. state.parentId, state.zoneIdList)
  494. def _sendRemoveAIInterest(self, handle):
  495. """
  496. handle is a bare int, NOT an InterestHandle. Use this to
  497. close an AI opened interest.
  498. """
  499. datagram = PyDatagram()
  500. # Add message type
  501. datagram.addUint16(CLIENT_REMOVE_INTEREST)
  502. datagram.addUint16((1<<15) + handle)
  503. self.send(datagram)
  504. def cleanupWaitAllInterestsComplete(self):
  505. if self._completeDelayedCallback is not None:
  506. self._completeDelayedCallback.destroy()
  507. self._completeDelayedCallback = None
  508. def queueAllInterestsCompleteEvent(self, frames=5):
  509. # wait for N frames, if no new interests, send out all-done event
  510. # calling this is OK even if there are no pending interest completes
  511. def checkMoreInterests():
  512. # if there are new interests, cancel this delayed callback, another
  513. # will automatically be scheduled when all interests complete
  514. # print 'checkMoreInterests(',self._completeEventCount.num,'):',base.clock.getFrameCount()
  515. return self._completeEventCount.num > 0
  516. def sendEvent():
  517. messenger.send(self.getAllInterestsCompleteEvent())
  518. for callback in self._allInterestsCompleteCallbacks:
  519. callback()
  520. self._allInterestsCompleteCallbacks = []
  521. self.cleanupWaitAllInterestsComplete()
  522. self._completeDelayedCallback = FrameDelayedCall(
  523. 'waitForAllInterestCompletes',
  524. callback=sendEvent,
  525. frames=frames,
  526. cancelFunc=checkMoreInterests)
  527. checkMoreInterests = None
  528. sendEvent = None
  529. def handleInterestDoneMessage(self, di):
  530. """
  531. This handles the interest done messages and may dispatch an event
  532. """
  533. assert DoInterestManager.notify.debugCall()
  534. contextId = di.getUint32()
  535. handle = di.getUint16()
  536. if self.__verbose():
  537. print('CR::INTEREST.interestDone(handle=%s)' % handle)
  538. DoInterestManager.notify.debug(
  539. "handleInterestDoneMessage--> Received handle %s, context %s" % (
  540. handle, contextId))
  541. if handle in DoInterestManager._interests:
  542. eventsToSend = []
  543. # if the context matches, send out the event
  544. if contextId == DoInterestManager._interests[handle].context:
  545. DoInterestManager._interests[handle].context = NO_CONTEXT
  546. # the event handlers may call back into the interest manager. Send out
  547. # the events after we're once again in a stable state.
  548. #DoInterestManager._interests[handle].sendEvents()
  549. eventsToSend = list(DoInterestManager._interests[handle].getEvents())
  550. DoInterestManager._interests[handle].clearEvents()
  551. else:
  552. DoInterestManager.notify.debug(
  553. "handleInterestDoneMessage--> handle: %s: Expecting context %s, got %s" % (
  554. handle, DoInterestManager._interests[handle].context, contextId))
  555. if __debug__:
  556. state = DoInterestManager._interests[handle]
  557. self._addDebugInterestHistory(
  558. "finished", state.desc, handle, contextId, state.parentId,
  559. state.zoneIdList)
  560. self._considerRemoveInterest(handle)
  561. for event in eventsToSend:
  562. messenger.send(event)
  563. else:
  564. DoInterestManager.notify.warning(
  565. "handleInterestDoneMessage: handle not found: %s" % (handle))
  566. # if there are no more outstanding interest-completes, send out global all-done event
  567. if self._completeEventCount.num == 0:
  568. self.queueAllInterestsCompleteEvent()
  569. assert self.printInterestsIfDebug()
  570. if __debug__:
  571. import unittest
  572. class AsyncTestCase(unittest.TestCase):
  573. def setCompleted(self):
  574. self._async_completed = True
  575. def isCompleted(self):
  576. return getattr(self, '_async_completed', False)
  577. class AsyncTestSuite(unittest.TestSuite):
  578. pass
  579. class AsyncTestLoader(unittest.TestLoader):
  580. suiteClass = AsyncTestSuite
  581. class AsyncTextTestRunner(unittest.TextTestRunner):
  582. def run(self, testCase):
  583. result = self._makeResult()
  584. startTime = time.time()
  585. test(result)
  586. stopTime = time.time()
  587. timeTaken = stopTime - startTime
  588. result.printErrors()
  589. self.stream.writeln(result.separator2)
  590. run = result.testsRun
  591. self.stream.writeln("Ran %d test%s in %.3fs" %
  592. (run, run != 1 and "s" or "", timeTaken))
  593. self.stream.writeln()
  594. if not result.wasSuccessful():
  595. self.stream.write("FAILED (")
  596. failed, errored = map(len, (result.failures, result.errors))
  597. if failed:
  598. self.stream.write("failures=%d" % failed)
  599. if errored:
  600. if failed:
  601. self.stream.write(", ")
  602. self.stream.write("errors=%d" % errored)
  603. self.stream.writeln(")")
  604. else:
  605. self.stream.writeln("OK")
  606. return result
  607. class TestInterestAddRemove(AsyncTestCase, DirectObject.DirectObject):
  608. def testInterestAdd(self):
  609. event = uniqueName('InterestAdd')
  610. self.acceptOnce(event, self.gotInterestAddResponse)
  611. self.handle = base.cr.addInterest(base.cr.GameGlobalsId, 100, 'TestInterest', event=event)
  612. def gotInterestAddResponse(self):
  613. event = uniqueName('InterestRemove')
  614. self.acceptOnce(event, self.gotInterestRemoveResponse)
  615. base.cr.removeInterest(self.handle, event=event)
  616. def gotInterestRemoveResponse(self):
  617. self.setCompleted()
  618. def runTests():
  619. suite = unittest.makeSuite(TestInterestAddRemove)
  620. unittest.AsyncTextTestRunner(verbosity=2).run(suite)