#!/usr/bin/env python # WS server example that synchronizes state across clients import asyncio import functools import json import logging import os import websockets from color import Color from option import OptionCode, Option from game import Game from player import ErrorCode, StateCode, Player logging.basicConfig() games = dict() # code -> game sockets = dict() # code -> [(player, websocket), ...] class DogEncoder(json.JSONEncoder): def default(self, obj): if hasattr(obj, '__dict__'): return obj.__dict__ return json.JSONEncoder.default(self, obj) from http import HTTPStatus MIME_TYPES = { "html": "text/html", "js": "text/javascript", "css": "text/css" } async def process_request(sever_root, path, request_headers): """Serves a file when doing a GET request with a valid path.""" if "Upgrade" in request_headers: return # Probably a WebSocket connection path = '/dog.html' response_headers = [ ('Server', 'asyncio websocket server'), ('Connection', 'close'), ] # Derive full system path full_path = os.path.realpath(os.path.join(sever_root, path[1:])) # Validate the path if os.path.commonpath((sever_root, full_path)) != sever_root or \ not os.path.exists(full_path) or not os.path.isfile(full_path): print("HTTP GET {} 404 NOT FOUND".format(path)) return HTTPStatus.NOT_FOUND, [], b'404 NOT FOUND' # Guess file content type extension = full_path.split(".")[-1] mime_type = MIME_TYPES.get(extension, "application/octet-stream") response_headers.append(('Content-Type', mime_type)) # Read the whole file into memory and send it out body = open(full_path, 'rb').read() response_headers.append(('Content-Length', str(len(body)))) print("HTTP GET {} 200 OK".format(path)) return HTTPStatus.OK, response_headers, body async def notify(player_sockets): if player_sockets: await asyncio.wait([websocket.send(json.dumps(player, cls=DogEncoder)) for (player, websocket) in player_sockets]) async def handler(websocket, path): player = Player() player.options = [ Option(OptionCode.NEW_GAME, "Nieuw spel"), Option(OptionCode.JOIN_GAME, "Doe mee met een spel", game_code='3936')] player.message = "Wat wil je doen?" player.set_state(StateCode.START) game_code = 0 try: await notify([(player, websocket)]) async for message in websocket: option = Option(**json.loads(message)) if not player.check_option(option): await notify([(player, websocket)]) continue player.set_error(None) if option.code == OptionCode.NEW_GAME: game_code = 3936 # random.randint(1000, 9999); sockets[game_code] = [(player, websocket)] player.message = f"De andere spelers kunnen meedoen door code {game_code} in te voeren" player.set_state(StateCode.JOIN_OTHERS, game_code=game_code) player.options = [] await notify(sockets[game_code]) elif option.code == OptionCode.JOIN_GAME: game_code = option.game_code socketlist = sockets.get(game_code) if game_code > 0 else None if socketlist is None: player.message = f"onbekande code {game_code}" player.set_error(ErrorCode.UNKNOWN_CODE, game_code=game_code) await notify([(player, websocket)]) continue sockets[game_code].append((player, websocket)) if len(sockets[game_code]) < 4: player.message = "wacht op de andere spelers" player.set_state(StateCode.JOIN_OTHERS, game_code=game_code) player.options = [] await notify([(player, websocket)]) else: for (player, _) in sockets[game_code]: player.message = "Kies een kleur om mee te spelen" player.set_state(StateCode.PICK_COLOR) player.options = [ Option(OptionCode.PICK_COLOR, "Rood", color=Color.RED), Option(OptionCode.PICK_COLOR, "Blauw", color=Color.BLUE), Option(OptionCode.PICK_COLOR, "Geel", color=Color.YELLOW), Option(OptionCode.PICK_COLOR, "Groen", color=Color.GREEN) ] await notify(sockets[game_code]) elif option.code == OptionCode.PICK_COLOR: color = option.color if any(other.color == color for (other, _) in sockets[game_code]): player.message = f"Kleur {option.text} is al gekozen door een andere speler. " + player.message player.set_error(ErrorCode.COLOR_ALREADY_CHOSEN, color=color) await notify([(player, websocket)]) continue player.color = color player.name = option.text player.message = "Wacht op de andere spelers" player.set_state(StateCode.PICK_COLOR_OTHERS) player.options = [] for (other, _) in sockets[game_code]: other.options = [option for option in other.options if option.color != color] if all( not any(p.options) for (p, _) in sockets[game_code]): game = Game(p for (p, _) in sockets[game_code]) games[game_code] = game await notify(sockets[game_code]) else: game = games[game_code] game.play_option(player, option) await notify(sockets[game_code]) finally: if player is not None and game_code > 0: sockets[game_code].remove((player, websocket)) pass if __name__ == "__main__": static_handler = functools.partial(process_request, os.path.join(os.getcwd(), 'ui')) start_server = websockets.serve(handler, "localhost", 6789, process_request=static_handler) print("Running server at http://127.0.0.1:6789/") asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever()