def _emitLastState(self, zero_zero_bug=False): _players = self._lastState._players _reliable = False # no ragequit in here if len(_players) != 2: log.log( "[%s] DuelInfo::_emitLastState: len(_players) != 2 (%s)?" % (str(datetime.datetime.now()), _players) ) return if zero_zero_bug: _currtime = self._lastState._currtime _timelimit = self._lastState._timelimit # if we were very close to the end use the scoreline as reliable if _currtime and _timelimit and cutepig.total_seconds(_timelimit - _currtime) < 5: _reliable = True # DEBUG _matchtime = self._lastState._matchtime if self._lastState._matchtime != None else "unknown" log.log( "[%s] DuelInfo: LAST SCORE %s - %s: %d - %d (%s)(%s)" % ( str(datetime.datetime.now()), _players[0]._name, _players[1]._name, _players[0]._score, _players[1]._score, "reliable" if (_reliable) else "non reliable", _matchtime, ) ) self._emitAnyState(self._lastState, _reliable)
def __call__(self, msg): # get the current mem usage fp = open( '/proc/%d/statm' % self.pid, 'r' ) statm = fp.read() fp.close() # all of this in pages # progSize, rssSize, sharedSize, codeSize, libSize, stackSize, dirtySize stats = statm.split() rssSize = int( stats[1] ) * self.pageSize log.log( '%s ( mem usage: %s )' % ( msg, self.prettyprint(rssSize)))
def __call__(self, *args): t = time.time() self.run(args) d = time.time() - t if(d > self._WARN_TIME): log.log("Task::__call__: execution time took %f seconds (%s)" % (d, self.__class__.__name__)) self._event = None # TODO: check nextupdate and stuff # reschedule if( self._registered ) : self._insertJob()
def checkMasterQuery( self, query ) : # FIXME: add internal asynchronous timeout to support nonblocking sockets! # this relying on timeout dont work well in new pigbrowser taskmanager.. try : r, addr = cutepig.net.getResponse2 ( query.socket ) if( not r and not addr ) : return False query.incoming += len ( r ) if ( addr != query.addr ) : log.log( "Warning: getting response2 from different address??" ) return False ofs = self.checkMasterResponse ( r ) if ( ofs == -1 ) : log.log( "Malicious packet from master %s" % ( cutepig.ip_int_str_full(query,addr) ) ) query.error = True return True # master server query stores the ip's in the buffer directly ips = self.parseMasterResponse ( r[ofs:] ) if ( not len(ips) ) : log.log( "Empty ip list from master %s %s" % ( cutepig.ip_int_str_full(query.addr), r ) ) return True # dont take as error? else : # log.log( "Read %d ips (last %s)" % (len(ips), ips[-1]) ) query.buffers.extend ( ips ) except socket.timeout : if ( not len(query.buffers) ) : log.log( "Master server %s timeouted" % ( cutepig.ip_int_str_full(query.addr) ) ) return True # we can stop now (quake specific: timeout=no more data) return False # keep going badass
def __call__(self, addr ): if( not self.geo ) : return ( 'ZZ', 'ZZ' ) loc = self.geo.country_code_by_addr ( ip_int_str (addr) ) if ( loc not in cutepig.continents.countries ) : # theres the occasional bug where we get the continent for # the country so should we check that out in here? log.log( "geolocator: %s not found in continents" % loc ) loc = 'ZZ' cont = 'ZZ' else : cont = cutepig.continents.countries[loc] return ( loc, cont )
def checkServerQuery ( self, query ) : try : r, addr = cutepig.net.getResponse2 ( query.socket ) except : return True if( not r and not addr ) : return False query.incoming += len ( r ) if ( addr != query.addr ) : log.log( "Warning: getting response2 from different address??" ) return False # quake doesnt need no stinking stages query.buffers.append ( r ) return True
def getRules ( self, rulestr ) : # the "rulestring" is divided with backward-slashes # and they are just key-value pairs, there should # be even amount of these split = rulestr.split ( '\\' ) # now its list of [ key, value, key, value.. ] if ( len(split) & 1 ) : log.log( "uneven amount in rules?" ) # key\\value\\key\\value[0x0a] rules = {} for i in range(0,len(split),2 ) : rules[split[i].lower()] = self.string(split[i+1]) return rules
def processInfo(self, info): # SWAP NEW/OLD STATE self._lastState = self._currentState if not self._currentState: self._currentState = DuelState(self, info) if len(self._currentState._players) == 2: log.log( "[%s] Starting to follow %s - %s" % ( str(datetime.datetime.now()), self._currentState._players[0]._name, self._currentState._players[1]._name, ) ) else: self._currentState = DuelState(self, info) # DO STUFF.. LOGIC.. if not self._currentState.isValid(): # num timeouts/errors? # do what? use lastState? self._dropSelf("NOT VALID") return if self._currentState.checkFinished(self._lastState): self._emitCurrentState() self._dropSelf("FINISHED") return if self._currentState.checkNewGame(self._lastState): self._emitLastState() self._dropSelf("NEW GAME") return # DEBUG inform score here _players = self._currentState._players # log.log("[%s] Intermediate score %s - %s: %d - %d @ %s" % (str(datetime.datetime.now()), # _players[0]._name, _players[1]._name, _players[0]._score,_players[1]._score, self._currentState._matchtime)) # schedule next update, minimum interval 1 second, max 30 delta = self._currentState.getRemainingSeconds() * 0.8 delta = min(30.0, max(1.0, delta)) self.setNextUpdate(self.time() + delta)
def run(self, args): if not self._query.checkServerQuery(): # no data, schedule a new update and wait for it self.setNextUpdate(self.time() + self.QUERY_UPDATE_INTERVAL) return # ok, get the info, nullify the query and socket info = self._query.finishServerQuery() self._query = None self._socket.close() self._socket = None self.removeSelf() self._pusher.serverinfoStopped(self) # emit the serverinfo to database _serverinfo = self._emitServerInfo(info) # here, check if we have valid duel going on, if so pass it back to app # which will initiate new duelinfofetcher process if _serverinfo.numplayers == 2 and _serverinfo.flags != FLAG_TIMEOUT: self._app.checkActiveDuel(info) else: pass # log.log("not checking active duel (%s)" % ("numplayers" if _serverinfo.numplayers!=2 else "timeout")) return # comment out above return to print this if "timeout" in info: log.log("serverinfo finished %s (timeout)" % (ip_int_str_full(self._ip))) elif "error" in info: log.log("serverinfo finished %s (error)" % (ip_int_str_full(self._ip))) else: log.log("serverinfo finished %s" % (ip_int_str_full(self._ip)))
def finishServerQuery ( self, query ) : # do the parsing thing if ( len(query.buffers) ) : ofs = self.checkServerResponse ( query.buffers[0], query.isfull ) if ( ofs == -1 ) : log.log( "getInfos, malicious packet from %s" % ( ip_int_str_full(query.addr) ) ) # if ( not return_timeouts ) : # return None info = { 'addr' : query.addr, 'error' : 1 } return info # or None i = self.parseServerResponse ( query.buffers[0][ofs:], query.isfull ) if ( i ) : i['addr'] = query.addr # if ( query.gametype and query.gametype != i['gt'] ) : # return None return i else : info = { 'addr' : query.addr, 'timeout' : 1 } return info # or None
def fromDict(self, d): # TODO: clamp the names to their maximum sizes # Theres sometimes strange excessive amounts of whitespace # in some server names.. please remove them!! # DEBUG: if not "rules" in d: log.log("NO RULES IN: %s" % d) # ch : added strip.. name = d["name"].strip() self.name = name[0:128] # 128 self.mapname = d["map"][0:32] # 32 self.maxclients = d["maxp"] # actually the number of ckueb self.gamename = d["mod"][0:32] # 32 self.flags = d["flags"] # ch : added sortname, only alphabetical characters pattern = re.compile("[\W_]+") self.sortname = pattern.sub("", name) if "rules" in d and "sv_skilllevel" in d["rules"]: self.skilllevel = d["rules"]["sv_skilllevel"] else: self.skilllevel = -1 # TODO: return qi + players # the number of players is # of in-game players # not the spectators n = 0 if "players" in d: for pl in d["players"]: if pl["team"] > 1: n += 1 self.numplayers = n self.numspecs = d["nump"] - n
def parseServerResponse ( self, buf, isfull ) : ls = buf.split ( '\x0a' ) if ( len(ls) < 1 ) : log.log( 'empty server response??\n%s' % buf ) return None rules = self.getRules ( ls[0] ) players = [] # number score time ping "name" "skin" color1 color2 if (isfull and len(ls) > 1 ) : for player in ls[1:] : player = player.strip() if ( len(player) <= 1 ) : continue d = self.parsePlayer ( player ) if ( d ) : players.append ( d ) if ( isfull ) : return self.fullInfo ( rules, players ) else : return self.quickInfo ( rules )
def isReliable(self): if(self._matchtime != 'finished'): log.log("DuelState::isReliable: matchtime") return False # check the score line for 0-0 bug if(len(self._players) != 2 ): log.log("DuelState::isReliable: len(players) != 2 (%d)" % (len(self._players))) return False if(self._players[0]._score == 0 and self._players[1]._score == 0): log.log("DuelState::isReliable: 0-0 bug") return False # hmm, what else return True
def checkNewGame(self, last): if(not last): return False # first validate that the last state had a matchtime if(not last._currtime): return False if(last._matchtime in ['countdown', 'warmup','finished']): return False # ok, lets check if current time is something else if(self._matchtime in ['countdown', 'warmup']): log.log("DuelState::checkNewGame: matchtime @ %s" % self._matchtime) return True if(not self._currtime): log.log("DuelState::checkNewGame: currtime null") return True if(self._currtime < last._currtime): # overtime logic if(not self._overtime): log.log("DuelState::checkNewGame: currtime smaller %s %s" % (self._currtime, last._currtime)) return True return False
def _emitCurrentState(self): _players = self._currentState._players _reliable = self._currentState.isReliable() _ragequit = self._currentState.checkRagequit(self._lastState) if _ragequit: # _players.append(_ragequit) _reliable = False log.log("** RAGEQUIT: %s" % (_ragequit._name)) if not _reliable: zero_zero_bug = False if len(_players) == 2 and _players[0]._score == 0 and _players[1]._score == 0: zero_zero_bug = True self._emitLastState(zero_zero_bug) return if len(_players) != 2: log.log( "[%s] DuelInfo::_emitCurrentState: len(_players) != 2 (%s)?" % (str(datetime.datetime.now()), _players) ) return # fix the overtime flag from laststate if self._lastState: self._currentState._overtime = self._lastState._overtime # DEBUG log.log( "[%s] DuelInfo: FINAL SCORE %s - %s: %d - %d (%s)" % ( str(datetime.datetime.now()), _players[0]._name, _players[1]._name, _players[0]._score, _players[1]._score, "reliable" if (_reliable) else "non reliable", ) ) self._emitAnyState(self._currentState, _reliable)
def duelStopped(self, duel): if(duel in self._activeServers): self._activeServers.remove(duel) else: log.log("Application::duelStopped: job not in the list")
def __call__(self, s): endTime = datetime.datetime.now() log.log( "%s ( time %s )" % ( s, (endTime - self.startTime)))
def _dropSelf(self, msg=None): self._app.duelStopped(self) self.removeSelf() if msg: log.log("[%s] DuelInfo dropped: %s" % (str(datetime.datetime.now()), msg))
def __init__(self, s): self.startTime = datetime.datetime.now() log.log( "%s - %s" % ( self.startTime, s ) )