You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
249 lines
6.1 KiB
249 lines
6.1 KiB
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 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}); |
|
} |
|
|
|
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 redraw_region(bbox) { |
|
storage.ctx0.save(); |
|
storage.ctx0.clearRect(bbox.xmin, bbox.ymin, bbox.xmax - bbox.xmin, bbox.ymax - bbox.ymin); |
|
|
|
storage.ctx0.beginPath(); |
|
storage.ctx0.rect(bbox.xmin, bbox.ymin, bbox.xmax - bbox.xmin, bbox.ymax - bbox.ymin); |
|
storage.ctx0.clip(); |
|
|
|
for (const event of storage.events) { |
|
if (event.type === EVENT.STROKE && !event.deleted) { |
|
if (stroke_intersects_region(event.points, bbox)) { |
|
draw_stroke(event); |
|
} |
|
} |
|
} |
|
|
|
storage.ctx0.restore(); |
|
} |
|
|
|
async function handle_event(event) { |
|
console.debug(`event type ${event.type} from user ${event.user_id}`); |
|
|
|
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; |
|
} |
|
} |
|
|
|
break; |
|
} |
|
|
|
case EVENT.IMAGE: { |
|
const r = await fetch(config.image_url + event.image_id); |
|
const blob = await r.blob(); |
|
const bitmap = await createImageBitmap(blob); |
|
|
|
|
|
const x = (event.x <= storage.canvas.width ? event.x : event.x - 65536); |
|
const y = (event.y <= storage.canvas.height ? event.y : event.y - 65536); |
|
|
|
storage.ctx0.drawImage(bitmap, x, y); |
|
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: { |
|
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); |
|
handle_event(event); |
|
storage.events.push(event); |
|
} |
|
|
|
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; |
|
} |
|
} |
|
}
|
|
|