PageRenderTime 50ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/ladderslave.py

https://github.com/renemilk/SpringLadder
Python | 743 lines | 696 code | 26 blank | 21 comment | 161 complexity | 79288e9c9b860ed3910e1d140199e700 MD5 | raw file
Possible License(s): WTFPL
  1. # -*- coding: utf-8 -*-
  2. from customlog import *
  3. from ParseConfig import *
  4. import commands, thread, signal, os, time, subprocess, traceback, platform, sys
  5. import replay_upload
  6. from db_entities import *
  7. from ladderdb import *
  8. from match import *
  9. from ranking import GlobalRankingAlgoSelector
  10. if platform.system() == "Windows":
  11. import win32api
  12. from utilities import *
  13. bstr_nonneg = lambda n: n>0 and bstr_nonneg(n>>1).lstrip('0')+str(n&1) or '0'
  14. """
  15. * b0 = undefined (reserved for future use)
  16. * b1 = ready (0=not ready, 1=ready)
  17. * b2..b5 = team no. (from 0 to 15. b2 is LSB, b5 is MSB)
  18. * b6..b9 = ally team no. (from 0 to 15. b6 is LSB, b9 is MSB)
  19. * b10 = mode (0 = spectator, 1 = normal player)
  20. * b11..b17 = handicap (7-bit number. Must be in range 0..100). Note: Only host can change handicap values of the players in the battle (with HANDICAP command). These 7 bits are always ignored in this command. They can only be changed using HANDICAP command.
  21. * b18..b21 = reserved for future use (with pre 0.71 versions these bits were used for team color index)
  22. * b22..b23 = sync status (0 = unknown, 1 = synced, 2 = unsynced)
  23. * b24..b27 = side (e.g.: arm, core, tll, ... Side index can be between 0 and 15, inclusive)
  24. * b28..b31 = undefined (reserved for future use)
  25. """
  26. class BattleStatus:
  27. def __init__(self, status, nick ):
  28. status = int(status)
  29. self.team = getteam(status)
  30. self.ally = getally(status)
  31. self.side = getside(status)
  32. self.spec = getspec(status)
  33. self.nick = nick
  34. self.decimal = int(status)
  35. def __str__(self):
  36. return "nick: %s -- team:%d ally:%d side:%d spec:%d decimal:%d"%(self.nick,self.team,self.ally,self.side,self.spec,self.decimal)
  37. import helpstrings
  38. helpstring_user = helpstrings.helpstring_user_slave
  39. def sendstatus(self, socket ):
  40. if self.ingame:
  41. socket.send("MYSTATUS 1\n")
  42. else:
  43. socket.send("MYSTATUS 0\n")
  44. class Main:
  45. sock = 0
  46. battleowner = ""
  47. battleid = -1
  48. script = ""
  49. ingame = False
  50. gamestarted = False
  51. joinedbattle = False
  52. toshutdown = False
  53. ladderid = -1
  54. if platform.system() == "Windows":
  55. scriptbasepath = os.environ['USERPROFILE']
  56. else:
  57. scriptbasepath = os.environ['HOME']
  58. battleusers = dict()
  59. battleoptions = dict()
  60. ladderlist = dict()
  61. battle_statusmap = dict()
  62. teams = dict()
  63. allies = dict()
  64. bots = dict()
  65. disabledunits = dict()
  66. battlefounder = ""
  67. hostip = ""
  68. hostport = 0
  69. def startspring(self,socket,g):
  70. currentworkingdir = os.getcwd()
  71. try:
  72. players = []
  73. for player in self.battle_statusmap:
  74. status = self.battle_statusmap[player]
  75. if not status.spec and player != self.app.config["nick"]:
  76. players.append(player)
  77. pregame_rankinfo = self.db.GetRankAndPositionInfo( players, self.ladderid )
  78. if self.ingame == True:
  79. self.saybattle( self.socket, self.battleid, "Error: game is already running")
  80. return
  81. self.output = ""
  82. self.ingame = True
  83. doSubmit = self.ladderid != -1 and self.db.LadderExists( self.ladderid ) and self.CheckValidSetup(self.ladderid,False,0)
  84. if doSubmit:
  85. self.saybattleex(socket, self.battleid, "will submit the result to the ladder")
  86. else:
  87. self.saybattleex(socket, self.battleid, "won't submit the result to the ladder")
  88. sendstatus( self, socket )
  89. st = time.time()
  90. self.log.Info("*** Starting spring: command line \"%s %s\"" % (self.app.config["springdedclientpath"], os.path.join(self.scriptbasepath,"%f.txt" % g )) )
  91. if platform.system() == "Windows":
  92. dedpath = "\\".join(self.app.config["springdedclientpath"].replace("/","\\").split("\\")[:self.app.config["springdedclientpath"].replace("/","\\").count("\\")])
  93. if not dedpath in sys.path:
  94. sys.path.append(dedpath)
  95. if "springdatapath" in self.app.config:
  96. springdatapath = self.app.config["springdatapath"]
  97. if not springdatapath in sys.path:
  98. sys.path.append(springdatapath)
  99. os.chdir(springdatapath)
  100. else:
  101. springdatapath = None
  102. if springdatapath!= None:
  103. os.environ['SPRING_DATADIR'] = springdatapath
  104. self.pr = subprocess.Popen((self.app.config["springdedclientpath"],os.path.join(self.scriptbasepath,"%f.txt" % g )),stdout=subprocess.PIPE,stderr=subprocess.STDOUT,cwd=springdatapath)
  105. l = self.pr.stdout.readline()
  106. while len(l) > 0:
  107. self.output += l
  108. l = self.pr.stdout.readline()
  109. status = self.pr.wait()
  110. et = time.time()
  111. if status != 0:
  112. self.saybattle( self.socket,self.battleid,"Error: Spring exited with status %i" % status)
  113. self.log.Error( "Error: Spring exited with status %i" % status )
  114. self.log.Error( self.output )
  115. if doSubmit:
  116. matchid = -1
  117. try:
  118. mr = AutomaticMatchToDbWrapper( self.output, self.ladderid )
  119. matchid = self.db.ReportMatch( mr, True )
  120. postgame_rankinfo = self.db.GetRankAndPositionInfo( players, self.ladderid )
  121. news_string = '\n'.join( self.GetRankInfoDifference( pregame_rankinfo, postgame_rankinfo ) )
  122. #self.saybattle( self.socket, self.battleid, news_string )
  123. self.saybattleex(self.socket, self.battleid, "has submitted the score update to the ladder: http://ladder.springrts.com/viewmatch.py?id=%d"%matchid)
  124. except:
  125. exc = traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
  126. self.log.Error( 'EXCEPTION: BEGIN\n%s\nEXCEPTION: END\nCLIENTLOG: BEGIN\n%s\nCLIENTLOG: END'%(exc,self.output) )
  127. self.saybattleex(self.socket, self.battleid, "could not submit ladder score updates")
  128. if matchid != -1:
  129. reply = replay_upload.postReplay( os.getcwd() + "/"+ self.db.GetMatchReplay( matchid ), 'LadderBot', "Ladder: %s, Match #%d" % ( self.db.GetLadderName(self.ladderid), matchid ) )
  130. replaysiteok = reply.split()[0] == 'SUCCESS'
  131. if replaysiteok:
  132. self.saybattleex(self.socket, self.battleid, reply.split()[1] )
  133. else:
  134. self.saybattleex(self.socket, self.battleid, "error uploading replay to http://replays.adune.nl")
  135. except:
  136. exc = traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
  137. self.log.Error( 'EXCEPTION: BEGIN\n%s\nEXCEPTION: END'%exc )
  138. try:
  139. os.remove(os.path.join(self.scriptbasepath,"%f.txt" % g))
  140. except:
  141. pass
  142. os.chdir(currentworkingdir)
  143. self.ingame = False
  144. sendstatus( self, socket )
  145. if self.toshutdown:
  146. self.KillBot()
  147. def KillBot(self):
  148. if platform.system() == "Windows":
  149. handle = win32api.OpenProcess(1, 0, os.getpid())
  150. win32api.TerminateProcess(handle, 0)
  151. else:
  152. os.kill(os.getpid(),signal.SIGKILL)
  153. def CheckValidSetup( self, ladderid, echoerrors, socket ):
  154. a = self.CheckvalidPlayerSetup(ladderid,echoerrors,socket)
  155. b = self.CheckValidOptionsSetup(ladderid,echoerrors,socket)
  156. return a and b
  157. def CheckvalidPlayerSetup( self, ladderid, echoerrors, socket ):
  158. IsOk = True
  159. laddername = self.db.GetLadderName( ladderid )
  160. teamcount = len(self.teams)
  161. allycount = len(self.allies)
  162. botcount = len(self.bots)
  163. bannedplayers = ""
  164. duplicatebots = ""
  165. checkedbots = []
  166. for player in self.battle_statusmap:
  167. if not self.db.AccessCheck( ladderid, player, Roles.User ):
  168. IsOk = False
  169. bannedplayers += " " + player
  170. if player in self.bots: # it's a bot
  171. botlib = self.bots[player]
  172. if not botlib in checkedbots:
  173. checkedbots.append(botlib)
  174. else:
  175. IsOk = False
  176. duplicatebots += " " + player
  177. if not len(bannedplayers) == 0 and echoerrors:
  178. self.saybattle( socket, self.battleid, "There are banned player for " + laddername + " (" + bannedplayers + " )" )
  179. if not len(duplicatebots) == 0 and echoerrors:
  180. self.saybattle( socket, self.battleid, "There are too many bots of the same type (" + duplicatebots + " )" )
  181. minbotcount = self.db.GetLadderOption( ladderid, "min_ai_count" )
  182. maxbotcount = self.db.GetLadderOption( ladderid, "max_ai_count" )
  183. minteamcount = self.db.GetLadderOption( ladderid, "min_team_count" )
  184. maxteamcount = self.db.GetLadderOption( ladderid, "max_team_count" )
  185. minallycount = self.db.GetLadderOption( ladderid, "min_ally_count" )
  186. maxallycount = self.db.GetLadderOption( ladderid, "max_ally_count" )
  187. if botcount < minbotcount:
  188. if echoerrors:
  189. self.saybattle( socket, self.battleid, "There are too few AIs for " + laddername + " (" + str(botcount) + ")" )
  190. IsOk = False
  191. if botcount > maxbotcount:
  192. if echoerrors:
  193. self.saybattle( socket, self.battleid, "There are too many AIs for " + laddername + " (" + str(botcount) + ")" )
  194. IsOk = False
  195. if teamcount < minteamcount:
  196. if echoerrors:
  197. self.saybattle( socket, self.battleid, "There are too few control teams for " + laddername + " (" + str(teamcount) + ")" )
  198. IsOk = False
  199. if teamcount > maxteamcount:
  200. if echoerrors:
  201. self.saybattle( socket, self.battleid, "There are too many control teams for " + laddername + " (" + str(teamcount) + ")" )
  202. IsOk = False
  203. if allycount < minallycount:
  204. if echoerrors:
  205. self.saybattle( socket, self.battleid, "There are too few allies for " + laddername + " (" + str(allycount) + ")" )
  206. IsOk = False
  207. if allycount > maxallycount:
  208. if echoerrors:
  209. self.saybattle( socket, self.battleid, "There are too few allies for " + laddername + " (" + str(allycount) + ")" )
  210. IsOk = False
  211. minteamsize = self.db.GetLadderOption( ladderid, "min_team_size" )
  212. maxteamsize = self.db.GetLadderOption( ladderid, "max_team_size" )
  213. minallysize = self.db.GetLadderOption( ladderid, "min_ally_size" )
  214. maxallysize = self.db.GetLadderOption( ladderid, "max_ally_size" )
  215. teamsizesok = True
  216. errorstring = "The following control teams have too few players in them for " + laddername + ":\n"
  217. for team in self.teams:
  218. teamsize = self.teams[team]
  219. if teamsize < minteamsize:
  220. errorstring += str(team) + "=" + str(teamsize) + " "
  221. teamsizesok = False
  222. IsOk = False
  223. if not teamsizesok and echoerrors:
  224. self.saybattle( socket, self.battleid, errorstring )
  225. teamsizesok = True
  226. errorstring = "The following control teams have too many players in them for " + laddername + ":\n"
  227. for team in self.teams:
  228. if teamsize > maxteamsize:
  229. IsOk = False
  230. errorstring += str(team) + "=" + str(teamsize) + " "
  231. teamsizesok = False
  232. if not teamsizesok and echoerrors:
  233. self.saybattle( socket, self.battleid, errorstring )
  234. allysizesok = True
  235. errorstring = "The following ally have too few players in them for " + laddername + ":\n"
  236. for ally in self.allies:
  237. allysize = self.allies[ally]
  238. if allysize < minallysize:
  239. IsOk = False
  240. allysizesok = False
  241. errorstring += str(team) + "=" + str(teamsize) + " "
  242. if not allysizesok and echoerrors:
  243. self.saybattle( socket, self.battleid, errorstring )
  244. allysizesok = True
  245. errorstring = "The following ally have too many players in them for " + laddername + ":\n"
  246. for ally in self.allies:
  247. allysize = self.allies[ally]
  248. if allysize > maxallysize:
  249. IsOk = False
  250. allysizesok = False
  251. errorstring += str(team) + "=" + str(teamsize) + " "
  252. if not allysizesok and echoerrors:
  253. self.saybattle( socket, self.battleid, errorstring )
  254. return IsOk
  255. def CheckValidOptionsSetup( self, ladderid, echoerrors, socket ):
  256. IsOk = True
  257. laddername = self.db.GetLadderName( ladderid )
  258. for key in self.battleoptions:
  259. value = self.battleoptions[key]
  260. OptionOk = self.CheckOptionOk( ladderid, key, value )
  261. if not OptionOk:
  262. if IsOk and echoerrors:
  263. self.saybattle( socket, self.battleid, "The following settings are not compatible with " + laddername + ":" )
  264. IsOk = False
  265. if echoerrors:
  266. self.saybattle( socket, self.battleid, key + "=" + value )
  267. return IsOk
  268. def CheckOptionOk( self, ladderid, keyname, value ):
  269. if self.db.GetOptionKeyValueExists( ladderid, False, keyname, value ): # option in the blacklist
  270. return False
  271. if self.db.GetOptionKeyExists( ladderid, True, keyname ): # whitelist not empty
  272. return self.db.GetOptionKeyValueExists( ladderid, True, keyname, value )
  273. else:
  274. return True
  275. def JoinGame(self,s):
  276. if self.joinedbattle:
  277. sendstatus( self, self.socket )
  278. if not self.gamestarted:
  279. return
  280. if self.ingame:
  281. return
  282. #start spring
  283. g = time.time()
  284. if platform.system() == "Linux":
  285. f = open(os.path.join(os.environ['HOME'],"%f.txt" % g),"a")
  286. else:
  287. f = open(os.path.join(os.environ['USERPROFILE'],"%f.txt" % g),"a")
  288. self.script = "[GAME]\n{"
  289. self.script += "\n\tHostIP=" + self.hostip + ";"
  290. self.script += "\n\tHostPort=" + self.hostport + ";"
  291. self.script += "\n\tIsHost=0;"
  292. self.script += "\n\tMyPlayerName=" + self.app.config["nick"] + ";"
  293. self.script += "\n}"
  294. f.write(self.script)
  295. f.close()
  296. thread.start_new_thread(self.startspring,(s,g))
  297. def onload(self,tasc):
  298. self.app = tasc.main
  299. self.tsc = tasc
  300. self.hosttime = time.time()
  301. self.battleid = int(self.app.config["battleid"])
  302. self.ladderid = int(self.app.config["ladderid"])
  303. self.battlepassword = self.app.config["battlepassword"]
  304. self.log = CLog()
  305. self.log.Init( self.app.config['nick']+'.log', self.app.config['nick']+'.err' )
  306. self.db = LadderDB( parselist(self.app.config["alchemy-uri"],",")[0], [], parselist(self.app.config["alchemy-verbose"],",")[0] )
  307. def oncommandfromserver(self,command,args,s):
  308. #print "From server: %s | Args : %s" % (command,str(args))
  309. self.socket = s
  310. if command == "JOINBATTLE":
  311. self.joinedbattle = True
  312. good( "Joined battle: " + str(self.battleid) )
  313. if command == "JOINBATTLEFAILED":
  314. self.joinedbattle = False
  315. bad( "Join battle failed, ID: " + str(self.battleid) + " reason: " + " ".join(args[0:] ) )
  316. self.KillBot()
  317. if command == "FORCEQUITBATTLE":
  318. self.joinedbattle = False
  319. bad( "Kicked from battle: " + str(self.battleid) )
  320. self.toshutdown = True
  321. if not self.ingame:
  322. self.KillBot()
  323. if command == "BATTLECLOSED" and len(args) == 1 and int(args[0]) == self.battleid:
  324. self.joinedbattle = False
  325. notice( "Battle closed: " + str(self.battleid) )
  326. self.toshutdown = True
  327. if not self.ingame:
  328. self.KillBot()
  329. if command == "ENABLEALLUNITS":
  330. self.disabledunits = dict()
  331. if command == "ENABLEUNITS" and len(args) > 1:
  332. for unit in args[1:]:
  333. del self.disabledunits[unit]
  334. if command == "DISABLEUNITS":
  335. for unit in args[1:]:
  336. self.disabledunits[unit] = 0
  337. if command == "SETSCRIPTTAGS":
  338. for option in args[0].split():
  339. pieces = parselist( option, "=" )
  340. if len(pieces) != 2:
  341. error( "parsing error of option string: " + option )
  342. key = pieces[0]
  343. if key.startswith("/game/"): # strip prefix
  344. key = key[6:]
  345. elif key.startswith("game/"):# strip prefix
  346. key = key[5:]
  347. if key.startswith("restrict/"):
  348. unitname = key[9:]
  349. self.disabledunits[unitname] = int(value)
  350. value = pieces[1]
  351. self.battleoptions[key] = value
  352. if command == "REQUESTBATTLESTATUS":
  353. self.socket.send( "MYBATTLESTATUS 4194816 255\n" )#spectator+synced/white
  354. if command == "SAIDBATTLE" and len(args) > 1 and args[1].startswith("!"):
  355. who = args[0]
  356. command = args[1]
  357. args = args[2:]
  358. if len(command) > 0 and command[0] == "!":
  359. if not self.db.AccessCheck( -1, who, Roles.User ):
  360. self.sayPermissionDenied( self.socket, who, command )
  361. #log
  362. return
  363. else:
  364. return
  365. try:
  366. if self.battle_statusmap[who].spec and who != self.battlefounder and not self.db.AccessCheck( -1, who, Roles.LadderAdmin ):
  367. return
  368. except:
  369. pass
  370. if command == "!ladderchecksetup":
  371. ladderid = self.ladderid
  372. if len(args) == 1 and args[0].isdigit():
  373. ladderid = int(args[0])
  374. if ladderid == -1:
  375. self.saybattle( self.socket, self.battleid,"No ladder has been enabled.")
  376. elif self.db.LadderExists( ladderid ):
  377. laddername = self.db.GetLadderName( ladderid )
  378. if self.CheckValidSetup( ladderid, True, self.socket ):
  379. self.saybattle( self.socket, self.battleid, "All settings are compatible with the ladder " + laddername )
  380. else:
  381. self.saybattle( self.socket, self.battleid,"Invalid ladder ID.")
  382. if command == "!ladderlist":
  383. self.saybattle( self.socket, self.battleid, "Available ladders, format name: ID:" )
  384. for l in self.db.GetLadderList(Ladder.name):
  385. self.saybattle( self.socket, self.battleid, "%s: %d" %(l.name, l.id ) )
  386. if command == "!ladder":
  387. if len(args) == 1 and args[0].isdigit():
  388. ladderid = int(args[0])
  389. if ladderid != -1:
  390. if self.db.LadderExists( ladderid ):
  391. laddername = self.db.GetLadderName( ladderid )
  392. self.saybattle( self.socket, self.battleid,"Enabled ladder reporting for ladder: " + laddername )
  393. self.ladderid = ladderid
  394. if self.CheckValidSetup( ladderid, True, self.socket ):
  395. self.saybattle( self.socket, self.battleid, "All settings are compatible with the ladder " + laddername )
  396. else:
  397. self.saybattle( self.socket, self.battleid,"Invalid ladder ID.")
  398. else:
  399. self.ladderid = ladderid
  400. self.saybattle( self.socket, self.battleid,"Ladder reporting disabled.")
  401. else:
  402. self.saybattle( self.socket, self.battleid,"Invalid command syntax, check !ladderhelp for usage.")
  403. if command == "!ladderleave":
  404. self.joinedbattle = False
  405. good( "Leaving battle: " + str(self.battleid) )
  406. self.socket.send("LEAVEBATTLE\n")
  407. self.toshutdown = True
  408. if not self.ingame:
  409. self.KillBot()
  410. if command == "!ladderhelp":
  411. self.saybattle( self.socket, self.battleid, "Hello, I am a bot to manage and keep stats of ladder games.\nYou can use the following commands:")
  412. self.saybattle( self.socket, self.battleid, helpstring_user )
  413. if command == '!ladderdebug':
  414. if not self.db.AccessCheck( self.ladderid, who, Roles.Owner ):
  415. self.sayPermissionDenied( self.socket, who, command )
  416. #log
  417. return
  418. import fakeoutput
  419. if len(args) > 0 and args[0].isdigit():
  420. idx = max( int(args[0]), len(fakeoutput.fakeoutput) -1 )
  421. output = fakeoutput.fakeoutput[idx]
  422. else:
  423. output = fakeoutput.fakeoutput[-1]
  424. upd = GlobalRankingAlgoSelector.GetPrintableRepresentation( self.db.GetRanks( self.ladderid ), self.db )
  425. players = ['doofus', 'idiot']
  426. pregame_rankinfo = self.db.GetRankAndPositionInfo( players, self.ladderid )
  427. self.saybattle( self.socket, self.battleid, 'before:\n' + upd )
  428. try:
  429. mr = AutomaticMatchToDbWrapper( output, self.ladderid )
  430. repeats = int(args[1]) if len(args) > 1 else 1
  431. for i in range(repeats):
  432. self.db.ReportMatch( mr, False )#false skips validation check of output against ladder rules
  433. upd = GlobalRankingAlgoSelector.GetPrintableRepresentation( self.db.GetRanks( self.ladderid ), self.db )
  434. self.saybattle( self.socket, self.battleid, 'pre-recalc:\n' +upd )
  435. self.db.RecalcRankings(self.ladderid)
  436. except InvalidOptionSetup, e:
  437. self.saybattle( self.socket, self.battleid, str(e) )
  438. return
  439. upd = GlobalRankingAlgoSelector.GetPrintableRepresentation( self.db.GetRanks( self.ladderid ), self.db )
  440. self.saybattle( self.socket, self.battleid, 'after:\n' +upd )
  441. postgame_rankinfo = self.db.GetRankAndPositionInfo( players, self.ladderid )
  442. self.saybattle( self.socket, self.battleid, '\n'.join( self.GetRankInfoDifference( pregame_rankinfo, postgame_rankinfo ) ) )
  443. if command == "!ladderforcestart":
  444. if not self.db.AccessCheck( self.ladderid, who, Roles.User ):
  445. self.sayPermissionDenied( self.socket, who, command )
  446. #log
  447. return
  448. self.JoinGame(s)
  449. if command == '!ladderstress':
  450. if not self.db.AccessCheck( self.ladderid, who, Roles.Owner ):
  451. self.sayPermissionDenied( self.socket, who, command )
  452. #log
  453. return
  454. import fakeoutput
  455. if len(args) > 0 and args[0].isdigit():
  456. idx = max( int(args[0]), len(fakeoutput.fakeoutput) -1 )
  457. output = fakeoutput.fakeoutput[idx]
  458. else:
  459. output = fakeoutput.fakeoutput[-1]
  460. if len(args) > 1 and args[1].isdigit():
  461. times = int(args[1])
  462. else:
  463. times = 1
  464. now = datetime.datetime.now()
  465. upd = GlobalRankingAlgoSelector.GetPrintableRepresentation( self.db.GetRanks( self.ladderid ), self.db )
  466. for i in range ( times ):
  467. try:
  468. mr = AutomaticMatchToDbWrapper( output, self.ladderid )
  469. repeats = int(args[1]) if len(args) > 1 else 1
  470. for i in range(repeats):
  471. self.db.ReportMatch( mr, False )#false skips validation check of output against ladder rules
  472. upd = GlobalRankingAlgoSelector.GetPrintableRepresentation( self.db.GetRanks( self.ladderid ), self.db )
  473. self.db.RecalcRankings(self.ladderid)
  474. except InvalidOptionSetup, e:
  475. self.saybattle( self.socket, self.battleid, str(e) )
  476. return
  477. upd = GlobalRankingAlgoSelector.GetPrintableRepresentation( self.db.GetRanks( self.ladderid ), self.db )
  478. self.saybattle( self.socket, self.battleid, '%i recalcs took %s:\n'%(times, str(datetime.datetime.now() - now) ))
  479. if command == "!ladderreportgame":
  480. if len(args) < 2:
  481. self.saybattle( self.socket, self.battleid, "Invalid command syntax (too few args), check !ladderhelp for usage." )
  482. else:
  483. ladderid = self.ladderid
  484. try:
  485. if not self.db.AccessCheck( ladderid, who, Roles.LadderAdmin ):
  486. self.sayPermissionDenied( self.socket, who, command )
  487. #log
  488. return
  489. ladder = self.db.GetLadder( ladderid )
  490. usercounter = 0
  491. userresults = dict()
  492. while ( usercounter != len(args) ):
  493. username, equal, result = args[usercounter].partition("=")
  494. if ( len(result) == 0 ):
  495. self.saybattle( self.socket, self.battleid, "Invalid command syntax, check !ladderhelp for usage." )
  496. return
  497. userresults[username] = int(result)
  498. usercounter = usercounter +1
  499. if not self.CheckvalidPlayerSetup( ladderid, True , self.socket ):
  500. self.saybattle( self.socket, self.battleid, "Invalid setup" )
  501. players = []
  502. teams_map = dict()
  503. allies_map = dict()
  504. for player in self.battle_statusmap:
  505. status = self.battle_statusmap[player]
  506. if not status.spec and player != self.app.config["nick"]:
  507. players.append(player)
  508. teams_map[player] = status.team
  509. allies_map[player] = status.ally
  510. mr = ManualMatchToDbWrapper( players, userresults, self.teams, ladderid, self.battleoptions, self.disabledunits, self.bots, self.allies, teams_map, allies_map )
  511. try:
  512. self.db.ReportMatch( mr )
  513. self.saybattleex(self.socket, self.battleid, "has submitted ladder score updates")
  514. except BannedPlayersDetectedException, b:
  515. self.saybattle( self.socket,self.battleid,str(b) )
  516. self.log.Error( b, 'BannedPlayersDetectedException' )
  517. except Exception, e:
  518. self.saybattle( self.socket,self.battleid,"There was an error reporting the battle outcome: %s"%str(e) )
  519. self.log.Error( e, 'Exception' )
  520. except ElementNotFoundException, e:
  521. self.saybattle( self.socket,self.battleid, "Invalid ladder ID." )
  522. self.log.Error( e, 'ElementNotFoundException' )
  523. if command == "!ladderlistoptions":
  524. if len(args) != 1 or not args[0].isdigit():
  525. ladderid = self.ladderid
  526. else:
  527. ladderid = int(args[0])
  528. if self.db.LadderExists( ladderid ):
  529. self.saybattle( self.socket,self.battleid, "Ladder: " + self.db.GetLadderName(ladderid) )
  530. self.saybattle( self.socket,self.battleid, "Min AIs in a Match ( how many AIs ): " + str(self.db.GetLadderOption( ladderid, "min_ai_count" )) )
  531. self.saybattle( self.socket,self.battleid, "Max Ais in a Match ( how many AIs ): " + str(self.db.GetLadderOption( ladderid, "max_ai_count" )) )
  532. self.saybattle( self.socket,self.battleid, "Min Players in a Team ( sharing control ): " + str(self.db.GetLadderOption( ladderid, "min_team_size" )) )
  533. self.saybattle( self.socket,self.battleid, "Max Players in a Team ( sharing control ): " + str(self.db.GetLadderOption( ladderid, "max_team_size" )) )
  534. self.saybattle( self.socket,self.battleid, "Min Teams in an Ally ( being allied ): " + str(self.db.GetLadderOption( ladderid, "min_ally_size" )) )
  535. self.saybattle( self.socket,self.battleid, "Max Teams in an Ally ( being allied ): " + str(self.db.GetLadderOption( ladderid, "max_ally_size" )) )
  536. self.saybattle( self.socket,self.battleid, "Min Teams in a Match ( how many Teams ): " + str(self.db.GetLadderOption( ladderid, "min_team_count" )) )
  537. self.saybattle( self.socket,self.battleid, "Max Teams in a Match ( how many Teams ): " + str(self.db.GetLadderOption( ladderid, "max_team_count" )) )
  538. self.saybattle( self.socket,self.battleid, "Min Alliances in a Match ( how many Allys ): " + str(self.db.GetLadderOption( ladderid, "min_ally_count" )) )
  539. self.saybattle( self.socket,self.battleid, "Max Alliances in a Match ( how many Allys ): " + str(self.db.GetLadderOption( ladderid, "max_ally_count" )) )
  540. self.saybattle( self.socket,self.battleid, "Whitelisted options ( if a key is present, no other value except for those listed will be allowed for such key ):" )
  541. for opt in self.db.GetFilteredOptions( ladderid, True ):
  542. self.saybattle( self.socket,self.battleid, opt.key + ": " + opt.value )
  543. self.saybattle( self.socket,self.battleid, "Blacklisted options ( if a value is present for a key, such value won't be allowed ):" )
  544. for opt in self.db.GetFilteredOptions( ladderid, False ):
  545. self.saybattle( self.socket,self.battleid, opt.key + ": " + opt.value )
  546. else:
  547. self.saybattle( self.socket,self.battleid, "Invalid ladder ID." )
  548. if command == "!score":
  549. if not self.db.AccessCheck( -1, who, Roles.User ):
  550. self.sayPermissionDenied( self.socket, who, command )
  551. #log
  552. return
  553. if len(args) > 2:
  554. self.saybattle( self.socket,self.battleid, "Invalid command syntax, check !ladderhelp for usage." )
  555. else:
  556. ladderid = self.ladderid
  557. playername = ""
  558. rep = ''
  559. if len(args) > 0:
  560. if args[0].isdigit():
  561. ladderid = int(args[0])
  562. if len(args) > 1:
  563. playername = args[1]
  564. else:
  565. playername = args[0]
  566. if ladderid != -1 and len(playername) == 0:
  567. rep = GlobalRankingAlgoSelector.GetPrintableRepresentation( self.db.GetRanks( ladderid ), self.db )
  568. elif ladderid != -1 and len(playername) != 0:
  569. rep = GlobalRankingAlgoSelector.GetPrintableRepresentation( self.db.GetRanks( ladderid, playername ), self.db )
  570. elif ladderid == -1 and len(playername) != 0:
  571. rep = GlobalRankingAlgoSelector.GetPrintableRepresentationPlayer( self.db.GetPlayerRanks( playername ), self.db )
  572. self.saybattle( self.socket,self.battleid, rep )
  573. if command == "!ladderopponent":
  574. if len(args) > 1:
  575. self.saybattle( self.socket,self.battleid, "Invalid command syntax, check !ladderhelp for usage." )
  576. return
  577. if len(args) == 1:
  578. ladderid = int(args[0])
  579. else:
  580. ladderid = self.ladderid
  581. if not self.db.AccessCheck( ladderid, who, Roles.User ):
  582. self.sayPermissionDenied( self.socket, who, command )
  583. #log
  584. return
  585. if not self.db.LadderExists( ladderid ):
  586. self.saybattle( self.socket,self.battleid, "Invalid ladderID." )
  587. return
  588. userlist, ranks = GlobalRankingAlgoSelector.GetCandidateOpponents( who, ladderid, self.db )
  589. opponent_found = False
  590. for user in userlist:
  591. try:
  592. userstatus = self.tsc.users[user]
  593. except: # skip offline
  594. continue
  595. if userstatus.ingame:
  596. continue
  597. if userstatus.afk:
  598. continue
  599. opponent_found = True
  600. self.saybattle( self.socket,self.battleid, ranks[user] )
  601. if not opponent_found:
  602. self.saybattle( self.socket,self.battleid, "No suitable candidates as opponent are available currently, try again later." )
  603. if command == "BATTLEOPENED" and len(args) > 12 and int(args[0]) == self.battleid:
  604. self.battlefounder = args[3]
  605. self.battleoptions["battletype"] = args[1]
  606. self.hostip = args[4]
  607. self.hostport = args[5]
  608. tabbedstring = " ".join(args[10:])
  609. tabsplit = parselist(tabbedstring,"\t")
  610. self.battleoptions["mapname"] = tabsplit[0]
  611. self.battleoptions["modname"] = tabsplit[2]
  612. if command == "UPDATEBATTLEINFO" and len(args) > 4 and int(args[0]) == self.battleid:
  613. tabbedstring = " ".join(args[4:])
  614. tabsplit = parselist(tabbedstring,"\t")
  615. self.battleoptions["mapname"] = tabsplit[0]
  616. if command == "CLIENTSTATUS" and len(args) > 1 and len(self.battlefounder) != 0 and args[0] == self.battlefounder:
  617. self.gamestarted = getingame(int(args[1]))
  618. self.JoinGame(s)
  619. if command == "CLIENTBATTLESTATUS":
  620. if len(args) != 3:
  621. error( "invalid CLIENTBATTLESTATUS:%s"%(args) )
  622. bs = BattleStatus( args[1], args[0] )
  623. self.battle_statusmap[ args[0] ] = bs
  624. self.FillTeamAndAllies()
  625. if command == "LEFTBATTLE":
  626. if len(args) != 2:
  627. error( "invalid LEFTBATTLE:%s"%(args) )
  628. if int(args[0]) == self.battleid:
  629. player = args[1]
  630. if player in self.battle_statusmap:
  631. del self.battle_statusmap[player]
  632. self.FillTeamAndAllies()
  633. if command == "ADDBOT":
  634. if len(args) != 6:
  635. error( "invalid ADDBOT:%s"%(args) )
  636. if int(args[0]) == self.battleid:
  637. botlib = args[5] # we'll use the bot's lib name intead of player name for ladder pourposes
  638. name = args[1]
  639. botlib = botlib.replace("|"," ")
  640. bs = BattleStatus( args[3], name )
  641. self.battle_statusmap[ name ] = bs
  642. self.FillTeamAndAllies()
  643. self.bots[name] = botlib
  644. if command == "UPDATEBOT":
  645. if len(args) < 2:
  646. error( "invalid UPDATEBOT:%s"%(args) )
  647. name = args[0]
  648. bs = BattleStatus( args[1], name )
  649. self.battle_statusmap[ botlib ] = bs
  650. self.FillTeamAndAllies()
  651. if command == "REMOVEBOT":
  652. if len(args) != 2:
  653. error( "invalid REMOVEBOT:%s"%(args) )
  654. if int(args[0]) == self.battleid:
  655. name = args[1]
  656. if name in self.bots:
  657. del self.bots[name]
  658. if name in self.battle_statusmap:
  659. del self.battle_statusmap[name]
  660. self.FillTeamAndAllies()
  661. def onloggedin(self,socket):
  662. sendstatus( self, socket )
  663. socket.send("JOINBATTLE " + str(self.battleid) + " " + self.battlepassword + "\n")
  664. def FillTeamAndAllies(self):
  665. self.teams = dict()
  666. self.allies = dict()
  667. for bs in self.battle_statusmap.values():
  668. if not bs.spec:
  669. if not bs.team in self.teams:
  670. self.teams[bs.team] = 1
  671. else:
  672. self.teams[bs.team] += 1
  673. if not bs.ally in self.allies:
  674. self.allies[bs.ally] = 1
  675. else:
  676. self.allies[bs.ally] += 1
  677. # print "allies:", self.allies
  678. # print "teams: ",self.teams
  679. # print "battle_statusmap",self.battle_statusmap
  680. def saybattle(self,socket,battleid,message):
  681. for line in message.split('\n'):
  682. self.log.Info( "Battle:%i, Message: %s" %(battleid,line) )
  683. socket.send("SAYBATTLE %s\n" % line)
  684. def saybattleex(self,socket,battleid,message):
  685. for line in message.split('\n'):
  686. self.log.Info( "Battle:%i, Message: %s" %(battleid,line) )
  687. socket.send("SAYBATTLEEX %s\n" % line)
  688. def sayPermissionDenied(self,socket, command, username ):
  689. socket.send("SAYPRIVATE %s You do not have sufficient access right to execute %s on this bot\n" %( username, command ) )
  690. def GetRankInfoDifference(self, pre, post ):
  691. #we cannot assume same ordering or even players in pre and post
  692. res = []
  693. for nick, info in post.iteritems():
  694. post_rank = info[0]
  695. post_pos = info[1]
  696. rank_type = info[2]
  697. if not nick in pre:
  698. pre_rank = rank_type()
  699. pre_pos = 0 #make num player on ladder +1
  700. else:
  701. pre_rank = pre[nick][0]
  702. pre_pos = pre[nick][1]
  703. res.append( '%s:\tNew position: %d (%d)\t New Rank: %s (was %s)'%(nick, post_pos, (pre_pos - post_pos),str(post_rank), str(pre_rank) ) )
  704. return res