2 many templates

This commit is contained in:
EvilMuffinHa 2020-10-14 11:04:30 -04:00
parent 06b86d706e
commit 00cc143d6e
20 changed files with 542 additions and 123 deletions

2
.gitignore vendored
View File

@ -133,5 +133,5 @@ dmypy.json
# jetbrains folder # jetbrains folder
.idea/ .idea/
qbBuzzer.iml qbBuzzer.iml
.env
test/ test/

View File

@ -2,43 +2,38 @@ from flask import *
from random import randint as rint from random import randint as rint
from src.config import Config from src.config import Config
from src.host import HostForm from src.host import HostForm
from src.sec import gencode, dohash from src.join import JoinForm
from src.sec import gencode, dohash, whitelist
from logging.config import dictConfig from logging.config import dictConfig
import logging from flask_socketio import SocketIO, emit, join_room, leave_room
from flask_socketio import SocketIO
import json import json
dictConfig({ # Loading logging preferences
'version': 1, with open("logger.json", "r") as f:
'formatters': {'default': { dconf = json.load(f)
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
}},
'handlers': {'wsgi': {
'class': 'logging.StreamHandler',
'stream': 'ext://flask.logging.wsgi_errors_stream',
'formatter': 'default'
}},
'root': {
'level': 'INFO',
'handlers': ['wsgi']
}
})
# Establishing logger
dictConfig(dconf)
# Loading flask app
app = Flask(__name__) app = Flask(__name__)
app.logger.info("Flask app loaded at " + __name__) app.logger.info("Flask app loaded at " + __name__)
app.config.from_object(Config) app.config.from_object(Config)
app.logger.setLevel(logging.DEBUG) # Version allows css / js to load instead of taking hours to update even on run smh
version = rint(0, 300000000) version = rint(0, 300000000)
# games
games = {} games = {}
# home
@app.route('/') @app.route('/')
def home(): def home():
return render_template('index.html', version=str(version)) return render_template('index.html', version=str(version))
# creating a room for players to join
@app.route("/host", methods=["GET", "POST"]) @app.route("/host", methods=["GET", "POST"])
def host(): def host():
form = HostForm() form = HostForm()
@ -46,7 +41,7 @@ def host():
hostcode = gencode(64) hostcode = gencode(64)
hash = dohash(hostcode) hash = dohash(hostcode)
resp = redirect(url_for("play", hash=hash)) resp = redirect(url_for("play", hash=hash))
resp.set_cookie("_gid", str(hostcode), httponly=True, secure=True) resp.set_cookie("_gid", str(hostcode))
with open("src/templates/games.json", "r") as f: with open("src/templates/games.json", "r") as f:
tmp = json.load(f) tmp = json.load(f)
games[hash] = tmp games[hash] = tmp
@ -55,7 +50,6 @@ def host():
games[hash]["bonus"] = form.bonus.data games[hash]["bonus"] = form.bonus.data
games[hash]["power"] = form.power.data games[hash]["power"] = form.power.data
games[hash]["negs"] = form.negs.data games[hash]["negs"] = form.negs.data
games[hash]["teams"] = form.teams.data
app.logger.debug("Game host at %s", hostcode) app.logger.debug("Game host at %s", hostcode)
app.logger.info("Game created at %s", hash) app.logger.info("Game created at %s", hash)
return resp return resp
@ -63,32 +57,136 @@ def host():
return render_template('host.html', title="Host Game", version=str(version), form=form, default=default) return render_template('host.html', title="Host Game", version=str(version), form=form, default=default)
# Inside the room itself
@app.route("/play/<hash>") @app.route("/play/<hash>")
def play(hash): def play(hash):
if hash in games.keys(): if hash in games.keys():
return render_template('play.html', version=str(version)) if dohash(request.cookies.get('_gid')) == hash:
return render_template('gamehost.html', version=str(version), gamecode=hash)
else:
if "name" in request.cookies:
name = request.cookies.get("name")
return render_template('play.html', version=str(version), gamecode=hash, username=name)
else:
return render_template('please.html', version=str(version))
else: else:
abort(404) abort(404)
@app.route("/join") @app.route("/kick")
def kick():
return render_template('kick.html', version=str(version))
# When players attempt to join a room
@app.route("/join", methods=["GET", "POST"])
def join(): def join():
return "In progress!" form = JoinForm()
if form.validate_on_submit():
wlist = whitelist()
if not all([a in wlist for a in form.name.data]):
return render_template('badname.html', title='Join Game', version=str(version))
hash = form.roomcode.data
if hash in games.keys():
if form.name.data in games[hash]["players"].keys():
return render_template('nametaken.html', title='Join Game', version=str(version))
games[hash]["players"][form.name.data] = 0
resp = redirect(url_for("play", hash=hash))
resp.set_cookie("_gid", "")
resp.set_cookie("name", form.name.data)
return resp
else:
return render_template('gamenotfound.html', title="Join Game", version=str(version))
return render_template('join.html', title="Join Game", version=str(version), form=form)
# If someone visits somethin stupid
@app.errorhandler(404) @app.errorhandler(404)
def page_not_found(e): def page_not_found(e):
return render_template('404.html'), 404 return render_template('404.html'), 404
# Connecting socketio for all socketio functions below
socketio = SocketIO(app) socketio = SocketIO(app)
# |
# V
@socketio.on('json') @socketio.on('json')
def handle_json(json): def handle_json(json):
print('received message: ' + str(json)) print('received message: ' + str(json))
emit('json', json)
# Checks when a player / host joins the room
@socketio.on('join')
def on_join(data):
room = data['room']
username = ""
if "username" in data.keys():
username = data['username']
else:
gid = data["_gid"]
if dohash(gid) == room:
username = "host"
join_room(str(room))
emit('player_join_event', games[room]["players"], room=room)
# When the host sends data to server
@socketio.on('host')
def host_msg(data):
room = data["room"]
gid = data["_gid"]
if dohash(gid) != room: # Check if the host is really the host
return
msg = data["data"]
if "lock" in msg.keys():
pass # lock buzzers
elif "kick" in msg.keys():
msg["url"] = url_for('kick')
username = msg["kick"]
del games[room]["players"][username]
emit("player_kick_event", msg, room=room)
elif "tossup" in msg.keys():
pass # give player points
elif "bonus" in msg.keys():
pass # give player points
elif "power" in msg.keys():
pass # give player points
elif "negs" in msg.keys():
pass # give player points
elif "amount" in msg.keys():
pass # give player points
# When the player buzzes
@socketio.on('buzz')
def buzz(data):
room = data["room"]
emit("buzz", data, room=room) # Just send it back
# When a player / host leaves
@socketio.on('leave')
def on_leave(data):
print("player leave")
room = data['room']
username = ""
if "username" in data.keys():
username = data['username']
else:
gid = data["_gid"]
if dohash(gid) == room:
username = "host"
del games[room]["players"][username]
leave_room(room)
emit('player_leave_event', {"player": username}, room=room)
# Run the thing lol
if __name__ == "__main__": if __name__ == "__main__":
socketio.run(app) socketio.run(app, host="0.0.0.0", port=25565)
# app.run(host="127.0.0.1", port=25565) # app.run(host="127.0.0.1", port=25565)

