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

Commit 2bd081a

Browse files
authored
Release 1.0
Thanks everyone! Check release tab for more info.
2 parents 9f396ef + 762e917 commit 2bd081a

30 files changed

+276
-4088
lines changed

.gitmodules

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[submodule "sourcemod_plugin/get5-webapi"]
2+
path = sourcemod_plugin/get5-webapi
3+
url = https://github.com/PhlexPlexico/get5-webapi
4+
branch = master

README.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
[![GitHub Downloads](https://img.shields.io/github/downloads/phlexplexico/get5-web/total.svg?label=Downloads)](https://github.com/phlexplexico/get5-web/releases/latest)
66
---
77

8-
**Status: Supported**
9-
8+
**Status: NOT Supported - End of Life Jan. 2020**
109

10+
_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._
1111

1212
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.**
1313

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

2020
## How to use it:
2121

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

2833
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.
2934

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

6065
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
66+
6167
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
68+
6269
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
6370

6471
## Other useful commands:

get5/get5_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def create_test_data(self):
4747
db.session.commit()
4848

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

5353
vetoBan = Veto.create(1, 'EnvyUs', 'de_dust2', 'ban')

get5/match.py

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import steamid
55
import get5
66
from get5 import app, db, BadRequestError, config_setting
7-
from models import User, Team, Match, GameServer, Season, Veto, match_audit, MapStats, PlayerStats
7+
from models import User, Team, Match, GameServer, Season, Veto, match_audit, MapStats, PlayerStats, MatchSpectator
88
from collections import OrderedDict
99
from datetime import datetime
1010
import util
@@ -30,7 +30,7 @@ class MultiCheckboxField(SelectMultipleField):
3030
def different_teams_validator(form, field):
3131
if form.team1_id.data == form.team2_id.data:
3232
raise ValidationError('Teams cannot be equal')
33-
33+
3434

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

139+
min_player_ready = IntegerField('Max # Players Per Team',
140+
default=5,
141+
validators=[validators.required(), validators.NumberRange(1, 10)])
142+
139143
def add_teams(self, user):
140144
if self.team1_id.choices is None:
141145
self.team1_id.choices = []
@@ -180,7 +184,11 @@ def add_seasons(self):
180184
self.season_selection.choices = []
181185
season_tuples = []
182186
season_tuples.append((0, 'No Season'))
183-
for seasons in Season.query.filter((Season.end_date >= datetime.now()) | (Season.end_date.is_(None))).order_by(-Season.id):
187+
if g.user.super_admin or g.user.admin:
188+
ourSeasons = Season.query.filter((Season.end_date >= datetime.now()) | (Season.end_date.is_(None))).order_by(-Season.id)
189+
else:
190+
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)
191+
for seasons in ourSeasons:
184192
season_tuples.append((seasons.id, seasons.name))
185193
self.season_selection.choices += season_tuples
186194

@@ -200,7 +208,7 @@ def match_create():
200208
max_matches = config_setting('USER_MAX_MATCHES')
201209
season_id = None
202210

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

@@ -266,7 +274,8 @@ def match_create():
266274
season_id, form.data['side_type'],
267275
form.data['veto_first'], form.data['server_id'],
268276
team1_series_score, team2_series_score, specList,
269-
form.data['private_match'], form.data['enforce_teams'])
277+
form.data['private_match'], form.data['enforce_teams'],
278+
form.data['min_player_ready'])
270279

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

279288
db.session.commit()
289+
290+
# Implement normalized spectator list.
291+
if specList:
292+
for singleAuth in specList:
293+
MatchSpectator.set_or_create(match.id, auth)
294+
280295
app.logger.info('User {} created match {}, assigned to server {}'
281296
.format(g.user.id, match.id, server.id))
282297

@@ -298,7 +313,7 @@ def match_create():
298313
@match_blueprint.route('/match/<int:matchid>/forfeit/<int:teamwinner>')
299314
def match_forfeit(matchid, teamwinner):
300315
match = Match.query.get_or_404(matchid)
301-
super_admintools_check(g.user, match)
316+
super_admintools_check(match)
302317
if teamwinner == 1:
303318
winnerId = match.team1_id
304319
elif teamwinner == 2:
@@ -346,7 +361,7 @@ def match(matchid):
346361
server = None
347362
team1 = Team.query.get_or_404(match.team1_id)
348363
team2 = Team.query.get_or_404(match.team2_id)
349-
check_private_or_public(g.user, match, team1, team2)
364+
check_private_or_public(match, team1, team2)
350365

