class FLYSocket { constructor(ws, callbackData = null, callbackRaw = null) { this.ws = new WebSocket(ws); this.pointer = 0; this.callbackData = callbackData; this.callbackRaw = callbackRaw; this.ws.onmessage = this.onMessage.bind(this); } onMessage(e) { new Response(event.data).arrayBuffer().then( function(buff) { let data = new DataView(buff); if (this.callbackRaw !== null) this.callbackRaw(data); if (this.callbackData !== null) this.callbackData( this.parse(data) ); }.bind(this) ); } parse(data) { window.data = data; // DEBUG let packets = []; this.pointer = 0; while (this.pointer < data.byteLength) { let packetID = this.readUint8(data); packets.push({ packetID: packetID, value: (this["handle_packet" + packetID] || this.handle_packetInvalid).call(this, data) }); } return packets; } readUint8(data) { return data.getUint8(this.pointer++, true); } readUint16(data) { let ret = data.getUint16(this.pointer, true); this.pointer += 2; return ret; } readInt16(data) { let ret = data.getInt16(this.pointer, true); this.pointer += 2; return ret; } readUint32(data) { let ret = data.getUint32(this.pointer, true); this.pointer += 4; return ret; } readFloat32(data) { let ret = data.getFloat32(this.pointer, true); this.pointer += 4; return ret; } handle_packet0(data) { // BxI // B: Length // I: FlightUUID let ret = []; let length = this.readUint8(data); for (let i = 0; i < length; i++) ret.push( this.readUint32(data) ); return ret; } handle_packet1(data) { // ? // ?: Subscribed to changes return this.readUint8(data) != 0; } handle_packet2(data) { // I? // I: FlightUUID // ?: Subscribed to flight data let ret = []; ret.push( this.readUint32(data) ); ret.push( this.readUint8(data) != 0 ); return ret; } handle_packet3(data) { // TODO: implement (Whole data / history) // xs // s: DataStream let ret = {}; ret["uuid"] = this.readUint32(data); ret["maxFuel"] = this.readFloat32(data); ret["model"] = this.readUint16(data); let playernameLength = this.readUint8(data); let playername = ""; for (let i = 0; i < playernameLength; i++) playername += String.fromCharCode( this.readUint8(data) ); ret["playername"] = playername; let timeline = []; while (this.pointer < data.byteLength) timeline.push( this.parseFrame(data) ); ret["timeline"] = timeline; return ret; } handle_packet4(data) { // I? // I: FlightUUID // ?: Flight active let ret = []; ret.push( this.readUint32(data) ); ret.push( this.readUint8(data) != 0 ); return ret; } handle_packet5(data) { // Ixs // I: FlightUUID // s: DataStream let ret = []; ret.push( this.readUint32(data) ); ret.push( this.parseFrame(data) ); return ret; } handle_packet255(data) { // Hxs // H: Length // s: ErrorMessage let ret = ""; let length = this.readUint16(data); for (let i = 0; i < length; i++) { ret += String.fromCharCode( this.readUint8(data) ); } return ret; } handle_packetInvalid(data) { throw TypeError(`Invalid DATA: ${Array.from( this.buffer2HexArray(data, --this.pointer) ).join(" ")}`); } parseFrame(data) { // IBxs // I: Timestamp // B: Flag // s: FrameStream let ret = {}; ret["timestamp"] = this.readUint32(data); let flags = this.readUint8(data); if (flags & (1<<0)) { let statusFlag = this.readUint8(data); ret["paused"] = !!( statusFlag & (1<<0) ); ret["engine"] = !!( statusFlag & (1<<1) ); } if (flags & (1<<1)) { ret["satisfaction"] = this.readUint8(data); } if (flags & (1<<2)) { ret["speed"] = this.readUint16(data); } if (flags & (1<<3)) { ret["altitude"] = this.readInt16(data); } if (flags & (1<<4)) { ret["health"] = this.readUint16(data); } if (flags & (1<<5)) { ret["fuel"] = this.readFloat32(data); } if (flags & (1<<6)) { ret["x"] = this.readFloat32(data); } if (flags & (1<<7)) { ret["y"] = this.readFloat32(data); } return ret; } *buffer2HexArray(buff, start = 0, end = buff.byteLength) { for (let i = start; i < end; i++) { yield buff.getUint8(i).toString(16); } } }