View File

@ -1,10 +1,9 @@
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import IntegerField, SubmitField, BooleanField from wtforms import IntegerField, SubmitField
from wtforms.validators import DataRequired from wtforms.validators import DataRequired
class HostForm(FlaskForm): class HostForm(FlaskForm):
tossup = IntegerField('Tossup Points', validators=[DataRequired()]) tossup = IntegerField('Tossup Points', validators=[DataRequired()])
bonus = IntegerField('Bonus Points', validators=[DataRequired()]) bonus = IntegerField('Bonus Points', validators=[DataRequired()])
power = IntegerField('Power Points', validators=[DataRequired()]) power = IntegerField('Power Points', validators=[DataRequired()])
negs = IntegerField('Neg Points [Positive]', validators=[DataRequired()]) negs = IntegerField('Neg Points [Positive]', validators=[DataRequired()])
teams = BooleanField('Teams? ')
create = SubmitField('Create Room') create = SubmitField('Create Room')

View File

@ -1,6 +1,7 @@
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, BooleanField from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired from wtforms.validators import DataRequired
class JoinForm(FlaskForm): class JoinForm(FlaskForm):
roomcode = StringField('Room Code', validators=[DataRequired()]) roomcode = StringField('Room Code', validators=[DataRequired()])
name = StringField('Name', validators=[DataRequired()])
create = SubmitField('Join Room') create = SubmitField('Join Room')

15
src/logger.json Normal file
View File

@ -0,0 +1,15 @@
{
"version": 1,
"formatters": {"default": {
"format": "[%(asctime)s] %(levelname)s in %(module)s: %(message)s"
}},
"handlers": {"wsgi": {
"class": "logging.StreamHandler",
"stream": "ext://flask.logging.wsgi_errors_stream",
"formatter": "default"
}},
"root": {
"level": "DEBUG",
"handlers": ["wsgi"]
}
}

View File

