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.
		
		
		
		
		
			
		
			
				
					
					
						
							265 lines
						
					
					
						
							6.7 KiB
						
					
					
				
			
		
		
	
	
							265 lines
						
					
					
						
							6.7 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 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 url = config.image_url + event.image_id; | 
						|
            const item = document.createElement('img'); | 
						|
            item.classList.add('floating-image'); | 
						|
            item.style.left = `${event.x}px`; | 
						|
            item.style.top = `${event.y}px`; | 
						|
            item.setAttribute('src', url); | 
						|
            elements.images.appendChild(item); | 
						|
            // 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; | 
						|
        } | 
						|
    } | 
						|
}
 | 
						|
 |