function deserializer_create(buffer, dataview) { return { 'offset': 0, 'size': buffer.byteLength, 'buffer': buffer, 'view': dataview, 'strview': new Uint8Array(buffer), }; } function des_u8(d) { const value = d.view.getUint8(d.offset); d.offset += 1; return value; } function des_u16(d) { const value = d.view.getUint16(d.offset, true); d.offset += 2; return value; } function des_u32(d) { const value = d.view.getUint32(d.offset, true); d.offset += 4; return value; } function des_u16array(d, count) { const result = []; for (let i = 0; i < count; ++i) { const item = d.view.getUint16(d.offset, true); d.offset += 2; result.push(item); } return result; } function des_event(d) { const event = {}; event.type = des_u8(d); event.user_id = des_u32(d); switch (event.type) { case EVENT.PREDRAW: { event.x = des_u16(d); event.y = des_u16(d); break; } case EVENT.STROKE: { const point_count = des_u16(d); const width = des_u16(d); const color = des_u32(d); const coords = des_u16array(d, point_count * 2); event.points = []; for (let i = 0; i < point_count; ++i) { const x = coords[2 * i + 0]; const y = coords[2 * i + 1]; event.points.push({'x': x, 'y': y}); } event.color = color; event.width = width; break; } case EVENT.IMAGE: { event.image_id = des_u32(d); event.x = des_u16(d); event.y = des_u16(d); break; } case EVENT.UNDO: case EVENT.REDO: { break; } default: { console.error('fuck'); } } return event; } function bitmap_bbox(event) { const x = (event.x <= storage.canvas.width ? event.x : event.x - 65536); const y = (event.y <= storage.canvas.height ? event.y : event.y - 65536); const bbox = { 'xmin': x, 'xmax': x + event.bitmap.width, 'ymin': y, 'ymax': y + event.bitmap.height }; return bbox; } async function handle_event(event) { console.debug(`event type ${event.type} from user ${event.user_id}`); // TODO(@speed): do not handle locally predicted events switch (event.type) { case EVENT.STROKE: { if (event.user_id in storage.predraw || event.user_id === storage.me.id) { storage.predraw[event.user_id] = []; redraw_predraw(); } draw_stroke(event); break; } case EVENT.UNDO: { for (let i = storage.events.length - 1; i >=0; --i) { const other_event = storage.events[i]; if (other_event.type === EVENT.STROKE && other_event.user_id === event.user_id && !other_event.deleted) { other_event.deleted = true; const stats = stroke_stats(other_event.points, storage.cursor.width); redraw_region(stats.bbox); break; } else if (other_event.type === EVENT.IMAGE && other_event.user_id === event.user_id && !other_event.deleted) { other_event.deleted = true; const bbox = bitmap_bbox(other_event); redraw_region(bbox); break; } } break; } case EVENT.IMAGE: { const r = await fetch(config.image_url + event.image_id); const blob = await r.blob(); const bitmap = await createImageBitmap(blob); event.bitmap = bitmap; const bbox = bitmap_bbox(event); storage.ctx0.drawImage(bitmap, bbox.xmin, bbox.ymin); break; } default: { console.error('fuck'); } } } async function handle_message(d) { const message_type = des_u8(d); console.debug(message_type); switch (message_type) { case MESSAGE.JOIN: case MESSAGE.INIT: { elements.canvas0.classList.add('white'); storage.me.id = des_u32(d); storage.server_lsn = des_u32(d); if (storage.server_lsn > storage.lsn) { // Server knows something that we don't storage.lsn = storage.server_lsn; } if (message_type === MESSAGE.JOIN) { ls.setItem('sessionId', des_u32(d)); console.debug('join in'); } else { console.debug('init in'); } const event_count = des_u32(d); console.debug(`${event_count} events in init`); storage.ctx0.clearRect(0, 0, storage.ctx0.canvas.width, storage.ctx0.canvas.height); for (let i = 0; i < event_count; ++i) { const event = des_event(d); await handle_event(event); storage.events.push(event); } elements.canvas0.classList.remove('white'); send_ack(event_count); sync_queue(); break; } case MESSAGE.FIRE: { const user_id = des_u32(d); const predraw_event = des_event(d); predraw_user(user_id, predraw_event); break; } case MESSAGE.ACK: { const lsn = des_u32(d); console.debug(`ack ${lsn} in`); if (lsn > storage.server_lsn) { // ACKs may arrive out of order storage.server_lsn = lsn; } break; } case MESSAGE.SYN: { const sn = des_u32(d); const count = des_u32(d); const we_expect = sn - storage.sn; const first = count - we_expect; console.debug(`syn ${sn} in`); for (let i = 0; i < count; ++i) { const event = des_event(d); if (i >= first) { handle_event(event); storage.events.push(event); } } storage.sn = sn; await send_ack(sn); break; } default: { console.error('fuck'); return; } } }