webserver.py 5.5 KB

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