@ -20,6 +20,10 @@ def sha3256(val):
return ho.digest() return ho.digest()
def whitelist():
return list(ascii_letters + digits)
def hexdigest(val): def hexdigest(val):
return hexlify(val).decode('utf-8') return hexlify(val).decode('utf-8')

BIN
src/static/buzz.mp3 Normal file

Binary file not shown.

View File

@ -1,7 +1,74 @@
var socket = io(); var socket = io();
test = function() {
console.log("h"); code = document.getElementById("code").value;
socket.emit('json', {data: 'I\'m connected!'}); name = document.getElementById("name").value;
kickurl = document.getElementById("kick").value;
function removeAllChildren(e) {
var child = e.lastElementChild;
while (child) {
e.removeChild(child);
child = e.lastElementChild;
}
}
socket.on("connect", function() {
// when client first connects to a game
socket.emit("join", {"username": name, "room": code, "_gid": ""});
})
socket.on("player_join_event", function(data) {
div = document.getElementById("sidebar");
removeAllChildren(div);
if (Object.keys(data).length == 0) {
var keepSidebarOpen = document.createElement("input");
keepSidebarOpen.setAttribute("type", "hidden");
div.appendChild(keepSidebarOpen);
return
}
var keys = Object.keys(data);
for (var key in keys) {
var playerDisplay = document.createElement("div");
playerDisplay.setAttribute("id", keys[key]);
playerDisplay.setAttribute("class", "playertag");
var playerLink = document.createElement("p");
playerLink.appendChild(document.createTextNode(keys[key]));
playerDisplay.appendChild(playerLink);
playerDisplay.appendChild(document.createElement("br"));
div.appendChild(playerDisplay);
}
});
socket.on("player_leave_event", function(data) {
var element = document.getElementById(data["player"]);
element.parentNode.removeChild(element);
});
socket.on("player_kick_event", function(data) {
if (data["kick"] == name) {
window.location = kickurl;
} else {
var element = document.getElementById(data["kick"]);
element.parentNode.removeChild(element);
}
})
function buzz() {
// when client hits buzz
socket.emit("buzz", {"username": name, "room": code, "_gid": ""});
}
$(document).on('keypress', function(e) {
if (e.key === ' ' || e.key === 'Spacebar') {
e.preventDefault();
buzz();
}
})
window.onbeforeunload = function leave() {
// when client leaves
console.log("leave")
socket.emit("leave", {"username": name, "room": code, "_gid": ""});
} }
// div id = game (that's where the game goes)

90
src/static/host.js Normal file
View File

@ -0,0 +1,90 @@
var socket = io();
code = document.getElementById("copycode").value;
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
gid = readCookie("_gid");
copycode = function() {
var copyText = document.getElementById("copycode")
copyText.select();
copyText.setSelectionRange(0, 99999);
document.execCommand("copy")
}
function send(data) {
socket.emit("host", {"room": code, "_gid": gid, "data": data});
}
function removeAllChildren(e) {
var child = e.lastElementChild;
while (child) {
e.removeChild(child);
child = e.lastElementChild;
}
}
socket.on("connect", function() {
socket.emit("join", {"room": code, "_gid": gid});
});
window.onbeforeunload = function leave() {
socket.emit("leave", {"room": code, "_gid": gid});
}
socket.on("player_join_event", function(data) {
div = document.getElementById("sidebar");
removeAllChildren(div);
if (Object.keys(data).length == 0) {
var keepSidebarOpen = document.createElement("input");
keepSidebarOpen.setAttribute("type", "hidden");
div.appendChild(keepSidebarOpen);
return
}
var keys = Object.keys(data);
for (var key in keys) {
var playerDisplay = document.createElement("div");
playerDisplay.setAttribute("id", keys[key]);
playerDisplay.setAttribute("class", "playertag");
var playerLink = document.createElement("p");
playerLink.appendChild(document.createTextNode(keys[key]));
var removeLink = document.createElement("a");
removeLink.setAttribute("onclick", "removePlayer('" + keys[key] + "');");
removeLink.appendChild(document.createTextNode("Kick"));
playerDisplay.appendChild(playerLink);
playerDisplay.appendChild(removeLink);
playerDisplay.appendChild(document.createElement("br"));
div.appendChild(playerDisplay);
}
});
socket.on("player_leave_event", function(data) {
var element = document.getElementById(data["player"]);
element.parentNode.removeChild(element);
});
socket.on("player_kick_event", function(data) {
var element = document.getElementById(data["kick"]);
element.parentNode.removeChild(element);
})
function removePlayer(playername) {
send({"kick": playername});
}
socket.on("buzz", function(message) {
var audio = new Audio('/static/buzz.mp3');
audio.play();
})

