webserver.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. #!/usr/bin/env python
  2. # WS server example that synchronizes state across clients
  3. import asyncio
  4. import functools
  5. import json
  6. import logging
  7. import os
  8. import random
  9. import websockets
  10. from http import HTTPStatus
  11. from option import OptionCode, Option
  12. from game import Game
  13. from player import ErrorCode, StateCode, Player
  14. HOST = os.environ.get("KEEZEN_HOST") or "localhost"
  15. PORT = os.environ.get("KEEZEN_PORT") or 6789
  16. logging.basicConfig()
  17. games = dict() # code -> game
  18. sockets = dict() # code -> [(player, websocket), ...]
  19. users = ["Anna", "Bob", "Cynthia", "Daan", "Esoirulthayro", "Frank", "Gerben", "Hanna", "Ida", "Joost", "Karin", "Loes", "Max", "Nina"]
  20. class DogEncoder(json.JSONEncoder):
  21. def default(self, obj):
  22. if hasattr(obj, '__dict__'):
  23. return obj.__dict__
  24. return json.JSONEncoder.default(self, obj)
  25. MIME_TYPES = {
  26. "html": "text/html",
  27. "js": "text/javascript",
  28. "css": "text/css"
  29. }
  30. async def process_request(sever_root, path, request_headers):
  31. """Serves a file when doing a GET request with a valid path."""
  32. if "Upgrade" in request_headers:
  33. return # Probably a WebSocket connection
  34. path = '/dog.html'
  35. response_headers = [
  36. ('Server', 'asyncio websocket server'),
  37. ('Connection', 'close'),
  38. ]
  39. # Derive full system path
  40. full_path = os.path.realpath(os.path.join(sever_root, path[1:]))
  41. # Validate the path
  42. if os.path.commonpath((sever_root, full_path)) != sever_root or \
  43. not os.path.exists(full_path) or not os.path.isfile(full_path):
  44. print("HTTP GET {} 404 NOT FOUND".format(path))
  45. return HTTPStatus.NOT_FOUND, [], b'404 NOT FOUND'
  46. # Guess file content type
  47. extension = full_path.split(".")[-1]
  48. mime_type = MIME_TYPES.get(extension, "application/octet-stream")
  49. response_headers.append(('Content-Type', mime_type))
  50. # Read the whole file into memory and send it out
  51. body = open(full_path, 'rb').read()
  52. response_headers.append(('Content-Length', str(len(body))))
  53. print("HTTP GET {} 200 OK".format(path))
  54. return HTTPStatus.OK, response_headers, body
  55. async def notify(player_sockets):
  56. if player_sockets:
  57. await asyncio.wait([websocket.send(json.dumps(player, cls=DogEncoder)) for (player, websocket) in player_sockets])
  58. async def handler(websocket, path):
  59. player = Player(name=users[0])
  60. player.options = [
  61. Option(OptionCode.NEW_GAME, "Nieuw spel"),
  62. Option(OptionCode.JOIN_GAME, "Doe mee met een spel", game_code='3936')]
  63. player.message = "Wat wil je doen?"
  64. player.state = StateCode.START
  65. game_code = 0
  66. game = None
  67. try:
  68. await notify([(player, websocket)])
  69. async for message in websocket:
  70. option = Option(**json.loads(message))
  71. if not player.check_option(option):
  72. await notify([(player, websocket)])
  73. continue
  74. player.set_error(None)
  75. if option.code == OptionCode.NEW_GAME:
  76. game_code = 3936 # random.randint(1000, 9999);
  77. game = Game()
  78. games[game_code] = game
  79. if option.user_name is None:
  80. option.user_name = users[0]
  81. users.append(users[0])
  82. users.remove(users[0])
  83. player = game.join_player(option.user_name)
  84. player.game_code = game_code
  85. sockets[game_code] = [(player, websocket)]
  86. await notify(sockets[game_code])
  87. elif option.code == OptionCode.JOIN_GAME:
  88. game_code = option.game_code
  89. game = games.get(game_code) if game_code is not None and type(game_code) is int and game_code > 0 else None
  90. if game is None:
  91. player.message = f"onbekande code {game_code}"
  92. player.set_error(ErrorCode.UNKNOWN_CODE, game_code=game_code)
  93. await notify([(player, websocket)])
  94. continue
  95. if option.user_name is None:
  96. option.user_name = users[0]
  97. users.append(users[0])
  98. users.remove(users[0])
  99. player = game.join_player(option.user_name)
  100. player.game_code = game_code
  101. sockets[game_code].append((player, websocket))
  102. await notify(sockets[game_code])
  103. else:
  104. game = games[game_code]
  105. game.play_option(player, option)
  106. await notify(sockets[game_code])
  107. finally:
  108. if player is not None:
  109. if game is not None:
  110. game.unjoin_player(player)
  111. if game_code > 0:
  112. sockets[game_code].remove((player, websocket))
  113. await notify(sockets[game_code])
  114. if __name__ == "__main__":
  115. random.seed()
  116. static_handler = functools.partial(process_request, os.path.join(os.getcwd(), 'ui'))
  117. start_server = websockets.serve(handler, HOST, PORT, process_request=static_handler)
  118. print(f"Running server at {HOST}:{PORT}")
  119. asyncio.get_event_loop().run_until_complete(start_server)
  120. asyncio.get_event_loop().run_forever()