import asyncio import logging import struct from time import time from objects import glob flights = {} #flight_data_cache = b"" clients = set() STRUCTS = { "start": b"IB24s", "end": b"", "flight_data": b"?BHhHff", "server_open": b"", "server_close": b"" } class Client: def __init__(self, ws): self.ws = ws self.subscriptions = set() self.get_flight_changes = False async def broadcast_flight_change(uuid, active): logging.info("[%d] Broadcasting flight running: %r" % (uuid, active)) for cli in [cli for cli in clients if cli.get_flight_changes or uuid in cli.subscriptions]: await cli.ws.send(struct.pack(b" Connected." % (*ws.remote_address, path)) async def send_error(msg): logging.debug(" -> ERROR: %s" % (*ws.remote_address, path, msg.decode())) await cli.ws.send(struct.pack(b" -> All active flights" % (*ws.remote_address, path)) await cli.ws.send(struct.pack(b"<2B%dI" % len(flights), 0, # packet_id len(flights), *flights )) async def subscribe_flight_changes(data): if not len(data): return await send_error(b"Invalid data") (cli.get_flight_changes,) = struct.unpack(b" -> %subscribed to all flight changes" % (*ws.remote_address, path, cli.get_flight_changes and "S" or "Uns")) await cli.ws.send(struct.pack(b" -> %subscribed to %d" % (*ws.remote_address, path, uuid in cli.subscriptions and "S" or "Uns", uuid)) await cli.ws.send(struct.pack(b" Disconnected." % (*ws.remote_address, path)) class Flight: def __init__(self, data): (self.uuid, self.playername_len, self.playername) = struct.unpack(b"<" + STRUCTS["start"], data[:29]) self.active = True self.last_timeline_values = [None] * 7 self.timeline = b"" logging.info("[%d] Flight started" % self.uuid) asyncio.ensure_future( broadcast_flight_change(self.uuid, self.active) ) def get_all(self): return struct.pack(b"<" + STRUCTS["start"], self.uuid, self.playername_len, self.playername, ) + self.timeline def add(self, data): data = [*struct.unpack(b"<" + STRUCTS["flight_data"], data[:16])] for i in range(len(data)): if data[i] == self.last_timeline_values[i]: data[i] = None else: self.last_timeline_values[i] = data[i] frame = self.format_flight_data(data) if not frame: logging.debug("[%d] Empty frame (skipping)" % self.uuid) return logging.debug("[%d] New frame: %s" % (self.uuid, frame)) asyncio.ensure_future( broadcast_flight_data(self.uuid, frame) ) self.timeline += frame @staticmethod def format_flight_data(data): ret = b"" flag = 0 for i in range(len(data)): if data[i] != None: flag |= 1< 5: logging.error("Invalid packet header: %b" % data[:4]) return switch = { 0: self.unimplemented, # ping? 1: self.handle_start, 2: self.handle_flight_data, 3: self.handle_end, 4: self.handle_server_open, 5: self.handle_server_close } switch.get(packet_id, self.unimplemented)(data[4:]) @staticmethod def unimplemented(data): logging.warning("Unimplemented: %b" % data) @staticmethod def handle_start(data): flight = Flight(data) flights[flight.uuid] = flight logging.debug("Added %d into flights dict" % flight.uuid) @staticmethod def handle_end(data): (uuid, data) = (*struct.unpack(b"