webserver.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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 websockets
  9. from color import Color
  10. from option import OptionCode, Option
  11. from game import Game
  12. from player import ErrorCode, StateCode, Player
  13. logging.basicConfig()
  14. games = dict() # code -> game
  15. sockets = dict() # code -> [(player, websocket), ...]
  16. class DogEncoder(json.JSONEncoder):
  17. def default(self, obj):
  18. if hasattr(obj, '__dict__'):
  19. return obj.__dict__
  20. return json.JSONEncoder.default(self, obj)
  21. from http import HTTPStatus
  22. MIME_TYPES = {
  23. "html": "text/html",
  24. "js": "text/javascript",
  25. "css": "text/css"
  26. }
  27. async def process_request(sever_root, path, request_headers):
  28. """Serves a file when doing a GET request with a valid path."""
  29. if "Upgrade" in request_headers:
  30. return # Probably a WebSocket connection
  31. path = '/dog.html'
  32. response_headers = [
  33. ('Server', 'asyncio websocket server'),
  34. ('Connection', 'close'),
  35. ]
  36. # Derive full system path
  37. full_path = os.path.realpath(os.path.join(sever_root, path[1:]))
  38. # Validate the path
  39. if os.path.commonpath((sever_root, full_path)) != sever_root or \
  40. not os.path.exists(full_path) or not os.path.isfile(full_path):
  41. print("HTTP GET {} 404 NOT FOUND".format(path))
  42. return HTTPStatus.NOT_FOUND, [], b'404 NOT FOUND'
  43. # Guess file content type
  44. extension = full_path.split(".")[-1]
  45. mime_type = MIME_TYPES.get(extension, "application/octet-stream")
  46. response_headers.append(('Content-Type', mime_type))
  47. # Read the whole file into memory and send it out
  48. body = open(full_path, 'rb').read()
  49. response_headers.append(('Content-Length', str(len(body))))
  50. print("HTTP GET {} 200 OK".format(path))
  51. return HTTPStatus.OK, response_headers, body
  52. async def notify(player_sockets):
  53. if player_sockets:
  54. await asyncio.wait([websocket.send(json.dumps(player, cls=DogEncoder)) for (player, websocket) in player_sockets])
  55. async def handler(websocket, path):
  56. player = Player()
  57. player.options = [
  58. Option(OptionCode.NEW_GAME, "Nieuw spel"),
  59. Option(OptionCode.JOIN_GAME, "Doe mee met een spel", game_code='3936')]
  60. player.message = "Wat wil je doen?"
  61. player.set_state(StateCode.START)
  62. game_code = 0
  63. try:
  64. await notify([(player, websocket)])
  65. async for message in websocket:
  66. option = Option(**json.loads(message))
  67. if not player.check_option(option):
  68. await notify([(player, websocket)])
  69. continue
  70. player.set_error(None)
  71. if option.code == OptionCode.NEW_GAME:
  72. game_code = 3936 # random.randint(1000, 9999);
  73. sockets[game_code] = [(player, websocket)]
  74. player.message = f"De andere spelers kunnen meedoen door code {game_code} in te voeren"
  75. player.set_state(StateCode.JOIN_OTHERS, game_code=game_code)
  76. player.options = []
  77. await notify(sockets[game_code])
  78. elif option.code == OptionCode.JOIN_GAME:
  79. game_code = option.game_code
  80. socketlist = sockets.get(game_code) if game_code > 0 else None
  81. if socketlist is None:
  82. player.message = f"onbekande code {game_code}"
  83. player.set_error(ErrorCode.UNKNOWN_CODE, game_code=game_code)
  84. await notify([(player, websocket)])
  85. continue
  86. sockets[game_code].append((player, websocket))
  87. if len(sockets[game_code]) < 4:
  88. player.message = "wacht op de andere spelers"
  89. player.set_state(StateCode.JOIN_OTHERS, game_code=game_code)
  90. player.options = []
  91. await notify([(player, websocket)])
  92. else:
  93. for (player, _) in sockets[game_code]:
  94. player.message = "Kies een kleur om mee te spelen"
  95. player.set_state(StateCode.PICK_COLOR)
  96. player.options = [
  97. Option(OptionCode.PICK_COLOR, "Rood", color=Color.RED),
  98. Option(OptionCode.PICK_COLOR, "Blauw", color=Color.BLUE),
  99. Option(OptionCode.PICK_COLOR, "Geel", color=Color.YELLOW),
  100. Option(OptionCode.PICK_COLOR, "Groen", color=Color.GREEN)
  101. ]
  102. await notify(sockets[game_code])
  103. elif option.code == OptionCode.PICK_COLOR:
  104. color = option.color
  105. if any(other.color == color for (other, _) in sockets[game_code]):
  106. player.message = f"Kleur {option.text} is al gekozen door een andere speler. " + player.message
  107. player.set_error(ErrorCode.COLOR_ALREADY_CHOSEN, color=color)
  108. await notify([(player, websocket)])
  109. continue
  110. player.color = color
  111. player.name = option.text
  112. player.message = "Wacht op de andere spelers"
  113. player.set_state(StateCode.PICK_COLOR_OTHERS)
  114. player.options = []
  115. for (other, _) in sockets[game_code]:
  116. other.options = [option for option in other.options if option.color != color]
  117. if all( not any(p.options) for (p, _) in sockets[game_code]):
  118. game = Game(p for (p, _) in sockets[game_code])
  119. games[game_code] = game
  120. await notify(sockets[game_code])
  121. else:
  122. game = games[game_code]
  123. game.play_option(player, option)
  124. await notify(sockets[game_code])
  125. finally:
  126. if player is not None and game_code > 0:
  127. sockets[game_code].remove((player, websocket))
  128. pass
  129. if __name__ == "__main__":
  130. static_handler = functools.partial(process_request, os.path.join(os.getcwd(), 'ui'))
  131. start_server = websockets.serve(handler, "localhost", 6789, process_request=static_handler)
  132. print("Running server at http://127.0.0.1:6789/")
  133. asyncio.get_event_loop().run_until_complete(start_server)
  134. asyncio.get_event_loop().run_forever()