Public repository for MUR pre alpha
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

199 lines
6.6 KiB

  1. #!flask/bin/python
  2. import subprocess
  3. import sqlite3
  4. import secrets
  5. from flask import Flask, abort, request, jsonify
  6. START_PORT = 8128
  7. MAX_GAMES = 10
  8. GAME_EXEC = 'godot_server'
  9. GAME_EXEC_ARG_PACK = '--main-pack'
  10. GAME_EXEC_ARG_PACK_PATH = '/opt/godot/MUR.pck'
  11. GAME_EXEC_ARG_PORT = '--port={0}'
  12. GAME_EXEC_ARG_ID = '--server-id={0}'
  13. GAME_EXEC_ARG_SECRET = '--secret={0}'
  14. GAME_EXEC_ARG_BOTS = '--bots={0}'
  15. GAME_EXEC_ARG_SERVER_ADDR = '--server-addr={0}'
  16. GAME_EXEC_ARG_API_ADDR = '--api-addr={0}'
  17. games = {}
  18. try:
  19. with sqlite3.connect("database.db") as con:
  20. cur = con.cursor()
  21. cur.execute(
  22. '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)')
  23. con.commit()
  24. except:
  25. con.rollback()
  26. finally:
  27. con.close()
  28. app = Flask(__name__)
  29. @app.route('/client/game/create', methods=['POST'])
  30. def create_game():
  31. # TODO: block multiple request from same user
  32. if not request.json:
  33. abort(400)
  34. if not 'name' in request.json:
  35. abort(400)
  36. bots = False
  37. name = request.json.get('name')
  38. if 'bots' in request.json:
  39. bots = request.json.get('bots')
  40. game_count = 0
  41. try:
  42. with sqlite3.connect("database.db") as con:
  43. # check free port
  44. port = START_PORT
  45. cur = con.cursor()
  46. cur.execute("SELECT * FROM games WHERE port=?", (port,))
  47. rows = cur.fetchall()
  48. while rows:
  49. port += 1
  50. game_count += 1
  51. cur.execute("SELECT * FROM games WHERE port=?", (port,))
  52. rows = cur.fetchall()
  53. if game_count >= MAX_GAMES:
  54. return
  55. # check duplicate name
  56. base_name = name
  57. base_name_count = 1
  58. cur.execute("SELECT * FROM games WHERE name=?", (name,))
  59. rows = cur.fetchall()
  60. while rows:
  61. base_name_count += 1
  62. name = base_name + "_" + str(base_name_count)
  63. cur.execute("SELECT * FROM games WHERE name=?", (name,))
  64. rows = cur.fetchall()
  65. # gen secret
  66. secret = secrets.token_hex(32)
  67. remote_addr = request.remote_addr
  68. if 'X-Forwarded-For' in request.headers:
  69. remote_addr = request.headers.getlist(
  70. "X-Forwarded-For")[0].rpartition(' ')[-1]
  71. cur.execute("INSERT INTO games (name,secret,ip,port,bots,player_count,running) VALUES (?,?,?,?,?,0,0)",
  72. (name, secret, remote_addr, port, bots))
  73. con.commit()
  74. cur.execute("SELECT id FROM games WHERE secret=?",
  75. (secret,))
  76. result = cur.fetchone()
  77. if result[0]:
  78. games[result[0]] = subprocess.Popen([GAME_EXEC,
  79. GAME_EXEC_ARG_PACK, GAME_EXEC_ARG_PACK_PATH,
  80. GAME_EXEC_ARG_ID.format(
  81. int(result[0])),
  82. GAME_EXEC_ARG_PORT.format(
  83. port),
  84. GAME_EXEC_ARG_SECRET.format(
  85. secret),
  86. GAME_EXEC_ARG_BOTS.format(
  87. int(bots)),
  88. GAME_EXEC_ARG_SERVER_ADDR.format(
  89. '127.0.0.1'), # localhost
  90. GAME_EXEC_ARG_API_ADDR.format('http://127.0.0.1:5000/')])
  91. except:
  92. con.rollback()
  93. abort(500)
  94. finally:
  95. con.close()
  96. if game_count >= MAX_GAMES:
  97. abort(403)
  98. return jsonify({'name': name, 'port': port})
  99. @app.route('/client/games', methods=['GET'])
  100. def get_games():
  101. try:
  102. with sqlite3.connect("database.db") as con:
  103. cur = con.cursor()
  104. query = "SELECT * FROM games ORDER BY running"
  105. if 'open' in request.args:
  106. query = "SELECT * FROM games WHERE running=0"
  107. cur.execute(query)
  108. rows = cur.fetchall()
  109. except:
  110. con.rollback()
  111. finally:
  112. con.close()
  113. result = []
  114. for row in rows:
  115. result.append(
  116. {'name': row[1], 'player_count': row[6], 'running': (row[4] == 1), 'port': row[5], 'bots': row[7]})
  117. return jsonify(result)
  118. @app.route('/game', methods=['PUT'])
  119. def update_game():
  120. if not request.json:
  121. abort(400)
  122. if not 'secret' in request.json or not 'player_count' in request.json or not 'running' in request.json:
  123. abort(400)
  124. secret = request.json.get('secret')
  125. player_count = request.json.get('player_count')
  126. running = request.json.get('running')
  127. try:
  128. with sqlite3.connect("database.db") as con:
  129. cur = con.cursor()
  130. cur.execute("SELECT id FROM games WHERE secret=?",
  131. (secret,))
  132. result = cur.fetchone()
  133. if result[0]:
  134. game_id = result[0]
  135. cur.execute("UPDATE games SET player_count=?,running=? WHERE id=?",
  136. (player_count, running, game_id,))
  137. con.commit()
  138. else:
  139. abort(401)
  140. except:
  141. con.rollback()
  142. return jsonify(False)
  143. finally:
  144. con.close()
  145. return jsonify(True)
  146. @app.route('/game', methods=['DELETE'])
  147. def close_game():
  148. if not request.json:
  149. abort(400)
  150. if not 'secret' in request.json:
  151. abort(400)
  152. secret = request.json.get('secret')
  153. try:
  154. with sqlite3.connect("database.db") as con:
  155. cur = con.cursor()
  156. cur.execute("SELECT id FROM games WHERE secret=?",
  157. (secret,))
  158. result = cur.fetchone()
  159. if result[0]:
  160. game_id = result[0]
  161. cur.execute("DELETE FROM games WHERE id=?", (game_id,))
  162. con.commit()
  163. if games[game_id]:
  164. games[game_id].terminate()
  165. games[game_id].communicate()
  166. del games[game_id]
  167. else:
  168. abort(401)
  169. except:
  170. con.rollback()
  171. return jsonify(False)
  172. finally:
  173. con.close()
  174. return jsonify(True)
  175. if __name__ == '__main__':
  176. app.run(debug=True, host='0.0.0.0', port=5000)