View File

@ -92,6 +92,63 @@ body {
margin-left: 5px; margin-left: 5px;
} }
.copycode {
text-align: center;
}
.form {
text-align: center;
}
.form input {
text-align: center;
}
.sidebar {
height: 100%; /* Full-height: remove this if you want "auto" height */
font-size: 25px;
position: fixed; /* Fixed Sidebar (stay in place on scroll) */
z-index: 1; /* Stay on top */
top: 0; /* Stay at the top */
left: 0;
background-color: #e2e27c;
padding-top: 20px;
padding-left: 10px;
padding-right: 20px;
}
/* The navigation menu links */
.sidebar a {
padding: 6px 8px 6px 8px;
text-decoration: none;
font-size: 14px;
color: #ea2a2d;
display: inline;
}
.sidebar p {
padding: 6px 8px 6px 6px;
text-decoration: none;
font-size: 25px;
color: #818181;
display: inline;
}
/* When you mouse over the navigation links, change their color */
.sidebar a:hover {
color: #f1f1f1;
}
/* Style page content */
.game {
padding: 0px 10px;
}
/* On smaller screens, where height is less than 450px, change the style of the sidebar (less padding and a smaller font size) */
@media screen and (max-height: 450px) {
.sidebar {padding-top: 15px;}
.sidebar a {font-size: 18px;}
}
@font-face { @font-face {
font-family: 'Abberancy-Regular'; font-family: 'Abberancy-Regular';

View File

@ -0,0 +1,14 @@
{% extends "base.html" %}
{% block content %}
<div class="button top">
<a href="{{ url_for('join') }}">Back</a>
</div>
<div class="title">
<h1> Bad Name </h1>
</div>
<div class="text">
<center>
<p>Names can only be alphanumeric. </p>
</center>
</div>
{% endblock %}

View File

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block content %}
<div class="subttl">
<h1> Game! </h1>
</div>
<div class="sidebar" id="sidebar">
</div>
<div id="join">
<center>
<p>Click to copy the game code: </p>
<input id="copycode" class="copycode" name="copycode" value="{{ gamecode }}" type="text" size="32" readonly onclick="copycode();"/>
</center>
</div>
<div id="game">
</div>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js" integrity="sha256-yr4fRk/GU1ehYJPAs8P4JlTgu0Hdsp4ZKrx8bDEDC3I=" crossorigin="anonymous"></script>
<script src="/static/host.js?version={{ version }}" type="text/javascript" charset="utf-8"></script>
{% endblock %}

View File

@ -0,0 +1,9 @@
{% extends "base.html" %}
{% block content %}
<div class="button top">
<a href="{{ url_for('join') }}">Back</a>
</div>
<div class="title">
<h1> Game not found </h1>
</div>
{% endblock %}

View File

@ -4,5 +4,6 @@
"bonus": 20, "bonus": 20,
"power": 15, "power": 15,
"negs": 5, "negs": 5,
"teams": false "teams": false,
"players": {}
} }

View File

@ -7,10 +7,11 @@
<div class="subttl"> <div class="subttl">
<h1> Host Game </h1> <h1> Host Game </h1>
</div> </div>
<div class="form host">
<form action="" method="post" novalidate> <form action="" method="post" novalidate>
{{ form.csrf_token }} {{ form.csrf_token }}
{{ form.hidden_tag() }}
<p> <p>
<center>
{{ form.tossup.label }}<br> {{ form.tossup.label }}<br>
{{ form.tossup(size=32, value=default[0]) }}<br> {{ form.tossup(size=32, value=default[0]) }}<br>
{% for error in form.tossup.errors %} {% for error in form.tossup.errors %}
@ -35,13 +36,10 @@
<span style="color: red;">[{{ error }}]</span><br> <span style="color: red;">[{{ error }}]</span><br>
{% endfor %} {% endfor %}
<br> <br>
{{ form.teams() }} {{ form.teams.label }}<br>
{% for error in form.negs.errors %}
<span style="color: red;">[{{ error }}]</span><br>
{% endfor %}
<br>
{{ form.create() }} {{ form.create() }}
<br> <br>
</center>
</p> </p>
</form> </form>
</div>
{% endblock %} {% endblock %}

View File

