import asyncio import logging import struct from time import time from objects import glob flights = {} clients = set() STRUCTS = { "start": b"fHB%ds", "flight_data": b"BBHhHfff", "end": b"", "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, uuid, data): self.uuid = uuid ( self.max_fuel, self.model_id, self.playername_len, self.playername ) = struct.unpack(b"<" + STRUCTS["start"] % 24, data[:31]) self.active = True self.last_timeline_values = [None] * 8 self.timeline = b"" # Fix playername padding self.playername = self.playername[:self.playername_len] logging.info("[%d] Flight started" % self.uuid) asyncio.ensure_future( broadcast_flight_change(self.uuid, self.active) ) def get_head(self): return struct.pack(b" 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): (uuid, data) = (*struct.unpack(b"