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.

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