import json import simple_websocket from flask import Flask from flask_sock import Sock from queue import Queue from threading import Thread, Lock class Client: def __init__(self, sock, client_id): self.sock = sock self.data = { 'id': client_id, 'name': '' } class GlebbyState: def __init__(self): self.clients_lock = Lock() self.clients = dict() self.next_client_id_lock = Lock() self.next_client_id = 0 self.incoming_messages = Queue() # domain stuff def handle_message_from(self, client_id, payload): print(f"{client_id}: {payload}") if payload['type'] == 'set-name': self.clients[client_id].data['name'] = payload['name'] self.broadcast(client_id, { 'type': 'set-name', 'name': payload['name'] }) elif payload['type'] == 'chat': self.broadcast(client_id, { 'type': 'chat', 'message': payload['message'] }) else: print("Unhandled!") def get_state_dto(self, client_id): return { 'yourId': client_id, 'players': [ self.clients[other_client_id].data for other_client_id in self.clients ] } # message receiving and sending def put_incoming_message(self, client_id, payload): self.incoming_messages.put({ 'from': client_id, 'payload': json.loads(payload) }) def send_to(self, client_id_from, client_id_to, payload): self.clients[client_id_to].sock.send(json.dumps({ 'from': client_id_from, 'payload': payload })) def broadcast(self, client_id_from, payload): with self.clients_lock: for client_id_to in self.clients: self.send_to(client_id_from, client_id_to, payload) # client management def _get_next_client_id(self): with self.next_client_id_lock: client_id = self.next_client_id self.next_client_id += 1 return client_id def add_client(self, sock): client_id = self._get_next_client_id() self.broadcast(client_id, {'type': 'join'}) with self.clients_lock: self.clients[client_id] = Client(sock, client_id) self.send_to(None, client_id, { 'type': 'init', 'state': self.get_state_dto(client_id) }) return client_id def remove_client(self, client_id): with self.clients_lock: del self.clients[client_id] self.broadcast(client_id, {'type': 'leave'}) # event thread stuff def start_event_thread(self): event_thread = Thread(target=lambda: self._event_thread()) event_thread.daemon = True event_thread.start() def _event_thread(self): while True: msg = self.incoming_messages.get() self.handle_message_from(msg['from'], msg['payload']) # Initialization state = GlebbyState() state.start_event_thread() app = Flask(__name__) sock = Sock(app) @sock.route('/glebby') def echo(sock): client_id = state.add_client(sock) try: while True: data = sock.receive() state.put_incoming_message(client_id, data) except simple_websocket.ConnectionClosed: state.remove_client(client_id)