ClientRepository.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. """ClientRepository module: contains the ClientRepository class"""
  2. from PandaModules import *
  3. from TaskManagerGlobal import *
  4. from MsgTypes import *
  5. from ShowBaseGlobal import *
  6. import Task
  7. import DirectNotifyGlobal
  8. import ClientDistClass
  9. import CRCache
  10. import Datagram
  11. # The repository must import all known types of Distributed Objects
  12. #import DistributedObject
  13. #import DistributedToon
  14. import DirectObject
  15. class ClientRepository(DirectObject.DirectObject):
  16. notify = DirectNotifyGlobal.directNotify.newCategory("ClientRepository")
  17. def __init__(self, dcFileName):
  18. self.number2cdc={}
  19. self.name2cdc={}
  20. self.doId2do={}
  21. self.doId2cdc={}
  22. self.parseDcFile(dcFileName)
  23. self.cache=CRCache.CRCache()
  24. return None
  25. def parseDcFile(self, dcFileName):
  26. self.dcFile = DCFile()
  27. fname = Filename(dcFileName)
  28. readResult = self.dcFile.read(fname)
  29. if not readResult:
  30. self.notify.error("Could not read dcfile: " + dcFileName)
  31. self.hashVal = self.dcFile.getHash()
  32. return self.parseDcClasses(self.dcFile)
  33. def parseDcClasses(self, dcFile):
  34. numClasses = dcFile.getNumClasses()
  35. for i in range(0, numClasses):
  36. # Create a clientDistClass from the dcClass
  37. dcClass = dcFile.getClass(i)
  38. clientDistClass = ClientDistClass.ClientDistClass(dcClass)
  39. # List the cdc in the number and name dictionaries
  40. self.number2cdc[dcClass.getNumber()]=clientDistClass
  41. self.name2cdc[dcClass.getName()]=clientDistClass
  42. return None
  43. def connect(self, serverName, serverPort):
  44. self.qcm=QueuedConnectionManager()
  45. gameServerTimeoutMs = base.config.GetInt("game-server-timeout-ms",
  46. 20000)
  47. # A big old 20 second timeout.
  48. self.tcpConn = self.qcm.openTCPClientConnection(
  49. serverName, serverPort, gameServerTimeoutMs)
  50. # Test for bad connection
  51. if self.tcpConn == None:
  52. return None
  53. else:
  54. self.qcr=QueuedConnectionReader(self.qcm, 0)
  55. self.qcr.addConnection(self.tcpConn)
  56. self.cw=ConnectionWriter(self.qcm, 0)
  57. self.startReaderPollTask()
  58. return self.tcpConn
  59. def startReaderPollTask(self):
  60. task = Task.Task(self.readerPollUntilEmpty)
  61. taskMgr.spawnTaskNamed(task, "readerPollTask")
  62. return None
  63. def readerPollUntilEmpty(self, task):
  64. while self.readerPollOnce():
  65. pass
  66. return Task.cont
  67. def readerPollOnce(self):
  68. self.ensureValidConnection()
  69. availGetVal = self.qcr.dataAvailable()
  70. if availGetVal:
  71. #print "Client: Incoming message!"
  72. datagram = NetDatagram()
  73. readRetVal = self.qcr.getData(datagram)
  74. if readRetVal:
  75. self.handleDatagram(datagram)
  76. else:
  77. ClientRepository.notify.warning("getData returned false")
  78. return availGetVal
  79. def ensureValidConnection(self):
  80. # Was the connection reset?
  81. if self.qcm.resetConnectionAvailable():
  82. resetConnectionPointer = PointerToConnection()
  83. if self.qcm.getResetConnection(resetConnectionPointer):
  84. self.fsm.request("noConnection")
  85. return None
  86. def handleDatagram(self, datagram):
  87. # This class is meant to be pure virtual, and any classes that
  88. # inherit from it need to make their own handleDatagram method
  89. pass
  90. def handleGenerateWithRequired(self, di):
  91. # Get the class Id
  92. classId = di.getArg(STUint16);
  93. # Get the DO Id
  94. doId = di.getArg(STUint32)
  95. # Look up the cdc
  96. cdc = self.number2cdc[classId]
  97. # Create a new distributed object, and put it in the dictionary
  98. distObj = self.generateWithRequiredFields(cdc, doId, di)
  99. # Call "generate" for the dist obj
  100. #distObj.generate()
  101. return None
  102. def handleGenerateWithRequiredOther(self, di):
  103. # Get the class Id
  104. classId = di.getArg(STUint16);
  105. # Get the DO Id
  106. doId = di.getArg(STUint32)
  107. # Look up the cdc
  108. cdc = self.number2cdc[classId]
  109. # Create a new distributed object, and put it in the dictionary
  110. distObj = self.generateWithRequiredOtherFields(cdc, doId, di)
  111. # Call "generate" for the distObj
  112. #distObj.generate()
  113. return None
  114. def generateWithRequiredFields(self, cdc, doId, di):
  115. # Is it in our dictionary?
  116. if self.doId2do.has_key(doId):
  117. # If so, just update it.
  118. distObj = self.doId2do[doId]
  119. distObj.generate()
  120. distObj.updateRequiredFields(cdc, di)
  121. # Is it in the cache? If so, pull it out, put it in the dictionaries,
  122. # and update it.
  123. elif self.cache.contains(doId):
  124. # If so, pull it out of the cache...
  125. distObj = self.cache.retrieve(doId)
  126. # put it in both dictionaries...
  127. self.doId2do[doId] = distObj
  128. self.doId2cdc[doId] = cdc
  129. # and update it.
  130. distObj.generate()
  131. distObj.updateRequiredFields(cdc, di)
  132. # If it is not in the dictionary or the cache, then...
  133. else:
  134. # Construct a new one
  135. distObj = cdc.constructor(self)
  136. # Assign it an Id
  137. distObj.doId = doId
  138. # Update the required fields
  139. distObj.generateInit() # Only called when constructed
  140. distObj.generate()
  141. distObj.updateRequiredFields(cdc, di)
  142. # Put the new do in both dictionaries
  143. self.doId2do[doId] = distObj
  144. self.doId2cdc[doId] = cdc
  145. return distObj
  146. def generateWithRequiredOtherFields(self, cdc, doId, di):
  147. # Is it in our dictionary?
  148. if self.doId2do.has_key(doId):
  149. # If so, just update it.
  150. distObj = self.doId2do[doId]
  151. distObj.generate()
  152. distObj.updateRequiredOtherFields(cdc, di)
  153. # Is it in the cache? If so, pull it out, put it in the dictionaries,
  154. # and update it.
  155. elif self.cache.contains(doId):
  156. # If so, pull it out of the cache...
  157. distObj = self.cache.retrieve(doId)
  158. # put it in both dictionaries...
  159. self.doId2do[doId] = distObj
  160. self.doId2cdc[doId] = cdc
  161. # and update it.
  162. distObj.generate()
  163. distObj.updateRequiredOtherFields(cdc, di)
  164. # If it is not in the dictionary or the cache, then...
  165. else:
  166. # Construct a new one
  167. distObj = cdc.constructor(self)
  168. # Assign it an Id
  169. distObj.doId = doId
  170. # Put the new do in both dictionaries
  171. self.doId2do[doId] = distObj
  172. self.doId2cdc[doId] = cdc
  173. # Update the required fields
  174. distObj.generateInit() # Only called when constructed
  175. distObj.generate()
  176. distObj.updateRequiredOtherFields(cdc, di)
  177. # Put the new do in both dictionaries
  178. # self.doId2do[doId] = distObj
  179. # self.doId2cdc[doId] = cdc
  180. return distObj
  181. def handleDisable(self, di):
  182. # Get the DO Id
  183. doId = di.getArg(STUint32)
  184. # disable it.
  185. self.disableDoId(doId)
  186. return None
  187. def disableDoId(self, doId):
  188. # Make sure the object exists
  189. if self.doId2do.has_key(doId):
  190. # Look up the object
  191. distObj = self.doId2do[doId]
  192. # remove the object from both dictionaries
  193. del(self.doId2do[doId])
  194. del(self.doId2cdc[doId])
  195. assert(len(self.doId2do) == len(self.doId2cdc))
  196. # cache the object
  197. self.cache.cache(distObj)
  198. else:
  199. ClientRepository.notify.warning("Disable failed. DistObj " +
  200. str(doId) +
  201. " is not in dictionary")
  202. return None
  203. def handleDelete(self, di):
  204. # Get the DO Id
  205. doId = di.getArg(STUint32)
  206. # If it is in the dictionaries, remove it.
  207. if self.doId2do.has_key(doId):
  208. obj = self.doId2do[doId]
  209. # Remove it from the dictionaries
  210. del(self.doId2do[doId])
  211. del(self.doId2cdc[doId])
  212. # Sanity check the dictionaries
  213. assert(len(self.doId2do) == len(self.doId2cdc))
  214. # Disable, announce, and delete the object itself...
  215. # unless delayDelete is on...
  216. obj.deleteOrDelay()
  217. # If it is in the cache, remove it.
  218. elif self.cache.contains(doId):
  219. self.cache.delete(doId)
  220. # Otherwise, ignore it
  221. else:
  222. ClientRepository.notify.warning(
  223. "Asked to delete non-existent DistObj " + str(doId))
  224. return None
  225. def handleUpdateField(self, di):
  226. # Get the DO Id
  227. doId = di.getArg(STUint32)
  228. #print("Updating " + str(doId))
  229. # Find the DO
  230. assert(self.doId2do.has_key(doId))
  231. do = self.doId2do[doId]
  232. # Find the cdc
  233. assert(self.doId2cdc.has_key(doId))
  234. cdc = self.doId2cdc[doId]
  235. # Let the cdc finish the job
  236. cdc.updateField(do, di)
  237. return None
  238. def handleUnexpectedMsgType(self, msgType, di):
  239. ClientRepository.notify.warning(
  240. "Ignoring unexpected message type: " +
  241. str(msgType) +
  242. " in state: " +
  243. self.fsm.getCurrentState().getName())
  244. return None
  245. def sendSetShardMsg(self, shardId):
  246. datagram = Datagram.Datagram()
  247. # Add message type
  248. datagram.addUint16(CLIENT_SET_SHARD)
  249. # Add shard id
  250. datagram.addUint32(shardId)
  251. # send the message
  252. self.send(datagram)
  253. return None
  254. def sendSetZoneMsg(self, zoneId):
  255. datagram = Datagram.Datagram()
  256. # Add message type
  257. datagram.addUint16(CLIENT_SET_ZONE)
  258. # Add zone id
  259. datagram.addUint16(zoneId)
  260. # send the message
  261. self.send(datagram)
  262. return None
  263. def sendUpdate(self, do, fieldName, args):
  264. # Get the DO id
  265. doId = do.doId
  266. # Get the cdc
  267. assert(self.doId2cdc.has_key(doId))
  268. cdc = self.doId2cdc[doId]
  269. # Let the cdc finish the job
  270. cdc.sendUpdate(self, do, fieldName, args)
  271. def send(self, datagram):
  272. #if self.notify.getDebug():
  273. # print "ClientRepository sending datagram:"
  274. # datagram.dumpHex(ostream)
  275. self.cw.send(datagram, self.tcpConn)
  276. return None
  277. def replaceMethod(self, oldMethod, newFunction):
  278. foundIt = 0
  279. import new
  280. # Iterate over the ClientDistClasses
  281. for cdc in self.number2cdc.values():
  282. # Iterate over the ClientDistUpdates
  283. for cdu in cdc.allCDU:
  284. method = cdu.func
  285. # See if this is a match
  286. if (method and (method.im_func == oldMethod)):
  287. # Create a new unbound method out of this new function
  288. newMethod = new.instancemethod(newFunction,
  289. method.im_self,
  290. method.im_class)
  291. # Set the new method on the cdu
  292. cdu.func = newMethod
  293. foundIt = 1
  294. return foundIt