class MapStats(db.Model): id = db.Column(db.Integer, primary_key=True) match_id = db.Column(db.Integer, db.ForeignKey('match.id')) map_number = db.Column(db.Integer) map_name = db.Column(db.String(64)) start_time = db.Column(db.DateTime) end_time = db.Column(db.DateTime) winner = db.Column(db.Integer, db.ForeignKey('team.id')) team1_score = db.Column(db.Integer, default=0) team2_score = db.Column(db.Integer, default=0) player_stats = db.relationship('PlayerStats', backref='mapstats', lazy='dynamic') @staticmethod def get_or_create(match_id, map_number, map_name=''): match = Match.query.get(match_id) if match is None or map_number >= match.max_maps: return None rv = MapStats.query.filter_by(match_id=match_id, map_number=map_number).first() if rv is None: rv = MapStats() rv.match_id = match_id rv.map_number = map_number rv.map_name = map_name rv.start_time = datetime.datetime.utcnow() rv.team1_score = 0 rv.team2_score = 0 db.session.add(rv) return rv def __repr__(self): return 'MapStats(' + str(self.id) + ',' + str(self.map_name) + ')'
class Season(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) name = db.Column(db.String(60), default='') start_date = db.Column(db.DateTime, default=datetime.datetime.utcnow()) end_date = db.Column(db.DateTime) matches = db.relationship('Match', backref='season', lazy='dynamic') @staticmethod def create(user, name, start_date, end_date): rv = Season() rv.user_id = user.id rv.name = name rv.start_date = start_date rv.end_date = end_date db.session.add(rv) return rv def get_season_name(self): return self.name def set_data(self, user, name, start_date, end_date): self.user_id = user.id self.name = name self.start_date = start_date self.end_date = end_date def can_edit(self, user): if not user: return False if self.user_id == user.id: return True return False def can_delete(self, user): if not self.can_edit(user): return False return self.get_recent_matches().count() == 0 def get_recent_matches(self, limit=10): season = Season.query.get_or_404(self.id) matches = season.matches recent_matches = matches.filter( (Match.season_id == self.id) & (Match.cancelled == False) & (Match.start_time != None) # noqa: E712 ).order_by(-Match.id).limit(5) if recent_matches is None: return [] else: return recent_matches def __repr__(self): return 'Season(id={}, user_id={}, name={}, start_date={}, end_date={})'.format( self.id, self.user_id, self.name, self.start_date, self.end_date)
class GameServer(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) display_name = db.Column(db.String(32), default='') ip_string = db.Column(db.String(32)) port = db.Column(db.Integer) portgotv = db.Column(db.Integer, default='') rcon_password = db.Column(db.String(32)) in_use = db.Column(db.Boolean, default=False) public_server = db.Column(db.Boolean, default=False, index=True) @staticmethod def create(user, display_name, ip_string, port, portgotv, rcon_password, public_server): rv = GameServer() rv.user_id = user.id rv.display_name = display_name rv.ip_string = ip_string rv.port = port rv.portgotv = portgotv rv.rcon_password = rcon_password rv.public_server = public_server db.session.add(rv) return rv def send_rcon_command(self, command, raise_errors=False, num_retries=3, timeout=3.0): return util.send_rcon_command(self.ip_string, self.port, self.rcon_password, command, raise_errors, num_retries, timeout) def get_hostport(self): return '{}:{}'.format(self.ip_string, self.port) def get_hostportgotv(self): return '{}:{}'.format(self.ip_string, self.portgotv) def get_display(self): if self.display_name: return '{} ({})'.format(self.display_name, self.get_hostport()) else: return self.get_hostport() def __repr__(self): return 'GameServer({})'.format(self.get_hostport())
class Veto(db.Model): id = db.Column(db.Integer, primary_key=True) match_id = db.Column(db.Integer, db.ForeignKey('match.id')) team_name = db.Column(db.String(64), default='') map = db.Column(db.String(32), default='') pick_or_veto = db.Column(db.String(4), default='veto') @staticmethod def create(match_id, team_name, map_name, p_v): rv = Veto() rv.match_id = match_id rv.team_name = team_name rv.map = map_name rv.pick_or_veto = p_v db.session.add(rv) return rv def __repr__(self): return 'Veto(id={})'.format(self.id)
class MatchSpectator(db.Model): id = db.Column(db.Integer, primary_key=True) match_id = db.Column(db.Integer, db.ForeignKey('match.id')) auth = db.Column(db.String(17)) @staticmethod def set_or_create(match_id, auth): rv = MatchSpectator.query.filter_by(match_id=match_id, auth=auth).first() if rv is None: rv = MatchSpectator() rv.set_data(match_id, auth) db.session.add(rv) else: rv.set_data(match_id, auth) return rv def set_data(self, match_id, auth): self.match_id = match_id self.auth = auth
class TeamAuthNames(db.Model): id = db.Column(db.Integer, primary_key=True) team_id = db.Column(db.Integer, db.ForeignKey('team.id')) auth = db.Column(db.String(17)) name = db.Column(db.String(40)) @staticmethod def set_or_create(team_id, auth, name): rv = TeamAuthNames.query.filter_by(team_id=team_id, auth=auth).first() if rv is None: rv = TeamAuthNames() rv.set_data(team_id, auth, name) db.session.add(rv) else: rv.set_data(team_id, auth, name) return rv def set_data(self, team_id, auth, name): self.team_id = team_id self.auth = auth self.name = name if name else ''
class PlayerStats(db.Model): id = db.Column(db.Integer, primary_key=True) match_id = db.Column(db.Integer, db.ForeignKey('match.id')) map_id = db.Column(db.Integer, db.ForeignKey('map_stats.id')) team_id = db.Column(db.Integer, db.ForeignKey('team.id')) steam_id = db.Column(db.String(40)) name = db.Column(db.String(40)) kills = db.Column(db.Integer, default=0) deaths = db.Column(db.Integer, default=0) roundsplayed = db.Column(db.Integer, default=0) assists = db.Column(db.Integer, default=0) flashbang_assists = db.Column(db.Integer, default=0) teamkills = db.Column(db.Integer, default=0) suicides = db.Column(db.Integer, default=0) headshot_kills = db.Column(db.Integer, default=0) damage = db.Column(db.Integer, default=0) bomb_plants = db.Column(db.Integer, default=0) bomb_defuses = db.Column(db.Integer, default=0) v1 = db.Column(db.Integer, default=0) v2 = db.Column(db.Integer, default=0) v3 = db.Column(db.Integer, default=0) v4 = db.Column(db.Integer, default=0) v5 = db.Column(db.Integer, default=0) k1 = db.Column(db.Integer, default=0) k2 = db.Column(db.Integer, default=0) k3 = db.Column(db.Integer, default=0) k4 = db.Column(db.Integer, default=0) k5 = db.Column(db.Integer, default=0) firstkill_t = db.Column(db.Integer, default=0) firstkill_ct = db.Column(db.Integer, default=0) firstdeath_t = db.Column(db.Integer, default=0) firstdeath_Ct = db.Column(db.Integer, default=0) def get_steam_id(self): return self.steam_id def get_steam_url(self): return 'http://steamcommunity.com/profiles/{}'.format(self.steam_id) def get_player_name(self): return get_steam_name(self.steam_id) def get_rating(self): try: AverageKPR = 0.679 AverageSPR = 0.317 AverageRMK = 1.277 KillRating = float(self.kills) / float( self.roundsplayed) / AverageKPR SurvivalRating = float(self.roundsplayed - self.deaths ) / self.roundsplayed / AverageSPR killcount = float(self.k1 + 4 * self.k2 + 9 * self.k3 + 16 * self.k4 + 25 * self.k5) RoundsWithMultipleKillsRating = killcount / \ self.roundsplayed / AverageRMK rating = (KillRating + 0.7 * SurvivalRating + RoundsWithMultipleKillsRating) / 2.7 return rating except ZeroDivisionError: return 0 def get_kdr(self): if self.deaths == 0: return float(self.kills) else: return float(self.kills) / self.deaths def get_hsp(self): if self.kills == 0: return 0.0 else: return float(self.headshot_kills) / self.kills def get_adr(self): if self.roundsplayed == 0: return 0.0 else: return float(self.damage) / self.roundsplayed def get_fpr(self): if self.roundsplayed == 0: return 0.0 else: return float(self.kills) / self.roundsplayed """ Custom made individual scoreboard to work with VMIX and an Excel file. The values will be automagically updated and put straight into a broadcast (neat!)""" def get_ind_scoreboard(self, map_number): d = {} map_stats = MapStats.query.filter_by(match_id=self.match_id, map_number=map_number).first() team = Team.query.get(self.team_id) d['map'] = map_stats.map_name d[team.name] = {} d[team.name][self.steam_id] = {} d[team.name][self.steam_id]['Player'] = get_steam_name(self.steam_id) d[team.name][self.steam_id]['kills'] = round(float(self.kills), 1) d[team.name][self.steam_id]['deaths'] = round(float(self.deaths), 1) d[team.name][self.steam_id]['assists'] = round(float(self.assists), 1) d[team.name][self.steam_id]['rating'] = round(float(self.get_rating()), 2) d[team.name][self.steam_id]['hsp'] = round(float(self.get_hsp()), 2) d[team.name][self.steam_id]['firstkill'] = round( float(self.firstkill_ct + self.firstkill_t), 1) d[team.name][self.steam_id]['k2'] = round(float(self.k2), 1) d[team.name][self.steam_id]['k3'] = round(float(self.k3), 1) d[team.name][self.steam_id]['k4'] = round(float(self.k4), 1) d[team.name][self.steam_id]['k5'] = round(float(self.k5), 1) d[team.name][self.steam_id]['ADR'] = round(float(self.get_adr()), 1) return d def get_deaths(self): return float(self.deaths) @staticmethod def get_or_create(matchid, mapnumber, steam_id): mapstats = MapStats.get_or_create(matchid, mapnumber) if len(mapstats.player_stats.all()) >= 40: # Cap on players per map return None rv = mapstats.player_stats.filter_by(steam_id=steam_id).first() if rv is None: rv = PlayerStats() rv.match_id = matchid rv.map_number = mapstats.id rv.steam_id = steam_id rv.map_id = mapstats.id db.session.add(rv) return rv def statsToCSVRow(self): team = Team.query.get(self.team_id) ourCSVText = [ team.name, self.steam_id, get_steam_name(self.steam_id), round(float(self.kills), 1), round(float(self.deaths), 1), round(float(self.assists), 1), round(float(self.get_rating() * 100), 2), round(float(self.get_hsp() * 100), 2), round(float(self.firstkill_ct + self.firstkill_t), 1), round(float(self.k2), 1), round(float(self.k3), 1), round(float(self.k4), 1), round(float(self.k5), 1), round(float(self.get_adr()), 1) ] return (ourCSVText)
class GameServer(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) display_name = db.Column(db.String(32), default='') ip_string = db.Column(db.String(32)) port = db.Column(db.Integer) rcon_password = db.Column(db.String(128)) in_use = db.Column(db.Boolean, default=False) public_server = db.Column(db.Boolean, default=False, index=True) @staticmethod def create(user, display_name, ip_string, port, rcon_password, public_server): rv = GameServer() rv.user_id = user.id rv.display_name = display_name rv.ip_string = ip_string rv.port = port rv.rcon_password = rcon_password rv.public_server = public_server db.session.add(rv) return rv def send_rcon_command(self, command, raise_errors=False, num_retries=3, timeout=3.0): encRcon = util.decrypt(dbKey, self.rcon_password) if encRcon is None: encRcon = self.rcon_password return util.send_rcon_command(self.ip_string, self.port, encRcon, command, raise_errors, num_retries, timeout) def get_hostport(self): return '{}:{}'.format(self.ip_string, self.port) def get_display(self): if self.display_name: return '{} ({})'.format(self.display_name, self.get_hostport()) else: return self.get_hostport() def receive_rcon_value(self, command): try: response = self.send_rcon_command(command, raise_errors=False) if response is not None: pattern = r'"([A-Za-z0-9_\./\\-]*)"' value = re.split(pattern, Markup(response.replace('\n', '<br>'))) else: return None except Exception as e: app.logger.info( "Tried to receive value from server but failed.\n{}".format(e)) return None # Not sure how stable this will be, but send off for the third # value of the string split. Most values returned have format # "sv_password" = "test" (def. "") return value[3] def __repr__(self): return 'GameServer({})'.format(self.get_hostport())
class Match(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) server_id = db.Column(db.Integer, db.ForeignKey('game_server.id'), index=True) team1_id = db.Column(db.Integer, db.ForeignKey('team.id')) team2_id = db.Column(db.Integer, db.ForeignKey('team.id')) season_id = db.Column(db.Integer, db.ForeignKey('season.id')) team1_string = db.Column(db.String(32), default='') team2_string = db.Column(db.String(32), default='') winner = db.Column(db.Integer, db.ForeignKey('team.id')) plugin_version = db.Column(db.String(32), default='unknown') forfeit = db.Column(db.Boolean, default=False) cancelled = db.Column(db.Boolean, default=False) start_time = db.Column(db.DateTime) end_time = db.Column(db.DateTime) max_maps = db.Column(db.Integer) title = db.Column(db.String(60), default='') skip_veto = db.Column(db.Boolean) api_key = db.Column(db.String(32)) veto_first = db.Column(db.String(5)) veto_mappool = db.Column(db.String(500)) map_stats = db.relationship('MapStats', backref='match', lazy='dynamic') side_type = db.Column(db.String(32)) team1_score = db.Column(db.Integer, default=0) team2_score = db.Column(db.Integer, default=0) team1_series_score = db.Column(db.Integer, default=0) team2_series_score = db.Column(db.Integer, default=0) spectator_auths = db.Column(db.PickleType) private_match = db.Column(db.Boolean) enforce_teams = db.Column(db.Boolean, default=True) @staticmethod def create(user, team1_id, team2_id, team1_string, team2_string, max_maps, skip_veto, title, veto_mappool, season_id, side_type, veto_first, server_id=None, team1_series_score=None, team2_series_score=None, spectator_auths=None, private_match=False, enforce_teams=True): rv = Match() rv.user_id = user.id rv.team1_id = team1_id rv.team2_id = team2_id rv.season_id = season_id rv.side_type = side_type rv.skip_veto = skip_veto rv.title = title rv.veto_mappool = ' '.join(veto_mappool) rv.server_id = server_id rv.max_maps = max_maps if veto_first == "CT": rv.veto_first = "team1" elif veto_first == "T": rv.veto_first = "team2" else: rv.veto_first = None rv.api_key = ''.join( random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(24)) rv.team1_series_score = team1_series_score rv.team2_series_score = team2_series_score rv.spectator_auths = spectator_auths rv.private_match = private_match rv.enforce_teams = enforce_teams db.session.add(rv) return rv def get_status_string(self, show_winner=True): if self.pending(): return 'Pending' elif self.live(): team1_score, team2_score = self.get_current_score() return 'Live, {}:{}'.format(team1_score, team2_score) elif self.finished(): t1score, t2score = self.get_current_score() min_score = min(t1score, t2score) max_score = max(t1score, t2score) score_string = '{}:{}'.format(max_score, min_score) if not show_winner: return 'Finished' elif self.winner == self.team1_id: return 'Won {} by {}'.format(score_string, self.get_team1().name) elif self.winner == self.team2_id: return 'Won {} by {}'.format(score_string, self.get_team2().name) else: return 'Tied {}'.format(score_string) else: return 'Cancelled' def is_private_match(self): return self.private_match def get_vs_string(self): team1 = self.get_team1() team2 = self.get_team2() scores = self.get_current_score() str = '{} vs {} ({}:{})'.format(team1.get_name_url_html(), team2.get_name_url_html(), scores[0], scores[1]) return Markup(str) def finalized(self): return self.cancelled or self.finished() def pending(self): return self.start_time is None and not self.cancelled def finished(self): return self.end_time is not None and not self.cancelled def live(self): return self.start_time is not None and self.end_time is None and not self.cancelled def get_server(self): return GameServer.query.filter_by(id=self.server_id).first() def get_start_time(self): return self.start_time if self.start_time is not None else '' def get_end_time(self): return self.end_time if self.end_time is not None else '' def get_season(self): if self.season_id: return Season.query.get(self.season_id) else: return None def get_season_id(self): return self.season_id def get_current_score(self): if self.max_maps == 1: mapstat = self.map_stats.first() if not mapstat: return (0, 0) else: return (mapstat.team1_score, mapstat.team2_score) else: return (self.team1_score, self.team2_score) def send_to_server(self): server = GameServer.query.get(self.server_id) if not server: return False url = url_for('match.match_config', matchid=self.id, _external=True, _scheme='http') # Remove http protocal since the get5 plugin can't parse args with the # : in them. url = url.replace("http://", "") url = url.replace("https://", "") loadmatch_response = server.send_rcon_command('get5_loadmatch_url ' + url) server.send_rcon_command('get5_web_api_key ' + self.api_key) # ***HACK FIX TO ENSURE CHECK_AUTHS WORKS AS INTENDED*** server.send_rcon_command('map de_dust2') if loadmatch_response: # There should be no response return False return True def get_team1(self): return Team.query.get(self.team1_id) def get_team2(self): return Team.query.get(self.team2_id) def get_user(self): return User.query.get(self.user_id) def get_winner(self): if self.team1_score > self.team2_score: return self.get_team1() elif self.team2_score > self.team1_score: return self.get_team2() else: return None def get_loser(self): if self.team1_score > self.team2_score: return self.get_team2() elif self.team2_score > self.team1_score: return self.get_team1() else: return None def build_match_dict(self): d = {} d['matchid'] = str(self.id) d['match_title'] = self.title d['side_type'] = self.side_type d['veto_first'] = self.veto_first d['skip_veto'] = self.skip_veto if self.max_maps == 2: d['bo2_series'] = True else: d['maps_to_win'] = self.max_maps / 2 + 1 def add_team_data(teamkey, teamid, matchtext): team = Team.query.get(teamid) if not team: return d[teamkey] = {} # Add entries if they have values. def add_if(key, value): if value: d[teamkey][key] = value add_if('name', team.name) add_if('name', team.name) add_if('tag', team.tag) add_if('flag', team.flag.upper()) add_if('logo', team.logo) add_if('matchtext', matchtext) # Add new series score. if teamkey == 'team1': add_if('series_score', self.team1_series_score) else: add_if('series_score', self.team2_series_score) # Attempt to send in KV Pairs of preferred names. # If none, or error, send in the regular list. try: d[teamkey]['players'] = OrderedDict() for uid, name in zip(team.auths, team.preferred_names): if uid: d[teamkey]['players'][uid] = name except: d[teamkey]['players'] = filter(lambda x: x != '', team.auths) add_team_data('team1', self.team1_id, self.team1_string) add_team_data('team2', self.team2_id, self.team2_string) d['cvars'] = {} d['cvars']['get5_web_api_url'] = url_for('home', _external=True, _scheme='http') d['cvars']['get5_check_auths'] = "1" if self.enforce_teams else "0" # Add in for spectators modification. d['min_spectators_to_ready'] = 0 # Perm spectators will go within config, then can add more from match # screen. d['spectators'] = {"players": app.config['SPECTATOR_IDS']} # If we don't have any perm spectators, create the new list. if not d['spectators']: d['spectators'] = {"players": []} # Append auths from match page if we have any. if self.spectator_auths: for spectator in self.spectator_auths: d['spectators']["players"].append(spectator) if not d['spectators']['players']: d['spectators'] = None if self.veto_mappool: d['maplist'] = [] for map in self.veto_mappool.split(): d['maplist'].append(map) return d def __repr__(self): return 'Match(id={})'.format(self.id)
class Team(db.Model): MAXPLAYERS = 7 id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) name = db.Column(db.String(40)) tag = db.Column(db.String(40), default='') flag = db.Column(db.String(4), default='') logo = db.Column(db.String(10), default='') auths = db.Column(db.PickleType) public_team = db.Column(db.Boolean, index=True) preferred_names = db.Column(db.PickleType) @staticmethod def create(user, name, tag, flag, logo, auths, public_team=False, preferred_names=None): rv = Team() rv.user_id = user.id rv.set_data(name, tag, flag, logo, auths, (public_team and util.is_admin(user)), preferred_names) db.session.add(rv) return rv def set_data(self, name, tag, flag, logo, auths, public_team, preferred_names=None): self.name = name self.tag = tag self.flag = flag.lower() if flag else '' self.logo = logo self.auths = auths self.public_team = public_team self.preferred_names = preferred_names def can_edit(self, user): if not user: return False if self.user_id == user.id: return True if util.is_super_admin(user): return True return False def get_players(self): results = [] for steam64 in self.auths: if steam64: name = get_steam_name(steam64) if not name: name = '' results.append((steam64, name)) return results def can_delete(self, user): if not self.can_edit(user): return False return self.get_recent_matches().count() == 0 def get_recent_matches(self, limit=10): if self.public_team: matches = Match.query.order_by(-Match.id).limit(100).from_self() else: owner = User.query.get_or_404(self.user_id) matches = owner.matches recent_matches = matches.filter( ((Match.team1_id == self.id) | (Match.team2_id == self.id)) & (Match.cancelled == False) & (Match.start_time != None) # noqa: E712 ).order_by(-Match.id).limit(5) if recent_matches is None: return [] else: return recent_matches def get_vs_match_result(self, match_id): other_team = None my_score = 0 other_team_score = 0 match = Match.query.get(match_id) if match.team1_id == self.id: my_score = match.team1_score other_team_score = match.team2_score other_team = Team.query.get(match.team2_id) else: my_score = match.team2_score other_team_score = match.team1_score other_team = Team.query.get(match.team1_id) # for a bo1 replace series score with the map score if match.max_maps == 1: mapstat = match.map_stats.first() if mapstat: if match.team1_id == self.id: my_score = mapstat.team1_score other_team_score = mapstat.team2_score else: my_score = mapstat.team2_score other_team_score = mapstat.team1_score if match.live(): return 'Live, {}:{} vs {}'.format(my_score, other_team_score, other_team.name) if my_score < other_team_score: return 'Lost {}:{} vs {}'.format(my_score, other_team_score, other_team.name) elif my_score > other_team_score: return 'Won {}:{} vs {}'.format(my_score, other_team_score, other_team.name) else: return 'Tied {}:{} vs {}'.format(other_team_score, my_score, other_team.name) def get_flag_html(self, scale=1.0): # flags are expected to be 32x21 width = int(round(32.0 * scale)) height = int(round(21.0 * scale)) html = '<img src="{}" width="{}" height="{}">' output = html.format(countries.get_flag_img_path(self.flag), width, height) return Markup(output) def get_logo_html(self, scale=1.0): if logos.has_logo(self.logo): width = int(round(32.0 * scale)) height = int(round(32.0 * scale)) html = ('<img src="{}" width="{}" height="{}">') return Markup( html.format(logos.get_logo_img(self.logo), width, height)) else: #app.logger.info("Looked for {} but found nothing.".format(self.logo)) return '' def get_url(self): return url_for('team.team', teamid=self.id) def get_name_url_html(self): return Markup('<a href="{}">{}</a>'.format(self.get_url(), self.name)) def get_logo_or_flag_html(self, scale=1.0, other_team=None): if logos.has_logo(self.logo): return self.get_logo_html(scale) else: return self.get_flag_html(scale) def __repr__(self): return 'Team(id={}, user_id={}, name={}, flag={}, logo={}, public={})'.format( self.id, self.user_id, self.name, self.flag, self.logo, self.public_team)
class PlayerStats(db.Model): id = db.Column(db.Integer, primary_key=True) match_id = db.Column(db.Integer, db.ForeignKey('match.id')) map_id = db.Column(db.Integer, db.ForeignKey('map_stats.id')) team_id = db.Column(db.Integer, db.ForeignKey('team.id')) steam_id = db.Column(db.String(40)) name = db.Column(db.String(40)) kills = db.Column(db.Integer, default=0) deaths = db.Column(db.Integer, default=0) roundsplayed = db.Column(db.Integer, default=0) assists = db.Column(db.Integer, default=0) flashbang_assists = db.Column(db.Integer, default=0) teamkills = db.Column(db.Integer, default=0) suicides = db.Column(db.Integer, default=0) headshot_kills = db.Column(db.Integer, default=0) damage = db.Column(db.Integer, default=0) bomb_plants = db.Column(db.Integer, default=0) bomb_defuses = db.Column(db.Integer, default=0) v1 = db.Column(db.Integer, default=0) v2 = db.Column(db.Integer, default=0) v3 = db.Column(db.Integer, default=0) v4 = db.Column(db.Integer, default=0) v5 = db.Column(db.Integer, default=0) k1 = db.Column(db.Integer, default=0) k2 = db.Column(db.Integer, default=0) k3 = db.Column(db.Integer, default=0) k4 = db.Column(db.Integer, default=0) k5 = db.Column(db.Integer, default=0) firstkill_t = db.Column(db.Integer, default=0) firstkill_ct = db.Column(db.Integer, default=0) firstdeath_t = db.Column(db.Integer, default=0) firstdeath_Ct = db.Column(db.Integer, default=0) def get_steam_url(self): return 'http://steamcommunity.com/profiles/{}'.format(self.steam_id) def get_rating(self): AverageKPR = 0.679 AverageSPR = 0.317 AverageRMK = 1.277 KillRating = float(self.kills) / float(self.roundsplayed) / AverageKPR SurvivalRating = float(self.roundsplayed - self.deaths) / self.roundsplayed / AverageSPR killcount = float(self.k1 + 4 * self.k2 + 9 * self.k3 + 16 * self.k4 + 25 * self.k5) RoundsWithMultipleKillsRating = killcount / \ self.roundsplayed / AverageRMK rating = (KillRating + 0.7 * SurvivalRating + RoundsWithMultipleKillsRating) / 2.7 return rating def get_kdr(self): if self.deaths == 0: return float(self.kills) else: return float(self.kills) / self.deaths def get_hsp(self): if self.kills == 0: return 0.0 else: return float(self.headshot_kills) / self.kills def get_adr(self): if self.roundsplayed == 0: return 0.0 else: return float(self.damage) / self.roundsplayed def get_fpr(self): if self.roundsplayed == 0: return 0.0 else: return float(self.kills) / self.roundsplayed @staticmethod def get_or_create(matchid, mapnumber, steam_id): mapstats = MapStats.get_or_create(matchid, mapnumber) if len(mapstats.player_stats.all()) >= 40: # Cap on players per map return None rv = mapstats.player_stats.filter_by(steam_id=steam_id).first() if rv is None: rv = PlayerStats() rv.match_id = matchid rv.map_number = mapstats.id rv.steam_id = steam_id rv.map_id = mapstats.id db.session.add(rv) return rv
class Match(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) server_id = db.Column(db.Integer, db.ForeignKey('game_server.id'), index=True) team1_id = db.Column(db.Integer, db.ForeignKey('team.id')) team2_id = db.Column(db.Integer, db.ForeignKey('team.id')) team1_string = db.Column(db.String(32), default='') team2_string = db.Column(db.String(32), default='') winner = db.Column(db.Integer, db.ForeignKey('team.id')) plugin_version = db.Column(db.String(32), default='unknown') forfeit = db.Column(db.Boolean, default=False) cancelled = db.Column(db.Boolean, default=False) start_time = db.Column(db.DateTime) end_time = db.Column(db.DateTime) max_maps = db.Column(db.Integer) title = db.Column(db.String(60), default='') skip_veto = db.Column(db.Boolean) api_key = db.Column(db.String(32)) veto_mappool = db.Column(db.String(500)) map_stats = db.relationship('MapStats', backref='match', lazy='dynamic') team1_score = db.Column(db.Integer, default=0) team2_score = db.Column(db.Integer, default=0) @staticmethod def create(user, team1_id, team2_id, team1_string, team2_string, max_maps, skip_veto, title, veto_mappool, server_id=None): rv = Match() rv.user_id = user.id rv.team1_id = team1_id rv.team2_id = team2_id rv.skip_veto = skip_veto rv.title = title rv.veto_mappool = ' '.join(veto_mappool) rv.server_id = server_id rv.max_maps = max_maps rv.api_key = ''.join( random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(24)) db.session.add(rv) return rv def get_status_string(self, show_winner=True): if self.pending(): return 'Pending' elif self.live(): team1_score, team2_score = self.get_current_score() return 'Live, {}:{}'.format(team1_score, team2_score) elif self.finished(): t1score, t2score = self.get_current_score() min_score = min(t1score, t2score) max_score = max(t1score, t2score) score_string = '{}:{}'.format(max_score, min_score) if not show_winner: return 'Finished' elif self.winner == self.team1_id: return 'Won {} by {}'.format(score_string, self.get_team1().name) elif self.winner == self.team2_id: return 'Won {} by {}'.format(score_string, self.get_team2().name) else: return 'Tied {}'.format(score_string) else: return 'Cancelled' def get_vs_string(self): team1 = self.get_team1() team2 = self.get_team2() scores = self.get_current_score() str = '{} vs {} ({}:{})'.format(team1.get_name_url_html(), team2.get_name_url_html(), scores[0], scores[1]) return Markup(str) def finalized(self): return self.cancelled or self.finished() def pending(self): return self.start_time is None and not self.cancelled def finished(self): return self.end_time is not None and not self.cancelled def live(self): return self.start_time is not None and self.end_time is None and not self.cancelled def get_server(self): return GameServer.query.filter_by(id=self.server_id).first() def get_current_score(self): if self.max_maps == 1: mapstat = self.map_stats.first() if not mapstat: return (0, 0) else: return (mapstat.team1_score, mapstat.team2_score) else: return (self.team1_score, self.team2_score) def send_to_server(self): server = GameServer.query.get(self.server_id) if not server: return False url = url_for('match.match_config', matchid=self.id, _external=True, _scheme='http') # Remove http protocal since the get5 plugin can't parse args with the # : in them. url = url.replace("http://", "") url = url.replace("https://", "") loadmatch_response = server.send_rcon_command('get5_loadmatch_url ' + url) server.send_rcon_command('get5_web_api_key ' + self.api_key) if loadmatch_response: # There should be no response return False return True def get_team1(self): return Team.query.get(self.team1_id) def get_team2(self): return Team.query.get(self.team2_id) def get_user(self): return User.query.get(self.user_id) def get_winner(self): if self.team1_score > self.team2_score: return self.get_team1() elif self.team2_score > self.team1_score: return self.get_team2() else: return None def get_loser(self): if self.team1_score > self.team2_score: return self.get_team2() elif self.team2_score > self.team1_score: return self.get_team1() else: return None def build_match_dict(self): d = {} d['matchid'] = str(self.id) d['match_title'] = self.title d['skip_veto'] = self.skip_veto if self.max_maps == 2: d['bo2_series'] = True else: d['maps_to_win'] = self.max_maps / 2 + 1 def add_team_data(teamkey, teamid, matchtext): team = Team.query.get(teamid) if not team: return d[teamkey] = {} # Add entries if they have values. def add_if(key, value): if value: d[teamkey][key] = value add_if('name', team.name) add_if('name', team.name) add_if('tag', team.tag) add_if('flag', team.flag.upper()) add_if('logo', team.logo) add_if('matchtext', matchtext) d[teamkey]['players'] = filter(lambda x: x != '', team.auths) add_team_data('team1', self.team1_id, self.team1_string) add_team_data('team2', self.team2_id, self.team2_string) d['cvars'] = {} d['cvars']['get5_web_api_url'] = url_for('home', _external=True, _scheme='http') if self.veto_mappool: d['maplist'] = [] for map in self.veto_mappool.split(): d['maplist'].append(map) if 'SPECTATOR_IDS' in app.config: d['spectators'] = {"players": app.config['SPECTATOR_IDS']} if 'MIN_SPECTATORS_TO_READY' in app.config: d['min_spectators_to_ready'] = app.config[ 'MIN_SPECTATORS_TO_READY'] return d def __repr__(self): return 'Match(id={})'.format(self.id)
class GameServer(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) display_name = db.Column(db.String(32), default='') ip_string = db.Column(db.String(32)) port = db.Column(db.Integer) gotv_port = db.Column(db.Integer) rcon_password = db.Column(db.String(32)) in_use = db.Column(db.Boolean, default=False) public_server = db.Column(db.Boolean, default=False, index=True) dathost_id = db.Column(db.String(40)) @staticmethod def create(user, display_name, ip_string, port, gotv_port, rcon_password, dathost_id, public_server): rv = GameServer() rv.user_id = user.id rv.display_name = display_name rv.ip_string = ip_string rv.port = port rv.gotv_port = gotv_port rv.rcon_password = rcon_password rv.dathost_id = dathost_id rv.public_server = public_server db.session.add(rv) return rv def send_rcon_command(self, command, raise_errors=False, num_retries=3, timeout=3.0): return util.send_rcon_command(self.ip_string, self.port, self.rcon_password, command, raise_errors, num_retries, timeout) def get_hostport(self): return '{}:{}'.format(self.ip_string, self.port) def get_display(self): if self.display_name: return '{} ({})'.format(self.display_name, self.get_hostport()) else: return self.get_hostport() def get_displayname(self): return '{}'.format(self.display_name) def get_dathostid(self): return self.dathost_id() def get_dathost_ison(self): #ToDo #Status of Dathost server # response = requests.get('https://dathost.net/api/0.1/game-servers/{}'.format(self.dathost_id), auth=(user, passw)) # if response.status_code == 200: # jsonresponse = response.json() # on = jsonresponse['on'] # if on == "True": # return True return False def __repr__(self): return 'GameServer({})'.format(self.get_hostport())
class Tournament(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) winner = db.Column(db.Integer, db.ForeignKey('team.id')) cancelled = db.Column(db.Boolean, default=False) finished = db.Column(db.Boolean, default=False) start_time = db.Column(db.DateTime) end_time = db.Column(db.DateTime) name = db.Column(db.String(60)) url = db.Column(db.String(60)) challonge_id = db.Column(db.Integer) challonge_data = db.Column(db.PickleType) participants = db.relationship('Team', secondary=TournamentTeam, backref='tournaments', lazy='dynamic') matches = db.relationship('Match', backref='tournament', lazy='dynamic') serverpool = db.relationship('GameServer', secondary=TournamentGameServer, backref='tournaments', lazy='dynamic') veto_mappool = db.Column(db.String(160)) @staticmethod def create(user, name, url, veto_mappool, serverpool=None, challonge_id=None, challonge_data=None): rv = Tournament() rv.user_id = user.id rv.url = url rv.name = name rv.veto_mappool = ' '.join(veto_mappool) if serverpool: rv.serverpool.extend(serverpool) rv.challonge_id = challonge_id rv.challonge_data = challonge_data db.session.add(rv) return rv def finalized(self): return self.cancelled or self.finished() def pending(self): return self.start_time is None and not self.cancelled def finished(self): return self.end_time is not None and not self.cancelled def live(self): return self.start_time is not None and self.end_time is None and not self.cancelled def get_user(self): return User.query.get(self.user_id) def get_available_server(self): for server in self.serverpool.all(): if not server.in_use: return server return None def __repr__(self): return 'Tournament(id={})'.format(self.id)
d['cvars']['get5_web_api_url'] = url if self.veto_mappool: d['maplist'] = [] for map in self.veto_mappool.split(): d['maplist'].append(map) return d def __repr__(self): return 'Match(id={})'.format(self.id) TournamentTeam = db.Table( 'tournament_team', db.Model.metadata, db.Column('tournament_id', db.Integer, db.ForeignKey('tournament.id')), db.Column('team_id', db.Integer, db.ForeignKey('team.id'))) TournamentGameServer = db.Table( 'tournament_gameserver', db.Model.metadata, db.Column('tournament_id', db.Integer, db.ForeignKey('tournament.id')), db.Column('game_server_id', db.Integer, db.ForeignKey('game_server.id'))) class Tournament(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) winner = db.Column(db.Integer, db.ForeignKey('team.id')) cancelled = db.Column(db.Boolean, default=False) finished = db.Column(db.Boolean, default=False) start_time = db.Column(db.DateTime)