@ -1,47 +1,30 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<div class="button top"> <div class="button top">
<a href="{{ url_for('home') }}">Back</a> <a href="{{ url_for('home') }}">Back</a>
</div> </div>
<div class="subttl"> <div class="subttl">
<h1> Host Game </h1> <h1> Join Game </h1>
</div> </div>
<form action="" method="post" novalidate> <div class="form join">
<form action="" method="post" novalidate>
{{ form.csrf_token }} {{ form.csrf_token }}
{{ form.hidden_tag() }} <p><center>
<p> {{ form.roomcode.label }}<br>
{{ form.tossup.label }}<br> {{ form.roomcode(size=32) }}<br>
{{ form.tossup(size=32, value=default[0]) }}<br> {% for error in form.roomcode.errors %}
{% for error in form.tossup.errors %}
<span style="color: red;">[{{ error }}]</span><br> <span style="color: red;">[{{ error }}]</span><br>
{% endfor %} {% endfor %}
<br> <br>
{{ form.bonus.label }}<br> {{ form.name.label }}<br>
{{ form.bonus(size=32, value=default[1]) }}<br> {{ form.name(size=32) }}<br>
{% for error in form.bonus.errors %} {% for error in form.name.errors %}
<span style="color: red;">[{{ error }}]</span><br>
{% endfor %}
<br>
{{ form.power.label }}<br>
{{ form.power(size=32, value=default[2]) }}<br>
{% for error in form.power.errors %}
<span style="color: red;">[{{ error }}]</span><br>
{% endfor %}
<br>
{{ form.negs.label }}<br>
{{ form.negs(size=32, value=default[3]) }}<br>
{% for error in form.negs.errors %}
<span style="color: red;">[{{ error }}]</span><br>
{% endfor %}
<br>
{{ form.teams() }} {{ form.teams.label }}<br>
{% for error in form.negs.errors %}
<span style="color: red;">[{{ error }}]</span><br> <span style="color: red;">[{{ error }}]</span><br>
{% endfor %} {% endfor %}
<br> <br>
{{ form.create() }} {{ form.create() }}
<br> <br> </center></p>
</p> </form>
</form> </div>
{% endblock %} {% endblock %}

10
src/templates/kick.html Normal file
View File

@ -0,0 +1,10 @@
{% extends "base.html" %}
{% block content %}
<div class="button top">
<a href="{{ url_for('join') }}">Back</a>
</div>
<div class="subttl">
<p>You have been kicked. </p>
</div>
{% endblock %}

View File

@ -0,0 +1,10 @@
{% extends "base.html" %}
{% block content %}
<div class="button top">
<a href="{{ url_for('join') }}">Back</a>
</div>
<div class="subttl">
<p>Name is already taken.</p>
</div>
{% endblock %}

View File

@ -1,9 +1,42 @@
{% extends "base.html" %} <!DOCTYPE html>
<html lang="en">
{% if title %}
<head>
<meta charset="UTF-8">
<link rel="shortcut icon" type="image/x-icon" href="/static/favicon.ico" />
<link rel="stylesheet" href="/static/index.css?version={{ version }}">
<title>{{ title }} - Buzzer! </title>
</head>
{% else %}
<head>
<title> Buzzer! </title>
<link rel="shortcut icon" type="image/x-icon" href="/static/favicon.ico" />
<link rel="stylesheet" href="/static/index.css?version={{ version }}">
</head>
{% endif %}
<body class="wrap">
<div class="subttl">
<h1> Play! </h1>
</div>
{% block content %} <br><input type="hidden" id="code" name="code" value="{{ gamecode }}"><br>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js" integrity="sha256-yr4fRk/GU1ehYJPAs8P4JlTgu0Hdsp4ZKrx8bDEDC3I=" crossorigin="anonymous"></script> <br><input type="hidden" id="name" name="name" value="{{ username }}">
<script src="/static/game.js" type="text/javascript" charset="utf-8"></script> <br><input type="hidden" id="kick" name="kick" value="{{ url_for('kick') }}">
<div class="sidebar" id="sidebar">
</div>
<div id="game"> <div id="game">
<center>
<div class="form name">
</div> </div>
{% endblock %} <div class="button buzzer">
<a onclick="buzz();">BUZZ! [SPACE]</a>
</div>
</center>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js" integrity="sha256-yr4fRk/GU1ehYJPAs8P4JlTgu0Hdsp4ZKrx8bDEDC3I=" crossorigin="anonymous"></script>
<script src="/static/game.js?version={{ version }}" type="text/javascript" charset="utf-8"></script>
</body>
</html>

View File

@ -0,0 +1,9 @@
{% extends "base.html" %}
{% block content %}
<div class="button top">
<a href="{{ url_for('join') }}">Back</a>
</div>
<div class="title">
<h1> Please join using the Join page. </h1>
</div>
{% endblock %}