function serializer_create(size) { const buffer = new ArrayBuffer(size); return { 'offset': 0, 'size': size, 'buffer': buffer, 'view': new DataView(buffer), 'strview': new Uint8Array(buffer), }; } function ser_u8(s, value) { s.view.setUint8(s.offset, value); s.offset += 1; } function ser_u16(s, value) { s.view.setUint16(s.offset, value, true); s.offset += 2; } function ser_u32(s, value) { s.view.setUint32(s.offset, value, true); s.offset += 4; } function ser_event(s, event) { ser_u8(s, event.type); ser_u8(s, 0); // padding for 16bit alignment switch (event.type) { case EVENT.PREDRAW: { ser_u16(s, event.x); ser_u16(s, event.y); break; } case EVENT.STROKE: { ser_u16(s, event.points.length); ser_u16(s, event.width); ser_u32(s, event.color); if (config.debug_print) console.debug('original', event.points); for (const point of event.points) { ser_u16(s, point.x); ser_u16(s, point.y); } break; } case EVENT.IMAGE: case EVENT.IMAGE_MOVE: { const image_id = parseInt(event.image_id); ser_u32(s, image_id); ser_u16(s, event.x); ser_u16(s, event.y); break; } case EVENT.UNDO: case EVENT.REDO: { break; } case EVENT.ERASER: { ser_u32(s, event.stroke_id); break; } default: { console.error('fuck'); } } } async function send_ack(sn) { const s = serializer_create(1 + 4); ser_u8(s, MESSAGE.ACK); ser_u32(s, sn); if (config.debug_print) console.debug(`ack ${sn} out`); try { if (ws) await ws.send(s.buffer); } catch(e) { ws.close(); } } async function sync_queue() { if (ws === null) { if (config.debug_print) console.debug('socket has closed, stopping SYNs'); return; } let size = 1 + 1 + 4 + 4; // opcode + lsn + event count let count = storage.lsn - storage.server_lsn; if (count === 0) { if (config.debug_print) console.debug('server ACKed all events, clearing queue'); storage.queue.length = 0; return; } for (let i = count - 1; i >= 0; --i) { const event = storage.queue[storage.queue.length - 1 - i]; size += event_size(event); } const s = serializer_create(size); ser_u8(s, MESSAGE.SYN); ser_u8(s, 0); // padding for 16bit alignment ser_u32(s, storage.lsn); ser_u32(s, count); for (let i = count - 1; i >= 0; --i) { const event = storage.queue[storage.queue.length - 1 - i]; ser_event(s, event); } if (config.debug_print) console.debug(`syn ${storage.lsn} out`); try { if (ws) await ws.send(s.buffer); } catch(e) { ws.close(); } setTimeout(sync_queue, config.sync_timeout); } function push_event(event) { storage.lsn += 1; switch (event.type) { case EVENT.STROKE: { const points = process_stroke(event.points); storage.queue.push({ 'type': EVENT.STROKE, 'points': points, 'width': event.width, 'color': event.color, }); break; } case EVENT.RULER: { event.type = EVENT.STROKE; storage.queue.push(event); break; } case EVENT.ERASER: case EVENT.IMAGE: case EVENT.IMAGE_MOVE: case EVENT.UNDO: case EVENT.REDO: { storage.queue.push(event); break; } default: { console.error('fuck'); } } } // Queue an event and initialize repated sends until ACKed function queue_event(event, skip = false) { push_event(event); if (skip) { return; } if (storage.timers.queue_sync) { clearTimeout(storage.timers.queue_sync); } sync_queue(); } // Fire and forget. Doesn't do anything if we are offline async function fire_event(event) { const s = serializer_create(1 + event_size(event)); ser_u8(s, MESSAGE.FIRE); ser_event(s, event); try { if (ws) await ws.send(s.buffer); } catch(e) { ws.close(); } }