webserver.py 5.1 KB

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