2019-07-28 13:32:28 +02:00
#!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} '
2019-08-07 20:29:46 +02:00
GAME_EXEC_ARG_BOT_DIFFICULTY = ' --bot-difficulty= {0} '
2019-07-28 13:32:28 +02:00
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 (
2019-08-07 20:29:46 +02:00
' 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) ' )
2019-07-28 13:32:28 +02:00
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
2019-08-07 20:29:46 +02:00
bot_difficulty = - 1
2019-07-28 13:32:28 +02:00
name = request . json . get ( ' name ' )
if ' bots ' in request . json :
bots = request . json . get ( ' bots ' )
2019-08-07 20:29:46 +02:00
if ' bot-difficulty ' in request . json :
bot_difficulty = request . json . get ( ' bot-difficulty ' )
2019-07-28 13:32:28 +02:00
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 ]
2019-08-07 20:29:46 +02:00
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 ) )
2019-07-28 13:32:28 +02:00
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 ) ) ,
2019-08-07 20:29:46 +02:00
GAME_EXEC_ARG_BOT_DIFFICULTY . format (
float ( bot_difficulty ) ) ,
2019-07-28 13:32:28 +02:00
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 )