Browse Source

You can draw once again!

ssao
A.Olokhtonov 10 months ago
parent
commit
c893a73ec5
  1. 4
      README.md
  2. 2
      client/aux.js
  3. 5
      client/client_recv.js
  4. 21
      client/client_send.js
  5. 24
      client/index.js
  6. 67
      client/math.js
  7. 108
      client/webgl_draw.js
  8. 56
      client/webgl_geometry.js
  9. 4
      client/webgl_listeners.js
  10. 15
      client/webgl_shaders.js
  11. 5
      server/deserializer.js
  12. 2
      server/recv.js
  13. 16
      server/send.js

4
README.md

@ -2,7 +2,7 @@ Release: @@ -2,7 +2,7 @@ Release:
* Engine
+ Benchmark harness
+ Reuse points, pack "nodraw" in high bit of stroke id (probably have at least one more bit, so up to 4 flag configurations)
- Draw dynamic data (strokes in progress)
+ Draw dynamic data (strokes in progress)
- Textured quads (pictures, code already written in older version)
- Resize and move pictures (draw handles)
- Z-prepass fringe bug (also, when do we enable the prepass?)
@ -26,6 +26,8 @@ Release: @@ -26,6 +26,8 @@ Release:
- Polish
- Show what's happening while the desk is loading (downloading, processing, uploading to gpu)
- Settings panel (including the setting for "offline mode")
- Use typedvector where appropriate
- Set up VAOs
- Presentation / "marketing"
- Title
- Icon

2
client/aux.js

