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