200 lines
6.6 KiB
Python
200 lines
6.6 KiB
Python
|
#!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_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)')
|
||
|
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
|
||
|
name = request.json.get('name')
|
||
|
if 'bots' in request.json:
|
||
|
bots = request.json.get('bots')
|
||
|
|
||
|
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,player_count,running) VALUES (?,?,?,?,?,0,0)",
|
||
|
(name, secret, remote_addr, port, bots))
|
||
|
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_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)
|