PageRenderTime 93ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

/mud/worlddaemon/worldservices.py

https://github.com/mixxit/solinia_depreciated
Python | 402 lines | 336 code | 49 blank | 17 comment | 30 complexity | 72c15e233296313d3601e7fd8bc77bf6 MD5 | raw file
  1. # Copyright (C) 2004-2007 Prairie Games, Inc
  2. # Please see LICENSE.TXT for details
  3. from twisted.cred.portal import Portal
  4. from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse
  5. from twisted.internet import reactor
  6. from zope.interface import implements
  7. from twisted.spread import pb
  8. from twisted.internet import reactor
  9. from twisted.cred.portal import IRealm
  10. import os,sys,imp
  11. from twisted.python import components, failure, log
  12. import math
  13. try:
  14. import win32api,win32con,win32event
  15. signal = None
  16. except ImportError:
  17. win32api = win32con = win32event = None
  18. import signal
  19. import time,traceback
  20. def main_is_frozen():
  21. return (hasattr(sys, "frozen") or # new py2exe
  22. hasattr(sys, "importers") # old py2exe
  23. or imp.is_frozen("__main__")) # tools/freeze
  24. #zone cluster services
  25. class ZoneClusterAvatar(pb.Avatar):
  26. avatars = {}
  27. clusterCount = 0
  28. numPlayers = 0
  29. #publicname (leader) -> [(pname,cname)]
  30. masterAllianceInfo = {}
  31. #publicname -> (charname,realm,zonename,transform)
  32. deathMarkerInfo = {}
  33. characterInfos = {}
  34. def __init__(self, clusternum, zonenames, worldname, publicname, password, \
  35. gmConnection=None):
  36. ZoneClusterAvatar.avatars[clusternum] = self
  37. self.clusterNum = clusternum
  38. self.zoneNames = zonenames
  39. self.zonePorts = []
  40. self.zonePasswords = []
  41. self.worldName = worldname
  42. self.publicName = publicname
  43. self.password = password
  44. self.worldPID = None
  45. self.zonePID = []
  46. self.reboot = True
  47. self.numPlayers = 0
  48. self.live = False
  49. self.worldPort = -1
  50. self.mind = None
  51. self.killed = False
  52. self.imp = None
  53. self.gmConnection = gmConnection
  54. def spawnRemoteWorldProcess(self,imp,spawnedCallback):
  55. print "spawning remote world process on imp %i"%imp.id
  56. znames="-zones=%s"%'!'.join(self.zoneNames)
  57. args= r' -cluster=%i -worldname=%s -publicname=%s -password=%s %s'%(self.clusterNum,self.worldName,self.publicName,self.password,znames)
  58. self.imp = imp
  59. self.spawnedCallback = spawnedCallback
  60. #XXX security, do not pass the login information here!
  61. self.imp.spawnWorldProcess(args)
  62. def spawnWorldProcess(self,spawnedCallback):
  63. print "spawning world process"
  64. frozen = main_is_frozen()
  65. cwd = os.getcwd()
  66. if frozen:
  67. cmd = r'..\bin\WorldServer.exe'
  68. args = ""
  69. else:
  70. cmd = sys.executable
  71. args = "%s/WorldServer.py"%cwd
  72. znames = "-zones=%s"%'!'.join(self.zoneNames)
  73. args += r' -cluster=%i -worldname=%s -publicname=%s -password=%s %s'%(self.clusterNum,self.worldName,self.publicName,self.password,znames)
  74. for arg in sys.argv:
  75. if arg.startswith("gameconfig="):
  76. args += " %s"%arg
  77. elif arg.startswith("-wx"):
  78. args += " -wx"
  79. self.spawnedCallback = spawnedCallback
  80. if sys.platform[:6] != 'darwin':
  81. s = 'start "%s" %s %s'%(os.getcwd(),cmd,args)
  82. s = os.path.normpath(s)
  83. os.system(s)
  84. else:
  85. cmd = sys.executable
  86. args = args.split(" ")
  87. args.insert(0,cmd)
  88. s = 'pythonw -c \'import os;os.spawnv(os.P_NOWAIT,"%s",[%s])\''%(cmd,','.join('"%s"'%arg for arg in args))
  89. print s
  90. os.system(s)
  91. #remote
  92. def sendCharacterInfos(self):
  93. for z in ZoneClusterAvatar.avatars.itervalues():
  94. try:
  95. z.mind.callRemote("setCharacterInfos", ZoneClusterAvatar.characterInfos)
  96. except:
  97. traceback.print_exc()
  98. def perspective_setCharacterInfo(self,publicName,cinfo):
  99. if not ZoneClusterAvatar.characterInfos.has_key(publicName) or ZoneClusterAvatar.characterInfos[publicName]!=cinfo:
  100. ZoneClusterAvatar.characterInfos[publicName]=cinfo
  101. self.sendCharacterInfos()
  102. def clearCharacterInfo(self,publicName):
  103. if not ZoneClusterAvatar.characterInfos.has_key(publicName):
  104. return
  105. del ZoneClusterAvatar.characterInfos[publicName]
  106. self.sendCharacterInfos()
  107. def perspective_resurrectionRequest(self,publicName,xpRecover,healthRecover,manaRecover,staminaRecover,tm,cname):
  108. for z in ZoneClusterAvatar.avatars.itervalues():
  109. try:
  110. z.mind.callRemote("resurrectionRequest",publicName,xpRecover,healthRecover,manaRecover,staminaRecover,tm,cname)
  111. except:
  112. traceback.print_exc()
  113. def sendDeathMarkerInfo(self):
  114. for z in ZoneClusterAvatar.avatars.itervalues():
  115. try:
  116. z.mind.callRemote("setDeathMarkerInfo", ZoneClusterAvatar.deathMarkerInfo)
  117. except:
  118. traceback.print_exc()
  119. def perspective_setDeathMarker(self,publicName,charName,realm,zoneName,pos,rot):
  120. ZoneClusterAvatar.deathMarkerInfo[publicName]=(charName,realm,zoneName,pos,rot)
  121. self.sendDeathMarkerInfo()
  122. def perspective_clearDeathMarker(self,publicName):
  123. if not ZoneClusterAvatar.deathMarkerInfo.has_key(publicName):
  124. return
  125. del ZoneClusterAvatar.deathMarkerInfo[publicName]
  126. self.sendDeathMarkerInfo()
  127. def sendAllianceInfo(self):
  128. for z in ZoneClusterAvatar.avatars.itervalues():
  129. try:
  130. z.mind.callRemote("setMasterAllianceInfo", ZoneClusterAvatar.masterAllianceInfo)
  131. except:
  132. traceback.print_exc()
  133. def clearAllianceInfo(self,publicName):
  134. changed = False
  135. remove = []
  136. for leader,a in ZoneClusterAvatar.masterAllianceInfo.iteritems():
  137. for pname,cname in a:
  138. if pname == publicName:
  139. changed = True
  140. a.remove((pname,cname))
  141. break
  142. if ZoneClusterAvatar.masterAllianceInfo.has_key(publicName):
  143. changed = True
  144. del ZoneClusterAvatar.masterAllianceInfo[publicName]
  145. if changed:
  146. self.sendAllianceInfo()
  147. def perspective_clearAllianceInfo(self,publicName):
  148. self.clearAllianceInfo(publicName)
  149. def perspective_setAllianceInfo(self,leaderName,info):
  150. members = []
  151. for pname,cname in info:
  152. members.append(pname)
  153. remove = []
  154. for leader,a in ZoneClusterAvatar.masterAllianceInfo.iteritems():
  155. for pname,cname in a[:]:
  156. if pname in members:
  157. a.remove((pname,cname))
  158. if not len(a):
  159. remove.append(leader)
  160. for r in remove:
  161. del ZoneClusterAvatar.masterAllianceInfo[r]
  162. ZoneClusterAvatar.masterAllianceInfo[leaderName]=info
  163. self.sendAllianceInfo()
  164. def perspective_joinAlliance(self,leaderName,publicName,characterName):
  165. if ZoneClusterAvatar.masterAllianceInfo.has_key(publicName):
  166. del ZoneClusterAvatar.masterAllianceInfo[publicName]
  167. a = ZoneClusterAvatar.masterAllianceInfo[leaderName]
  168. for pname,cname in a:
  169. if pname == publicName:
  170. print "Warning: %s already in %s's alliance"%(publicName,leaderName)
  171. return
  172. a.append((publicName,characterName))
  173. self.sendAllianceInfo()
  174. def perspective_setWorldPID(self,pid,worldport,genesisTime,csavatar):
  175. from charservices import CServerAvatar,CServerMind
  176. if CServerAvatar.genesisTime:
  177. if CServerAvatar.genesisTime != genesisTime:
  178. traceback.print_stack()
  179. print "AssertionError: CServerAvatar.genesisTime != genesisTime!"
  180. return
  181. else:
  182. CServerAvatar.genesisTime = genesisTime
  183. CServerAvatar.worldCSAvatars[self.clusterNum]=csavatar
  184. mind = CServerMind.worldCSMinds[self.clusterNum]=CServerMind()
  185. self.worldPort = worldport
  186. self.worldPID = pid
  187. return mind
  188. def perspective_setZonePID(self,pids,zports,zpasswords):
  189. self.zonePID = pids
  190. self.zonePorts = zports
  191. self.zonePasswords = zpasswords
  192. if not self.live:
  193. print "Zone Cluster %i is live"%self.clusterNum
  194. self.live = True
  195. ZoneClusterAvatar.clusterCount-=1
  196. self.spawnedCallback(self)
  197. self.spawnedCallback = None
  198. #if not ZoneClusterAvatar.clusterCount:
  199. # print "All clusters are live announcing to master server"
  200. # #ZoneClusterAvatar.avatars[0].mind.callRemote("announceWorldToMaster")
  201. def perspective_playerJoinedWorld(self, publicname):
  202. ZoneClusterAvatar.numPlayers += 1
  203. def perspective_playerLeftWorld(self, publicname, transfering):
  204. if not transfering:
  205. self.clearAllianceInfo(publicname)
  206. self.clearCharacterInfo(publicname)
  207. ZoneClusterAvatar.numPlayers -= 1
  208. def perspective_printException(self, exp):
  209. print exp
  210. def killWorld(self):
  211. try:
  212. d = self.mind.callRemote("shutdown")
  213. #we'll get an error in perspective->playersExported if there is nothing to export *or* if there is an error when we kill the process
  214. #so, this is just a safeguard
  215. d.addErrback(self.killWorldNow)
  216. except:
  217. self.killWorldNow(True)
  218. def perspective_playersExported(self):
  219. print "Players Exported"
  220. self.killWorldNow(True)
  221. def killProcess(self,pid):
  222. if self.imp:
  223. return
  224. print "Killing Process",pid
  225. try:
  226. if win32api:
  227. handle = win32api.OpenProcess(win32con.SYNCHRONIZE|win32con.PROCESS_TERMINATE, 0, pid)
  228. if handle:
  229. win32api.TerminateProcess(handle,-1)
  230. win32api.CloseHandle(handle)
  231. else:
  232. os.kill(pid,signal.SIGKILL)
  233. except:
  234. #oh no!
  235. traceback.print_exc()
  236. def killWorldNow(self,result):
  237. if self.killed:
  238. return
  239. self.killed = True
  240. if self.imp:
  241. self.imp.mind.callRemote("killWorldNow",self.worldPID,self.zonePID)
  242. else:
  243. print "Killing World"
  244. for p in self.zonePID:
  245. self.killProcess(p)
  246. self.killProcess(self.worldPID)
  247. del ZoneClusterAvatar.avatars[self.clusterNum]
  248. def perspective_propagateCmd(self, cmd, *args, **kwargs):
  249. # At least the command argument must be present.
  250. # Also don't propagate command if world is being killed.
  251. if self.killed or not cmd:
  252. return
  253. # If the command is to send guild or GM chat, also forward
  254. # it to eventual other worlds via GM server.
  255. try:
  256. if self.gmConnection != None and self.gmConnection.perspective:
  257. if cmd == "sendGuildMsg":
  258. self.gmConnection.perspective.callRemote("WorldDaemon", \
  259. "sendGuildMsg",*args,**kwargs)
  260. elif cmd == "receiveGMChat":
  261. self.gmConnection.perspective.callRemote("WorldDaemon", \
  262. "receiveGMChat",*args,**kwargs)
  263. except:
  264. traceback.print_exc()
  265. # Call the desired command on all zone clusters.
  266. for avatar in ZoneClusterAvatar.avatars.itervalues():
  267. # Skip ourselves.
  268. if avatar == self:
  269. continue
  270. try:
  271. avatar.mind.callRemote(cmd,*args,**kwargs)
  272. except:
  273. traceback.print_exc()
  274. class SimpleRealm:
  275. implements(IRealm)
  276. def requestAvatar(self, avatarId, mind, *interfaces):
  277. if not mind:
  278. raise BadConnectionError("no mind")
  279. ip = mind.broker.transport.getPeer()
  280. print ip,avatarId,mind
  281. #if ip.host != '127.0.0.1':
  282. # raise BadConnectionError("bad ip")
  283. if pb.IPerspective in interfaces:
  284. p = ZoneClusterAvatar.avatars[int(avatarId)]
  285. p.mind = mind
  286. return pb.IPerspective, p, lambda : None
  287. else:
  288. raise NotImplementedError("no interface")
  289. def StartServices():
  290. #fire up the World Stuff
  291. portal = Portal(SimpleRealm())
  292. checker = InMemoryUsernamePasswordDatabaseDontUse()
  293. from md5 import md5
  294. password = md5("daemon").digest()
  295. for x in range(0,100):
  296. checker.addUser(str(x), password)
  297. portal.registerChecker(checker)
  298. reactor.listenTCP(7000,pb.PBServerFactory(portal))