351366
map_stat_list = match.map_stats.all()
352367
completed = match.winner
@@ -374,8 +389,8 @@ def match(matchid):
374389
if g.user:
375390
is_match_owner = (g.user.id == match.user_id)
376391
has_admin_access = (config_setting(
377-
'ADMINS_ACCESS_ALL_MATCHES') and util.is_admin(g.user))
378-
has_super_admin_access = util.is_super_admin(g.user)
392+
'ADMINS_ACCESS_ALL_MATCHES') and g.user.admin)
393+
has_super_admin_access = g.user.super_admin
379394
is_server_op = util.is_server_owner(g.user, server)
380395
return render_template(
381396
'match.html', user=g.user, admin_access=has_admin_access,
@@ -398,7 +413,7 @@ def merge(a, b):
398413
match = Match.query.get_or_404(matchid)
399414
team1 = Team.query.get_or_404(match.team1_id)
400415
team2 = Team.query.get_or_404(match.team2_id)
401-
check_private_or_public(g.user, match, team1, team2)
416+
check_private_or_public(match, team1, team2)
402417
map_num = 0
403418
map_stat_list = match.map_stats.all()
404419
player_dict = {}
@@ -448,7 +463,7 @@ def match_config(matchid):
448463
def match_cancel(matchid):
449464
app.logger.info("Match server id is: {}".format(matchid))
450465
match = Match.query.get_or_404(matchid)
451-
admintools_check(g.user, match)
466+
admintools_check(match)
452467

453468
match.cancelled = True
454469
server = GameServer.query.get(match.server_id)
@@ -472,10 +487,11 @@ def match_rcon(matchid):
472487
command = request.values.get('command')
473488
server = GameServer.query.get_or_404(match.server_id)
474489
owns_server = util.is_server_owner(g.user, server)
475-
is_sadmin = util.is_super_admin(g.user)
490+
is_sadmin = g.user.super_admin
476491
# Check to see if user owns server.
477-
if not owns_server or not is_sadmin:
478-
raise BadRequestError('You are not the server owner.')
492+
if not owns_server:
493+
if not is_sadmin:
494+
raise BadRequestError('You are not the server owner.')
479495

480496
if command:
481497
try:
@@ -499,7 +515,7 @@ def match_rcon(matchid):
499515
@match_blueprint.route('/match/<int:matchid>/pause')
500516
def match_pause(matchid):
501517
match = Match.query.get_or_404(matchid)
502-
admintools_check(g.user, match)
518+
admintools_check(match)
503519
server = GameServer.query.get_or_404(match.server_id)
504520

505521
try:
@@ -514,7 +530,7 @@ def match_pause(matchid):
514530
@match_blueprint.route('/match/<int:matchid>/unpause')
515531
def match_unpause(matchid):
516532
match = Match.query.get_or_404(matchid)
517-
admintools_check(g.user, match)
533+
admintools_check(match)
518534
server = GameServer.query.get_or_404(match.server_id)
519535

520536
try:
@@ -529,7 +545,8 @@ def match_unpause(matchid):
529545
@match_blueprint.route('/match/<int:matchid>/adduser')
530546
def match_adduser(matchid):
531547
match = Match.query.get_or_404(matchid)
532-
admintools_check(g.user, match)
548+
app.logger.info("Our user: {}".format(g.user))
549+
admintools_check(match)
533550
server = GameServer.query.get_or_404(match.server_id)
534551
team = request.values.get('team')
535552
if not team:
@@ -542,6 +559,8 @@ def match_adduser(matchid):
542559
command = 'get5_addplayer {} {}'.format(new_auth, team)
543560
response = server.send_rcon_command(command, raise_errors=True)
544561
match_audit.create(g.user.id, matchid, datetime.now(), command)
562+
if (team == "spec"):
563+
MatchSpectator.set_or_create(matchid, new_auth)
545564
db.session.commit()
546565
flash(response)
547566
except util.RconError as e:
@@ -556,7 +575,7 @@ def match_adduser(matchid):
556575
@match_blueprint.route('/match/<int:matchid>/backup', methods=['GET'])
557576
def match_backup(matchid):
558577
match = Match.query.get_or_404(matchid)
559-
admintools_check(g.user, match)
578+
admintools_check(match)
560579
server = GameServer.query.get_or_404(match.server_id)
561580
file = request.values.get('file')
562581

@@ -581,9 +600,9 @@ def match_backup(matchid):
581600
flash('Restored backup file {}'.format(file))
582601
else:
583602
flash('Failed to restore backup file {}'.format(file))
584-
return redirect('match/{}/backup'.format(matchid))
603+
return redirect('/match/{}/backup'.format(matchid))
585604

586-
return redirect('match/{}'.format(matchid))
605+
return redirect('/match/{}'.format(matchid))
587606

588607

589608
@match_blueprint.route("/matches")
@@ -624,6 +643,7 @@ def delete_cancelled_matches():
624643
PlayerStats.query.filter_by(match_id=match.id).delete()
625644
MapStats.query.filter_by(match_id=match.id).delete()
626645
Veto.query.filter_by(match_id=match.id).delete()
646+
MatchSpectator.query.filter_by(match_id=match.id).delete()
627647
matches.delete()
628648
db.session.commit()
629649
return redirect('/matches/' + str(g.user.id))
@@ -662,11 +682,11 @@ def generate():
662682
# Begin Helper Functions
663683

664684

665-
def super_admintools_check(user, match):
666-
if user is None:
685+
def super_admintools_check(match):
686+
if not g.user:
667687
raise BadRequestError('You do not have access to this page')
668688

669-
if not util.is_super_admin(user):
689+
if not g.user.super_admin:
670690
raise BadRequestError('You do not have access to this page')
671691

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

678698

679-
def admintools_check(user, match):
680-
if user is None:
699+
def admintools_check(match):
700+
if not g.user:
681701
raise BadRequestError('You do not have access to this page')
682702

683-
grant_admin_access = util.is_admin(user) and get5.config_setting(
703+
grant_admin_access = (g.user.admin or g.user.super_admin) and get5.config_setting(
684704
'ADMINS_ACCESS_ALL_MATCHES')
685-
if user.id != match.user_id and not grant_admin_access:
705+
if g.user.id != match.user_id and not grant_admin_access:
686706
raise BadRequestError('You do not have access to this page')
687707

688708
if match.finished():
@@ -691,24 +711,24 @@ def admintools_check(user, match):
691711
if match.cancelled:
692712
raise BadRequestError('Match is cancelled')
693713

694-
def check_private_or_public(user, match, team1, team2):
714+
def check_private_or_public(match, team1, team2):
695715
if match.is_private_match():
696-
if not user:
716+
if not g.user:
697717
raise BadRequestError("Please login before viewing this match.")
698718
# Get team lists, and check if logged in user is part of match.
699-
if (user.id == match.user_id) or (config_setting(
700-
'ADMINS_ACCESS_ALL_MATCHES') and util.is_admin(user)) or util.is_super_admin(user):
719+
if (g.user.id == match.user_id) or (config_setting(
720+
'ADMINS_ACCESS_ALL_MATCHES') and g.user.admin) or g.user.super_admin:
701721
isPlayer = False
702722
playerstats_steam = [r.steam_id for r in PlayerStats.query.filter(
703723
PlayerStats.match_id == match.id)]
704724
playerList = list(
705725
set(team1.auths + team2.auths + playerstats_steam))
706726
app.logger.info("Our list: {}".format(playerList))
707-
if (config_setting('ADMINS_ACCESS_ALL_MATCHES') and util.is_admin(user)) or util.is_super_admin(user):
727+
if (config_setting('ADMINS_ACCESS_ALL_MATCHES') and g.user.admin) or g.user.super_admin:
708728
isPlayer = True
709729
else:
710730
for player in playerList:
711-
if user.steam_id == player:
731+
if g.user.steam_id == player:
712732
isPlayer = True
713733
break
714734
if not isPlayer:

0 commit comments

Comments
 (0)