@ -33,7 +33,7 @@ async function insert_image(state, context, file) { @@ -33,7 +33,7 @@ async function insert_image(state, context, file) {
}
function event_size(event) {
let size = 1 + 3; // type + padding
let size = 4; // type
switch (event.type) {
case EVENT.PREDRAW: {

5
client/client_recv.js

@ -87,7 +87,6 @@ function des_event(d, state = null) { @@ -87,7 +87,6 @@ function des_event(d, state = null) {
state.coordinates.count += point_count * 2;
event.stroke_id = stroke_id;
event.lods = [];
event.color = color;
event.width = width;
@ -304,7 +303,7 @@ function handle_event(state, context, event, options = {}) { @@ -304,7 +303,7 @@ function handle_event(state, context, event, options = {}) {
}
async function handle_message(state, context, d) {
const message_type = des_u8(d);
const message_type = des_u32(d);
let do_draw = false;
// if (config.debug_print) console.debug(message_type);
@ -421,7 +420,7 @@ async function handle_message(state, context, d) { @@ -421,7 +420,7 @@ async function handle_message(state, context, d) {
if (config.debug_print) console.debug(`syn ${sn} in`);
for (let i = 0; i < count; ++i) {
const event = des_event(d);
const event = des_event(d, state);
if (i >= first) {
const need_draw = handle_event(state, context, event);
do_draw = do_draw || need_draw;

21
client/client_send.js

@ -43,11 +43,6 @@ function ser_clear(s) { @@ -43,11 +43,6 @@ function ser_clear(s) {
s.gpu_upload_from = 0;
}
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;
@ -71,7 +66,7 @@ function ser_align(s, to) { @@ -71,7 +66,7 @@ function ser_align(s, to) {
}
function ser_event(s, event) {
ser_u8(s, event.type);
ser_u32(s, event.type);
switch (event.type) {
case EVENT.PREDRAW: {
@ -101,8 +96,6 @@ function ser_event(s, event) { @@ -101,8 +96,6 @@ function ser_event(s, event) {
if (config.debug_print) console.debug('original', event.points);
ser_align(s, 4);
for (const point of event.points) {
ser_f32(s, point.x);
ser_f32(s, point.y);
@ -137,9 +130,9 @@ function ser_event(s, event) { @@ -137,9 +130,9 @@ function ser_event(s, event) {
}
async function send_ack(sn) {
const s = serializer_create(1 + 4);
const s = serializer_create(4 + 4);
ser_u8(s, MESSAGE.ACK);
ser_u32(s, MESSAGE.ACK);
ser_u32(s, sn);
if (config.debug_print) console.debug(`ack ${sn} out`);
@ -158,7 +151,7 @@ async function sync_queue(state) { @@ -158,7 +151,7 @@ async function sync_queue(state) {
return;
}
let size = 1 + 3 + 4 + 4; // opcode + lsn + event count
let size = 4 + 4 + 4; // opcode + lsn + event count
let count = state.lsn - state.server_lsn;
if (count === 0) {
@ -174,7 +167,7 @@ async function sync_queue(state) { @@ -174,7 +167,7 @@ async function sync_queue(state) {
const s = serializer_create(size);
ser_u8(s, MESSAGE.SYN);
ser_u32(s, MESSAGE.SYN);
ser_u32(s, state.lsn);
ser_u32(s, count);
@ -251,9 +244,9 @@ function queue_event(state, event, skip = false) { @@ -251,9 +244,9 @@ function queue_event(state, event, skip = false) {
async function fire_event(state, event) {
if (!state.online) { return; }
const s = serializer_create(1 + event_size(event));
const s = serializer_create(4 + event_size(event));
ser_u8(s, MESSAGE.FIRE);
ser_u32(s, MESSAGE.FIRE);
ser_event(s, event);
try {

24
client/index.js

@ -22,7 +22,8 @@ const config = { @@ -22,7 +22,8 @@ const config = {
bytes_per_stroke: 2 * 3 + 2, // r, g, b, width
initial_static_bytes: 4096 * 16,
initial_dynamic_bytes: 4096,
stroke_texture_size: 1024,
stroke_texture_size: 1024, // means no more than 1024^2 = 1M strokes in total (this is a LOT. HMH blackboard has like 80K)
dynamic_stroke_texture_size: 128, // means no more than 128^2 = 16K dynamic strokes at once
benchmark: {
zoom: 0.035,
offset: { x: 900, y: 400 },
@ -245,10 +246,15 @@ function main() { @@ -245,10 +246,15 @@ function main() {
'instance_data_points': tv_create(Float32Array, 4096),
'instance_data_ids': tv_create(Uint32Array, 4096),
'dynamic_instance_points': tv_create(Float32Array, 4096),
'dynamic_instance_ids': tv_create(Uint32Array, 4096),
'lods': [],
'stroke_data': serializer_create(config.initial_static_bytes),
'dynamic_stroke_data': serializer_create(config.initial_static_bytes),
'dynamic_stroke_count': 0,
'dynamic_segment_count': 0,
'bgcolor': {'r': 1.0, 'g': 1.0, 'b': 1.0},
@ -261,18 +267,6 @@ function main() { @@ -261,18 +267,6 @@ function main() {
const url = new URL(window.location.href);
const parts = url.pathname.split('/');
config.lod_levels = Math.ceil(Math.log2(1.0 / config.min_zoom));
for (let i = 0; i < config.lod_levels; ++i) {
context.lods.push({
'max_zoom': Math.pow(0.25, i), // use this LOD level when current canvas.zoom is less than this value, but not less than the next level max_zoom (or if this is the last zoom level)
'total_points': 0,
'segments': serializer_create(config.initial_static_bytes),
'data_buffer': null,
'index_buffer': null,
});
}
state.desk_id = parts.length > 0 ? parts[parts.length - 1] : 0;
init_webgl(state, context);

67
client/math.js

@ -50,6 +50,7 @@ function rdp_find_max(state, zoom, stroke, start, end) { @@ -50,6 +50,7 @@ function rdp_find_max(state, zoom, stroke, start, end) {
return result;
}
*/
function process_rdp_indices_r(state, zoom, mask, stroke, start, end) {
// Looks like the recursive implementation spends most of its time in the function call overhead
// Let's try to use an explicit stack instead to give the js engine more room to play with
@ -124,6 +125,72 @@ function process_stroke(state, zoom, stroke) { @@ -124,6 +125,72 @@ function process_stroke(state, zoom, stroke) {
return npoints;
}
function rdp_find_max2(points, start, end) {
const EPS = 0.5;
let result = -1;
let max_dist = 0;
const a = points[start];
const b = points[end];
const dx = b.x - a.x;
const dy = b.y - a.y;
const dist_ab = Math.sqrt(dx * dx + dy * dy);
const sin_theta = dy / dist_ab;
const cos_theta = dx / dist_ab;
for (let i = start; i < end; ++i) {
const p = points[i];
const ox = p.x - a.x;
const oy = p.y - a.y;
const rx = cos_theta * ox + sin_theta * oy;
const ry = -sin_theta * ox + cos_theta * oy;
const x = rx + a.x;
const y = ry + a.y;
const dist = Math.abs(y - a.y);
if (dist > EPS && dist > max_dist) {
result = i;
max_dist = dist;
}
}
return result;
}
function process_rdp_r2(points, start, end) {
let result = [];
const max = rdp_find_max2(points, start, end);
if (max !== -1) {
const before = process_rdp_r2(points, start, max);
const after = process_rdp_r2(points, max, end);
result = [...before, points[max], ...after];
}
return result;
}
function process_rdp2(points) {
const result = process_rdp_r2(points, 0, points.length - 1);
result.unshift(points[0]);
result.push(points[points.length - 1]);
return result;
}
// TODO: unify with regular process stroke
function process_stroke2(points) {
const result = process_rdp2(points);
return result;
}
function strokes_intersect_line(state, a, b) {
// TODO: handle stroke / eraser width
const result = [];

108
client/webgl_draw.js

@ -73,11 +73,17 @@ function draw(state, context) { @@ -73,11 +73,17 @@ function draw(state, context) {
bvh_clip(state, context);
const segment_count = geometry_write_instances(state, context);
const dynamic_segment_count = context.dynamic_segment_count;
const dynamic_stroke_count = context.dynamic_stroke_count;
// "Static" data upload
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_instance']);
gl.bufferData(gl.ARRAY_BUFFER, context.instance_data_points.size * 4 + context.instance_data_ids.size * 4, gl.STREAM_DRAW);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, tv_data(context.instance_data_points));
gl.bufferSubData(gl.ARRAY_BUFFER, context.instance_data_points.size * 4, tv_data(context.instance_data_ids));
gl.bindTexture(gl.TEXTURE_2D, context.textures['stroke_data']);
// TODO: this is stable data, only upload new strokes as they arrive
upload_square_rgba16ui_texture(gl, context.stroke_data, config.stroke_texture_size);
gl.uniform2f(locations['u_res'], context.canvas.width, context.canvas.height);
gl.uniform2f(locations['u_scale'], state.canvas.zoom, state.canvas.zoom);
@ -91,7 +97,7 @@ function draw(state, context) { @@ -91,7 +97,7 @@ function draw(state, context) {
gl.enableVertexAttribArray(locations['a_b']);
gl.enableVertexAttribArray(locations['a_stroke_id']);
// Points (a, b) and stroke ids are not stored in separate cpu buffers so that points can be resued
// Points (a, b) and stroke ids are stored in separate cpu buffers so that points can be reused (look at stride and offset values)
gl.vertexAttribPointer(locations['a_a'], 2, gl.FLOAT, false, 2 * 4, 0);
gl.vertexAttribPointer(locations['a_b'], 2, gl.FLOAT, false, 2 * 4, 2 * 4);
gl.vertexAttribIPointer(locations['a_stroke_id'], 1, gl.INT, 4, context.instance_data_points.size * 4);
@ -99,81 +105,47 @@ function draw(state, context) { @@ -99,81 +105,47 @@ function draw(state, context) {
gl.vertexAttribDivisor(locations['a_a'], 1);
gl.vertexAttribDivisor(locations['a_b'], 1);
gl.vertexAttribDivisor(locations['a_stroke_id'], 1);
gl.bindTexture(gl.TEXTURE_2D, context.textures['stroke_data']);
// TODO: this is stable data, only upload new strokes as they arrive
upload_square_rgba16ui_texture(gl, context.stroke_data, config.stroke_texture_size);
gl.activeTexture(gl.TEXTURE0);
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, segment_count); // TODO: based on clipping results
document.getElementById('debug-stats').innerHTML = `
<span>Segments onscreen: ${segment_count}</span>
<span>Canvas offset: (${state.canvas.offset.x}, ${state.canvas.offset.y})</span>
<span>Canvas zoom: ${Math.round(state.canvas.zoom * 100000) / 100000}</span>`;
/*
// Dynamic data (stroke previews that are currently in progress)
const dynamic_points = context.dynamic_serializer.offset / config.bytes_per_point;
if (dynamic_points > 0) {
gl.drawBuffers([gl.BACK]);
locations = context.locations['sdf'].main;
gl.useProgram(context.programs['sdf'].main);
gl.uniform2f(locations['u_res'], context.canvas.width, context.canvas.height);
gl.uniform2f(locations['u_scale'], state.canvas.zoom, state.canvas.zoom);
gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y);
gl.uniform1i(locations['u_stroke_count'], state.stroke_count);
gl.uniform1i(locations['u_debug_mode'], state.debug.red);
gl.clear(gl.DEPTH_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_packed_dynamic']);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers['b_packed_dynamic_index']);
gl.enableVertexAttribArray(locations['a_pos']);
gl.enableVertexAttribArray(locations['a_line']);
gl.enableVertexAttribArray(locations['a_color']);
gl.enableVertexAttribArray(locations['a_stroke_id']);
// Static draw (everything already bound)
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, segment_count);
// Dynamic strokes should be drawn above static strokes
gl.clear(gl.DEPTH_BUFFER_BIT);
gl.vertexAttribPointer(locations['a_pos'], 3, gl.FLOAT, false, config.bytes_per_point, 0);
gl.vertexAttribPointer(locations['a_line'], 4, gl.FLOAT, false, config.bytes_per_point, 4 * 3);
gl.vertexAttribPointer(locations['a_color'], 3, gl.UNSIGNED_BYTE, true, config.bytes_per_point, 4 * 3 + 4 * 4);
gl.vertexAttribIPointer(locations['a_stroke_id'], 1, gl.INT, config.bytes_per_point, 4 * 3 + 4 * 4 + 4);
// Dynamic draw (strokes currently being drawn)
gl.uniform1i(locations['u_stroke_count'], dynamic_stroke_count);
gl.uniform1i(locations['u_stroke_data'], 0);
gl.uniform1i(locations['u_stroke_texture_size'], config.dynamic_stroke_texture_size);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_dynamic_instance']);
const dynamic_indices = [];
let base = 0;
// Dynamic data upload
gl.bufferData(gl.ARRAY_BUFFER, context.dynamic_instance_points.size * 4 + context.dynamic_instance_ids.size * 4, gl.STREAM_DRAW);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, tv_data(context.dynamic_instance_points));
gl.bufferSubData(gl.ARRAY_BUFFER, context.dynamic_instance_points.size * 4, tv_data(context.dynamic_instance_ids));
gl.bindTexture(gl.TEXTURE_2D, context.textures['dynamic_stroke_data']);
upload_square_rgba16ui_texture(gl, context.dynamic_stroke_data, config.dynamic_stroke_texture_size);
for (const player_id in state.players) {
// player has the same data as their current stroke: points, color, width
const player = state.players[player_id];
if (player.points.length > 1) {
for (let i = 0; i < player.points.length - 1; ++i) {
dynamic_indices.push(base + 0);
dynamic_indices.push(base + 1);
dynamic_indices.push(base + 2);
dynamic_indices.push(base + 3);
dynamic_indices.push(base + 2);
dynamic_indices.push(base + 1);
gl.enableVertexAttribArray(locations['a_a']);
gl.enableVertexAttribArray(locations['a_b']);
gl.enableVertexAttribArray(locations['a_stroke_id']);
// Points (a, b) and stroke ids are stored in separate cpu buffers so that points can be reused (look at stride and offset values)
gl.vertexAttribPointer(locations['a_a'], 2, gl.FLOAT, false, 2 * 4, 0);
gl.vertexAttribPointer(locations['a_b'], 2, gl.FLOAT, false, 2 * 4, 2 * 4);
gl.vertexAttribIPointer(locations['a_stroke_id'], 1, gl.INT, 4, context.dynamic_instance_points.size * 4);
base += 4;
}
}
}
gl.vertexAttribDivisor(locations['a_a'], 1);
gl.vertexAttribDivisor(locations['a_b'], 1);
gl.vertexAttribDivisor(locations['a_stroke_id'], 1);
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, dynamic_segment_count);
if (context.need_dynamic_upload) {
gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(context.dynamic_serializer.buffer, 0, context.dynamic_serializer.offset), gl.DYNAMIC_DRAW);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(dynamic_indices), gl.DYNAMIC_DRAW);
context.need_dynamic_upload = false;
}
document.getElementById('debug-stats').innerHTML = `
<span>Segments onscreen: ${segment_count}</span>
<span>Canvas offset: (${state.canvas.offset.x}, ${state.canvas.offset.y})</span>
<span>Canvas zoom: ${Math.round(state.canvas.zoom * 100000) / 100000}</span>`;
gl.drawElements(gl.TRIANGLES, dynamic_indices.length, gl.UNSIGNED_INT, 0);
}
*/
if (context.gpu_timer_ext) {
gl.endQuery(context.gpu_timer_ext.TIME_ELAPSED_EXT);

56
client/webgl_geometry.js

@ -24,9 +24,9 @@ function geometry_prepare_stroke(state) { @@ -24,9 +24,9 @@ function geometry_prepare_stroke(state) {
if (state.players[state.me].points.length === 0) {
return null;
}
}
const points = process_stroke(state, state.canvas.zoom, state.players[state.me].points);
const points = process_stroke2(state.players[state.me].points);
return {
'color': state.players[state.me].color,
@ -223,30 +223,62 @@ function geometry_delete_stroke(state, context, stroke_index) { @@ -223,30 +223,62 @@ function geometry_delete_stroke(state, context, stroke_index) {
}
function recompute_dynamic_data(state, context) {
let bytes_needed = 0;
let total_points = 0;
let total_strokes = 0;
for (const player_id in state.players) {
const player = state.players[player_id];
if (player.points.length > 0) {
bytes_needed += player.points.length * 6 * config.bytes_per_point;
total_points += player.points.length;
total_strokes += 1;
}
}
if (bytes_needed > context.dynamic_serializer.size) {
context.dynamic_serializer = serializer_create(Math.ceil(bytes_needed * 1.62));
} else {
context.dynamic_serializer.offset = 0;
}
context.dynamic_instance_data = tv_ensure(context.dynamic_instance_points, round_to_pow2(total_points * 2, 4096));
context.dynamic_instance_ids = tv_ensure(context.dynamic_instance_ids, round_to_pow2(total_points, 4096));
tv_clear(context.dynamic_instance_points);
tv_clear(context.dynamic_instance_ids);
context.dynamic_stroke_data = ser_ensure(context.dynamic_stroke_data, config.bytes_per_stroke * total_strokes);
ser_clear(context.dynamic_stroke_data);
let stroke_index = 0;
for (const player_id in state.players) {
// player has the same data as their current stroke: points, color, width
const player = state.players[player_id];
if (player.points.length > 1) {
push_stroke(context.dynamic_serializer, player, 0);
for (let i = 0; i < player.points.length; ++i) {
const p = player.points[i];
tv_add(context.dynamic_instance_points, p.x);
tv_add(context.dynamic_instance_points, p.y);
if (i !== player.points.length - 1) {
tv_add(context.dynamic_instance_ids, stroke_index);
} else {
tv_add(context.dynamic_instance_ids, stroke_index | (1 << 31));
}
}
if (player.points.length > 0) {
const color_u32 = player.color;
const r = (color_u32 >> 16) & 0xFF;
const g = (color_u32 >> 8) & 0xFF;
const b = color_u32 & 0xFF;
ser_u16(context.dynamic_stroke_data, r);
ser_u16(context.dynamic_stroke_data, g);
ser_u16(context.dynamic_stroke_data, b);
ser_u16(context.dynamic_stroke_data, player.width);
stroke_index += 1; // TODO: proper player Z order
}
}
context.need_dynamic_upload = true;
context.dynamic_segment_count = total_points;
context.dynamic_stroke_count = total_strokes;
}
function geometry_add_point(state, context, player_id, point) {

4
client/webgl_listeners.js

@ -279,9 +279,9 @@ function mouseup(e, state, context) { @@ -279,9 +279,9 @@ function mouseup(e, state, context) {
const stroke = geometry_prepare_stroke(state);
if (stroke) {
//geometry_add_stroke(state, context, stroke, 0); // TODO: stroke index?
//geometry_add_stroke(state, context, stroke, 0);
queue_event(state, stroke_event(state));
//geometry_clear_player(state, context, state.me);
geometry_clear_player(state, context, state.me);
schedule_draw(state, context);
}

15
client/webgl_shaders.js

@ -323,24 +323,18 @@ function init_webgl(state, context) { @@ -323,24 +323,18 @@ function init_webgl(state, context) {
}
};
for (let i = 0; i < context.lods.length; ++i) {
const level = context.lods[i];
level.data_buffer = gl.createBuffer();
level.index_buffer = gl.createBuffer();
}
context.buffers['debug'] = {
'b_packed': gl.createBuffer(),
};
context.buffers['sdf'] = {
'b_packed_dynamic': gl.createBuffer(),
'b_packed_dynamic_index': gl.createBuffer(),
'b_instance': gl.createBuffer(),
'b_dynamic_instance': gl.createBuffer(),
};
context.textures = {
'stroke_data': gl.createTexture(),
'dynamic_stroke_data': gl.createTexture(),
};
gl.bindTexture(gl.TEXTURE_2D, context.textures['stroke_data']);
@ -348,6 +342,11 @@ function init_webgl(state, context) { @@ -348,6 +342,11 @@ function init_webgl(state, context) {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16UI, config.stroke_texture_size, config.stroke_texture_size, 0, gl.RGBA_INTEGER, gl.UNSIGNED_SHORT, null);
gl.bindTexture(gl.TEXTURE_2D, context.textures['dynamic_stroke_data']);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16UI, config.dynamic_stroke_texture_size, config.dynamic_stroke_texture_size, 0, gl.RGBA_INTEGER, gl.UNSIGNED_SHORT, null);
const resize_canvas = (entries) => {
// https://www.khronos.org/webgl/wiki/HandlingHighDPI
const entry = entries[0];

5
server/deserializer.js

@ -47,7 +47,7 @@ export function align(d, to) { @@ -47,7 +47,7 @@ export function align(d, to) {
export function event(d) {
const event = {};
event.type = u8(d);
event.type = u32(d);
switch (event.type) {
case EVENT.PREDRAW: {
@ -75,7 +75,6 @@ export function event(d) { @@ -75,7 +75,6 @@ export function event(d) {
const point_count = u16(d);
const width = u16(d);
const color = u32(d);
align(d, 4);
event.width = width;
event.color = color;
event.points = f32array(d, point_count * 2);
@ -108,4 +107,4 @@ export function event(d) { @@ -108,4 +107,4 @@ export function event(d) {
}
return event;
}
}

2
server/recv.js

@ -164,7 +164,7 @@ export async function handle_message(ws, d) { @@ -164,7 +164,7 @@ export async function handle_message(ws, d) {
const session = sessions[ws.data.session_id];
const desk_id = session.desk_id;
const message_type = des.u8(d);
const message_type = des.u32(d);
switch (message_type) {
case MESSAGE.FIRE: {

16
server/send.js

@ -94,7 +94,7 @@ export async function send_init(ws) { @@ -94,7 +94,7 @@ export async function send_init(ws) {
const desk = desks[desk_id];
let opcode = MESSAGE.INIT;
let size = 1 + 4 + 4 + 4 + 4 + 4 + 3; // opcode + user_id + lsn + event count + stroke count + user count + total_point_count + align on 4
let size = 4 + 4 + 4 + 4 + 4 + 4; // opcode + user_id + lsn + event count + stroke count + user count + total_point_count
let session = null;
if (session_id in sessions && sessions[session_id].desk_id == desk_id) {
@ -130,7 +130,7 @@ export async function send_init(ws) { @@ -130,7 +130,7 @@ export async function send_init(ws) {
const s = ser.create(size);
ser.u8(s, opcode);
ser.u32(s, opcode);
ser.u32(s, session.lsn);
if (opcode === MESSAGE.JOIN) {
@ -168,10 +168,10 @@ export function send_ack(ws, lsn) { @@ -168,10 +168,10 @@ export function send_ack(ws, lsn) {
return;
}
const size = 1 + 4; // opcode + lsn
const size = 4 + 4; // opcode + lsn
const s = ser.create(size);
ser.u8(s, MESSAGE.ACK);
ser.u32(s, MESSAGE.ACK);
ser.u32(s, lsn);
if (config.DEBUG_PRINT) console.log(`ack ${lsn} out`);
@ -184,9 +184,9 @@ export function send_fire(ws, event) { @@ -184,9 +184,9 @@ export function send_fire(ws, event) {
return;
}
const s = ser.create(1 + 4 + event_size(event));
const s = ser.create(4 + 4 + event_size(event));
ser.u8(s, MESSAGE.FIRE);
ser.u32(s, MESSAGE.FIRE);
ser.event(s, event);
ws.send(s.buffer);
@ -208,7 +208,7 @@ async function sync_session(session_id) { @@ -208,7 +208,7 @@ async function sync_session(session_id) {
return;
}
let size = 1 + 4 + 4; // opcode + sn + event count
let size = 4 + 4 + 4; // opcode + sn + event count
let count = desk.sn - session.sn;
if (count === 0) {
@ -223,7 +223,7 @@ async function sync_session(session_id) { @@ -223,7 +223,7 @@ async function sync_session(session_id) {
const s = ser.create(size);
ser.u8(s, MESSAGE.SYN);
ser.u32(s, MESSAGE.SYN);
ser.u32(s, desk.sn);
ser.u32(s, count);

Loading…
Cancel
Save