|
|
#!flask/bin/python import subprocess import sqlite3 import secrets from flask import Flask, abort, request, jsonify
START_PORT = 8128 MAX_GAMES = 10 GAME_EXEC = 'godot_server' GAME_EXEC_ARG_PACK = '--main-pack' GAME_EXEC_ARG_PACK_PATH = '/opt/godot/MUR.pck' GAME_EXEC_ARG_PORT = '--port={0}' GAME_EXEC_ARG_ID = '--server-id={0}' GAME_EXEC_ARG_SECRET = '--secret={0}' GAME_EXEC_ARG_BOTS = '--bots={0}' GAME_EXEC_ARG_BOT_DIFFICULTY = '--bot-difficulty={0}' GAME_EXEC_ARG_SERVER_ADDR = '--server-addr={0}' GAME_EXEC_ARG_API_ADDR = '--api-addr={0}'
games = {}
try: with sqlite3.connect("database.db") as con: cur = con.cursor() cur.execute( 'CREATE TABLE IF NOT EXISTS games (id INTEGER PRIMARY KEY, name TEXT, secret TEXT, ip TEXT, running INTEGER, port INTEGER, player_count INTEGER, bots INTEGER, bot_difficulty REAL)') con.commit() except: con.rollback() finally: con.close()
app = Flask(__name__)
@app.route('/client/game/create', methods=['POST']) def create_game(): # TODO: block multiple request from same user if not request.json: abort(400) if not 'name' in request.json: abort(400)
bots = False bot_difficulty = -1 name = request.json.get('name') if 'bots' in request.json: bots = request.json.get('bots')
if 'bot-difficulty' in request.json: bot_difficulty = request.json.get('bot-difficulty')
game_count = 0 try: with sqlite3.connect("database.db") as con: # check free port port = START_PORT cur = con.cursor() cur.execute("SELECT * FROM games WHERE port=?", (port,)) rows = cur.fetchall() while rows: port += 1 game_count += 1 cur.execute("SELECT * FROM games WHERE port=?", (port,)) rows = cur.fetchall()
if game_count >= MAX_GAMES: return
# check duplicate name base_name = name base_name_count = 1 cur.execute("SELECT * FROM games WHERE name=?", (name,)) rows = cur.fetchall() while rows: base_name_count += 1 name = base_name + "_" + str(base_name_count) cur.execute("SELECT * FROM games WHERE name=?", (name,)) rows = cur.fetchall()
# gen secret secret = secrets.token_hex(32) remote_addr = request.remote_addr if 'X-Forwarded-For' in request.headers: remote_addr = request.headers.getlist( "X-Forwarded-For")[0].rpartition(' ')[-1]
cur.execute("INSERT INTO games (name,secret,ip,port,bots,bot-difficulty,player_count,running) VALUES (?,?,?,?,?,0,0)", (name, secret, remote_addr, port, bots, bot_difficulty)) con.commit() cur.execute("SELECT id FROM games WHERE secret=?", (secret,)) result = cur.fetchone() if result[0]: games[result[0]] = subprocess.Popen([GAME_EXEC, GAME_EXEC_ARG_PACK, GAME_EXEC_ARG_PACK_PATH, GAME_EXEC_ARG_ID.format( int(result[0])), GAME_EXEC_ARG_PORT.format( port), GAME_EXEC_ARG_SECRET.format( secret), GAME_EXEC_ARG_BOTS.format( int(bots)), GAME_EXEC_ARG_BOT_DIFFICULTY.format( float(bot_difficulty)), GAME_EXEC_ARG_SERVER_ADDR.format( '127.0.0.1'), # localhost GAME_EXEC_ARG_API_ADDR.format('http://127.0.0.1:5000/')]) except: con.rollback() abort(500) finally: con.close() if game_count >= MAX_GAMES: abort(403) return jsonify({'name': name, 'port': port})
@app.route('/client/games', methods=['GET']) def get_games(): try: with sqlite3.connect("database.db") as con: cur = con.cursor() query = "SELECT * FROM games ORDER BY running" if 'open' in request.args: query = "SELECT * FROM games WHERE running=0" cur.execute(query) rows = cur.fetchall() except: con.rollback() finally: con.close() result = [] for row in rows: result.append( {'name': row[1], 'player_count': row[6], 'running': (row[4] == 1), 'port': row[5], 'bots': row[7]}) return jsonify(result)
@app.route('/game', methods=['PUT']) def update_game(): if not request.json: abort(400) if not 'secret' in request.json or not 'player_count' in request.json or not 'running' in request.json: abort(400)
secret = request.json.get('secret') player_count = request.json.get('player_count') running = request.json.get('running') try: with sqlite3.connect("database.db") as con: cur = con.cursor() cur.execute("SELECT id FROM games WHERE secret=?", (secret,)) result = cur.fetchone() if result[0]: game_id = result[0] cur.execute("UPDATE games SET player_count=?,running=? WHERE id=?", (player_count, running, game_id,)) con.commit() else: abort(401) except: con.rollback() return jsonify(False) finally: con.close() return jsonify(True)
@app.route('/game', methods=['DELETE']) def close_game(): if not request.json: abort(400) if not 'secret' in request.json: abort(400)
secret = request.json.get('secret') try: with sqlite3.connect("database.db") as con: cur = con.cursor() cur.execute("SELECT id FROM games WHERE secret=?", (secret,)) result = cur.fetchone() if result[0]: game_id = result[0] cur.execute("DELETE FROM games WHERE id=?", (game_id,)) con.commit() if games[game_id]: games[game_id].terminate() games[game_id].communicate() del games[game_id] else: abort(401) except: con.rollback() return jsonify(False) finally: con.close() return jsonify(True)
if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=5000)
|