Skip to content
This repository has been archived by the owner on Oct 19, 2021. It is now read-only.

Commit

Permalink
Release 1.0
Browse files Browse the repository at this point in the history
Thanks everyone! Check release tab for more info.
  • Loading branch information
PhlexPlexico authored Dec 15, 2019
2 parents 9f396ef + 762e917 commit 2bd081a
Show file tree
Hide file tree
Showing 30 changed files with 276 additions and 4,088 deletions.
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[submodule "sourcemod_plugin/get5-webapi"]
path = sourcemod_plugin/get5-webapi
url = https://github.com/PhlexPlexico/get5-webapi
branch = master
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
[![GitHub Downloads](https://img.shields.io/github/downloads/phlexplexico/get5-web/total.svg?label=Downloads)](https://github.com/phlexplexico/get5-web/releases/latest)
---

**Status: Supported**

**Status: NOT Supported - End of Life Jan. 2020**

_As of January 1st, 2020, this application will no longer be supported with any updates. Issues will be created to help assist setup and point out any bugs. If someone would like to take over, please make a fork and make some PRs to fix any issues. If you would even like, merge them back here with a link to your fork in the README. Thank you everyone for finding this web panel, and I hope you still find use for this old technology. Some new development is coming down the pipe elsewhere, which I hope will help other developers make their own implementations of get5-web._

This is a web panel meant to be used in conjunction with the [get5](https://github.com/splewis/get5) CS:GO server plugin. It provides a more convenient way of managing matches and match servers. **This webpanel is intended for competitive 5v5 leagues and scrims, and nothing more.**

Expand All @@ -19,11 +19,16 @@ _IF YOU HAVE ANY ISSUES WITH THE WEBPANEL OR THE API_STATS PLUGIN, **PLEASE REPO

## How to use it:

1a. Download the new get5_apistats.smx from the [releases](https://github.com/PhlexPlexico/get5-web/releases) page.
1b. Create your game servers on the "Add a server" page by giving their ip, port, and rcon password
2. Create teams on the "Create a Team" page by listing the steamids for each of the players
3. Go to the "Create a Match" page, selecting the teams, server, and rules for the match
4. Optional - Create a season with a given date range to keep track for a subset of matches.
1. Download the new get5_apistats.smx from the [releases](https://github.com/PhlexPlexico/get5-web/releases) page.

2. Create your game servers on the "Add a server" page by giving their ip, port, and rcon password

3. Create teams on the "Create a Team" page by listing the steamids for each of the players

4. Go to the "Create a Match" page, selecting the teams, server, and rules for the match

5. Optional - Create a season with a given date range to keep track for a subset of matches.


Once you do this, the site will send an rcon command to the game server `get5_loadmatch_url <webserver>/match/<matchid>/config`, which will load the match config onto the gameserver automatically for you. Stats and game status will automatically be updated on the webpage.

Expand Down Expand Up @@ -58,7 +63,9 @@ Please see the [installation instructions](https://github.com/PhlexPlexico/get5-
## How do the game server and web panel communicate?

1. When a server is added the web server will send `get5_web_avaliable` command through rcon that will check for the appropriate get5 plugins to be installed on the server

2. When a match is assigned to a server, the `get5_loadmatch_url` command is used through rcon to tell the websever a file to download the get5 match config from

3. When stats begin to update (map start, round end, map end, series end), the game server plugins will send HTTP requests to the web server, using a per-match API token set in the `get5_web_api_key` cvar when the match was assigned to the server

## Other useful commands:
Expand Down
2 changes: 1 addition & 1 deletion get5/get5_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def create_test_data(self):
db.session.commit()

Match.create(user, team1.id, team2.id, '', '', 1, False,
'Map {MAPNUMBER}', ['de_dust2', 'de_cache', 'de_mirage'], season.id, 'always_knife', 'CT', server.id, 0, 0, None, False, False)
'Map {MAPNUMBER}', ['de_dust2', 'de_cache', 'de_mirage'], season.id, 'always_knife', 'CT', server.id, 0, 0, None, False, False, 5)
db.session.commit()

vetoBan = Veto.create(1, 'EnvyUs', 'de_dust2', 'ban')
Expand Down
86 changes: 53 additions & 33 deletions get5/match.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import steamid
import get5
from get5 import app, db, BadRequestError, config_setting
from models import User, Team, Match, GameServer, Season, Veto, match_audit, MapStats, PlayerStats
from models import User, Team, Match, GameServer, Season, Veto, match_audit, MapStats, PlayerStats, MatchSpectator
from collections import OrderedDict
from datetime import datetime
import util
Expand All @@ -30,7 +30,7 @@ class MultiCheckboxField(SelectMultipleField):
def different_teams_validator(form, field):
if form.team1_id.data == form.team2_id.data:
raise ValidationError('Teams cannot be equal')


def mappool_validator(form, field):
if 'preset' in form.series_type.data and len(form.veto_mappool.data) != 1:
Expand Down Expand Up @@ -136,6 +136,10 @@ class MatchForm(Form):
enforce_teams = BooleanField('Enforce Auths on Team',
default=True)

min_player_ready = IntegerField('Max # Players Per Team',
default=5,
validators=[validators.required(), validators.NumberRange(1, 10)])

def add_teams(self, user):
if self.team1_id.choices is None:
self.team1_id.choices = []
Expand Down Expand Up @@ -180,7 +184,11 @@ def add_seasons(self):
self.season_selection.choices = []
season_tuples = []
season_tuples.append((0, 'No Season'))
for seasons in Season.query.filter((Season.end_date >= datetime.now()) | (Season.end_date.is_(None))).order_by(-Season.id):
if g.user.super_admin or g.user.admin:
ourSeasons = Season.query.filter((Season.end_date >= datetime.now()) | (Season.end_date.is_(None))).order_by(-Season.id)
else:
ourSeasons = Season.query.filter((Season.end_date >= datetime.now()) | (Season.end_date.is_(None))).filter(Season.user_id == g.user.id).order_by(-Season.id)
for seasons in ourSeasons:
season_tuples.append((seasons.id, seasons.name))
self.season_selection.choices += season_tuples

Expand All @@ -200,7 +208,7 @@ def match_create():
max_matches = config_setting('USER_MAX_MATCHES')
season_id = None

if max_matches >= 0 and num_matches >= max_matches and not (util.is_admin(g.user) or util.is_super_admin(g.user)):
if max_matches >= 0 and num_matches >= max_matches and not (g.user.admin or g.user.super_admin):
flash('You already have the maximum number of matches ({}) created'.format(
num_matches))

Expand Down Expand Up @@ -266,7 +274,8 @@ def match_create():
season_id, form.data['side_type'],
form.data['veto_first'], form.data['server_id'],
team1_series_score, team2_series_score, specList,
form.data['private_match'], form.data['enforce_teams'])
form.data['private_match'], form.data['enforce_teams'],
form.data['min_player_ready'])

# Save plugin version data if we have it
if json_reply and 'plugin_version' in json_reply:
Expand All @@ -277,6 +286,12 @@ def match_create():
server.in_use = True

db.session.commit()

# Implement normalized spectator list.
if specList:
for singleAuth in specList:
MatchSpectator.set_or_create(match.id, auth)

app.logger.info('User {} created match {}, assigned to server {}'
.format(g.user.id, match.id, server.id))

Expand All @@ -298,7 +313,7 @@ def match_create():
@match_blueprint.route('/match/<int:matchid>/forfeit/<int:teamwinner>')
def match_forfeit(matchid, teamwinner):
match = Match.query.get_or_404(matchid)
super_admintools_check(g.user, match)
super_admintools_check(match)
if teamwinner == 1:
winnerId = match.team1_id
elif teamwinner == 2:
Expand Down Expand Up @@ -346,7 +361,7 @@ def match(matchid):
server = None
team1 = Team.query.get_or_404(match.team1_id)
team2 = Team.query.get_or_404(match.team2_id)
check_private_or_public(g.user, match, team1, team2)
check_private_or_public(match, team1, team2)

map_stat_list = match.map_stats.all()
completed = match.winner
Expand Down Expand Up @@ -374,8 +389,8 @@ def match(matchid):
if g.user:
is_match_owner = (g.user.id == match.user_id)
has_admin_access = (config_setting(
'ADMINS_ACCESS_ALL_MATCHES') and util.is_admin(g.user))
has_super_admin_access = util.is_super_admin(g.user)
'ADMINS_ACCESS_ALL_MATCHES') and g.user.admin)
has_super_admin_access = g.user.super_admin
is_server_op = util.is_server_owner(g.user, server)
return render_template(
'match.html', user=g.user, admin_access=has_admin_access,
Expand All @@ -398,7 +413,7 @@ def merge(a, b):
match = Match.query.get_or_404(matchid)
team1 = Team.query.get_or_404(match.team1_id)
team2 = Team.query.get_or_404(match.team2_id)
check_private_or_public(g.user, match, team1, team2)
check_private_or_public(match, team1, team2)
map_num = 0
map_stat_list = match.map_stats.all()
player_dict = {}
Expand Down Expand Up @@ -448,7 +463,7 @@ def match_config(matchid):
def match_cancel(matchid):
app.logger.info("Match server id is: {}".format(matchid))
match = Match.query.get_or_404(matchid)
admintools_check(g.user, match)
admintools_check(match)

match.cancelled = True
server = GameServer.query.get(match.server_id)
Expand All @@ -472,10 +487,11 @@ def match_rcon(matchid):
command = request.values.get('command')
server = GameServer.query.get_or_404(match.server_id)
owns_server = util.is_server_owner(g.user, server)
is_sadmin = util.is_super_admin(g.user)
is_sadmin = g.user.super_admin
# Check to see if user owns server.
if not owns_server or not is_sadmin:
raise BadRequestError('You are not the server owner.')
if not owns_server:
if not is_sadmin:
raise BadRequestError('You are not the server owner.')

if command:
try:
Expand All @@ -499,7 +515,7 @@ def match_rcon(matchid):
@match_blueprint.route('/match/<int:matchid>/pause')
def match_pause(matchid):
match = Match.query.get_or_404(matchid)
admintools_check(g.user, match)
admintools_check(match)
server = GameServer.query.get_or_404(match.server_id)

try:
Expand All @@ -514,7 +530,7 @@ def match_pause(matchid):
@match_blueprint.route('/match/<int:matchid>/unpause')
def match_unpause(matchid):
match = Match.query.get_or_404(matchid)
admintools_check(g.user, match)
admintools_check(match)
server = GameServer.query.get_or_404(match.server_id)

try:
Expand All @@ -529,7 +545,8 @@ def match_unpause(matchid):
@match_blueprint.route('/match/<int:matchid>/adduser')
def match_adduser(matchid):
match = Match.query.get_or_404(matchid)
admintools_check(g.user, match)
app.logger.info("Our user: {}".format(g.user))
admintools_check(match)
server = GameServer.query.get_or_404(match.server_id)
team = request.values.get('team')
if not team:
Expand All @@ -542,6 +559,8 @@ def match_adduser(matchid):
command = 'get5_addplayer {} {}'.format(new_auth, team)
response = server.send_rcon_command(command, raise_errors=True)
match_audit.create(g.user.id, matchid, datetime.now(), command)
if (team == "spec"):
MatchSpectator.set_or_create(matchid, new_auth)
db.session.commit()
flash(response)
except util.RconError as e:
Expand All @@ -556,7 +575,7 @@ def match_adduser(matchid):
@match_blueprint.route('/match/<int:matchid>/backup', methods=['GET'])
def match_backup(matchid):
match = Match.query.get_or_404(matchid)
admintools_check(g.user, match)
admintools_check(match)
server = GameServer.query.get_or_404(match.server_id)
file = request.values.get('file')

Expand All @@ -581,9 +600,9 @@ def match_backup(matchid):
flash('Restored backup file {}'.format(file))
else:
flash('Failed to restore backup file {}'.format(file))
return redirect('match/{}/backup'.format(matchid))
return redirect('/match/{}/backup'.format(matchid))

return redirect('match/{}'.format(matchid))
return redirect('/match/{}'.format(matchid))


@match_blueprint.route("/matches")
Expand Down Expand Up @@ -624,6 +643,7 @@ def delete_cancelled_matches():
PlayerStats.query.filter_by(match_id=match.id).delete()
MapStats.query.filter_by(match_id=match.id).delete()
Veto.query.filter_by(match_id=match.id).delete()
MatchSpectator.query.filter_by(match_id=match.id).delete()
matches.delete()
db.session.commit()
return redirect('/matches/' + str(g.user.id))
Expand Down Expand Up @@ -662,11 +682,11 @@ def generate():
# Begin Helper Functions


def super_admintools_check(user, match):
if user is None:
def super_admintools_check(match):
if not g.user:
raise BadRequestError('You do not have access to this page')

if not util.is_super_admin(user):
if not g.user.super_admin:
raise BadRequestError('You do not have access to this page')

if match.finished():
Expand All @@ -676,13 +696,13 @@ def super_admintools_check(user, match):
raise BadRequestError('Match is cancelled')


def admintools_check(user, match):
if user is None:
def admintools_check(match):
if not g.user:
raise BadRequestError('You do not have access to this page')

grant_admin_access = util.is_admin(user) and get5.config_setting(
grant_admin_access = (g.user.admin or g.user.super_admin) and get5.config_setting(
'ADMINS_ACCESS_ALL_MATCHES')
if user.id != match.user_id and not grant_admin_access:
if g.user.id != match.user_id and not grant_admin_access:
raise BadRequestError('You do not have access to this page')

if match.finished():
Expand All @@ -691,24 +711,24 @@ def admintools_check(user, match):
if match.cancelled:
raise BadRequestError('Match is cancelled')

def check_private_or_public(user, match, team1, team2):
def check_private_or_public(match, team1, team2):
if match.is_private_match():
if not user:
if not g.user:
raise BadRequestError("Please login before viewing this match.")
# Get team lists, and check if logged in user is part of match.
if (user.id == match.user_id) or (config_setting(
'ADMINS_ACCESS_ALL_MATCHES') and util.is_admin(user)) or util.is_super_admin(user):
if (g.user.id == match.user_id) or (config_setting(
'ADMINS_ACCESS_ALL_MATCHES') and g.user.admin) or g.user.super_admin:
isPlayer = False
playerstats_steam = [r.steam_id for r in PlayerStats.query.filter(
PlayerStats.match_id == match.id)]
playerList = list(
set(team1.auths + team2.auths + playerstats_steam))
app.logger.info("Our list: {}".format(playerList))
if (config_setting('ADMINS_ACCESS_ALL_MATCHES') and util.is_admin(user)) or util.is_super_admin(user):
if (config_setting('ADMINS_ACCESS_ALL_MATCHES') and g.user.admin) or g.user.super_admin:
isPlayer = True
else:
for player in playerList:
if user.steam_id == player:
if g.user.steam_id == player:
isPlayer = True
break
if not isPlayer:
Expand Down
Loading

0 comments on commit 2bd081a

Please sign in to comment.