diff --git a/README.md b/README.md
index aa482a4..6fad53b 100644
--- a/README.md
+++ b/README.md
@@ -20,8 +20,9 @@ Release:
- Be able to have multiple "current" strokes per player. In case of bad internet this can happen!
* Missing features I do not consider bonus
+ Player pointers
- - Player list
- - Follow player
+ + Pretty player pointers
+ + Player list
+ + Follow player
- Eraser
- Line drawing
- Follow player (like Ligma)
diff --git a/client/aux.js b/client/aux.js
index 7b81a18..39acf24 100644
--- a/client/aux.js
+++ b/client/aux.js
@@ -41,7 +41,12 @@ function event_size(event) {
size += 4 * 2;
break;
}
-
+
+ case EVENT.MOVE_CANVAS: {
+ size += 4 * 2 + 4;
+ break;
+ }
+
case EVENT.LEAVE:
case EVENT.CLEAR: {
break;
@@ -161,12 +166,45 @@ function HTML(html) {
return template.content.firstChild;
}
+function toggle_follow_player(state, player_id) {
+ document.querySelectorAll('.player-list .player').forEach(p => p.classList.remove('following'));
+
+ if (state.following_player === null) {
+ state.following_player = player_id;
+ } else {
+ if (player_id === state.following_player) {
+ state.following_player = null;
+ } else {
+ state.following_player = player_id;
+ }
+ }
+
+ const player_element = document.querySelector(`.player-list .player[data-player-id="${state.following_player}"]`);
+ if (player_element) player_element.classList.add('following');
+
+ send_follow(state.following_player);
+}
+
function insert_player_cursor(state, player_id) {
const color = random_bright_color_from_seed(parseInt(player_id));
const path_copy = state.cursor_path.cloneNode();
+
path_copy.style.fill = color;
+
const cursor = HTML(``);
+ const player = HTML(`
+
diff --git a/client/index.js b/client/index.js
index f4a6633..73242ea 100644
--- a/client/index.js
+++ b/client/index.js
@@ -39,6 +39,7 @@ const EVENT = Object.freeze({
MOVE_CURSOR: 14,
MOVE_SCREEN: 15,
LEAVE: 16,
+ MOVE_CANVAS: 17,
STROKE: 20,
RULER: 21, // gets re-written with EVENT.STROKE before sending to server
@@ -59,6 +60,7 @@ const MESSAGE = Object.freeze({
FULL: 103,
FIRE: 104,
JOIN: 105,
+ FOLLOW: 106,
});
// Source:
@@ -223,6 +225,7 @@ function main() {
'rdp_cache': {},
'stats': {},
+ 'following_player': null,
};
const context = {
diff --git a/client/math.js b/client/math.js
index 95b8d97..e5db202 100644
--- a/client/math.js
+++ b/client/math.js
@@ -11,8 +11,8 @@ function screen_to_canvas(state, p) {
}
function canvas_to_screen(state, p) {
- const xs = p.x * state.canvas.zoom + state.canvas.offset.x;
- const ys = p.y * state.canvas.zoom + state.canvas.offset.y;
+ const xs = (p.x * state.canvas.zoom + state.canvas.offset.x) / window.devicePixelRatio;
+ const ys = (p.y * state.canvas.zoom + state.canvas.offset.y) / window.devicePixelRatio;
return {'x': xs, 'y': ys};
}
@@ -352,7 +352,7 @@ function mulberry32(seed) {
function random_bright_color_from_seed(seed) {
const h = Math.round(mulberry32(seed) * 360);
- const s = 75;
+ const s = 25;
const l = 50;
return `hsl(${h}deg ${s}% ${l}%)`;
diff --git a/client/webgl_draw.js b/client/webgl_draw.js
index 0f41335..9b4a547 100644
--- a/client/webgl_draw.js
+++ b/client/webgl_draw.js
@@ -59,6 +59,11 @@ function draw_html(state) {
if (!player.online && player_cursor_element !== null) {
player_cursor_element.remove();
+ const player_list_item = document.querySelector(`.player-list .player[data-player-id="${player_id}"]`);
+ if (player_list_item) player_list_item.remove();
+ if (document.querySelector('.player-list').childElementCount === 0) {
+ document.querySelector('.player-list').classList.add('vhide');
+ }
}
if (player_cursor_element && player.online) {
diff --git a/client/webgl_listeners.js b/client/webgl_listeners.js
index 6070649..c41051a 100644
--- a/client/webgl_listeners.js
+++ b/client/webgl_listeners.js
@@ -99,7 +99,7 @@ function cancel(e) {
function zenmode() {
document.querySelector('.pallete-wrapper').classList.toggle('hidden');
- document.querySelector('.sizer-wrapper').classList.toggle('hidden');
+ document.querySelector('.top-wrapper').classList.toggle('hidden');
}
async function paste(e, state, context) {
@@ -228,6 +228,13 @@ function mousemove(e, state, context) {
if (state.moving) {
state.canvas.offset.x += e.movementX;
state.canvas.offset.y += e.movementY;
+
+ // If we are moving our canvas, we don't need to follow anymore
+ if (state.following_player !== null) {
+ toggle_follow_player(state, state.following_player);
+ }
+
+ fire_event(state, movecanvas_event(state));
do_draw = true;
}
@@ -333,6 +340,13 @@ function wheel(e, state, context) {
return;
}
+ // If we are moving our canvas, we don't need to follow anymore
+ if (state.following_player !== null) {
+ toggle_follow_player(state, state.following_player);
+ }
+
+ fire_event(state, movecanvas_event(state));
+
const zoom_offset_x = Math.round((dz * old_zoom) * canvasp.x);
const zoom_offset_y = Math.round((dz * old_zoom) * canvasp.y);
@@ -420,6 +434,7 @@ function touchmove(e, state, context) {
state.touch.waiting_for_second_finger = false;
}
+
geometry_add_point(state, context, state.me, canvasp);
fire_event(state, predraw_event(canvasp.x, canvasp.y));
@@ -479,9 +494,16 @@ function touchmove(e, state, context) {
state.canvas.offset.y -= zoom_offset_y;
}
+ // If we are moving our canvas, we don't need to follow anymore
+ if (state.following_player !== null) {
+ toggle_follow_player(state, state.following_player);
+ }
+
state.touch.first_finger_position = first_finger_position;
state.touch.second_finger_position = second_finger_position;
+ fire_event(state, movecanvas_event(state));
+
schedule_draw(state, context);
return;
@@ -492,9 +514,6 @@ function touchend(e, state, context) {
for (const touch of e.changedTouches) {
if (state.touch.drawing) {
if (state.touch.ids[0] == touch.identifier) {
- // const event = stroke_event();
- // await queue_event(event);
-
const stroke = geometry_prepare_stroke(state);
if (stroke) {
diff --git a/server/deserializer.js b/server/deserializer.js
index 19ada58..9e23e2b 100644
--- a/server/deserializer.js
+++ b/server/deserializer.js
@@ -62,6 +62,13 @@ export function event(d) {
break;
}
+ case EVENT.MOVE_CANVAS: {
+ event.offset_x = u32(d);
+ event.offset_y = u32(d);
+ event.zoom = f32(d);
+ break;
+ }
+
case EVENT.CLEAR: {
break;
}
diff --git a/server/enums.js b/server/enums.js
index fbd7e2e..121d98f 100644
--- a/server/enums.js
+++ b/server/enums.js
@@ -12,6 +12,7 @@ export const EVENT = Object.freeze({
MOVE_CURSOR: 14,
MOVE_SCREEN: 15,
LEAVE: 16,
+ MOVE_CANVAS: 17,
STROKE: 20,
UNDO: 30,
@@ -28,4 +29,5 @@ export const MESSAGE = Object.freeze({
FULL: 103,
FIRE: 104,
JOIN: 105,
+ FOLLOW: 106,
});
diff --git a/server/recv.js b/server/recv.js
index e1ca4b8..e9ade78 100644
--- a/server/recv.js
+++ b/server/recv.js
@@ -90,6 +90,20 @@ function recv_fire(d, session) {
send.fire_event(session, event);
}
+function recv_follow(d, session) {
+ const user_id = des.u32(d);
+
+ if (config.DEBUG_PRINT) console.log(`follow ${user_id} in`);
+
+ if (user_id === 4294967295) {
+ // unfollow
+ session.follow = null;
+ } else {
+ // follow
+ session.follow = user_id;
+ }
+}
+
function handle_event(session, event) {
switch (event.type) {
case EVENT.STROKE: {
@@ -166,6 +180,11 @@ export async function handle_message(ws, d) {
break;
}
+ case MESSAGE.FOLLOW: {
+ recv_follow(d, session);
+ break;
+ }
+
default: {
console.error('fuck');
console.trace();
diff --git a/server/send.js b/server/send.js
index f677604..504d984 100644
--- a/server/send.js
+++ b/server/send.js
@@ -15,7 +15,12 @@ function event_size(event) {
size += 4 * 2;
break;
}
-
+
+ case EVENT.MOVE_CANVAS: {
+ size += 4 * 2 + 4;
+ break;
+ }
+
case EVENT.LEAVE:
case EVENT.CLEAR: {
break;
@@ -210,6 +215,11 @@ export function fire_event(from_session, event) {
continue;
}
+ if (event.type === EVENT.MOVE_CANVAS && other.follow !== from_session.id) {
+ // Do not spam canvas move events to those who don't follow us
+ continue;
+ }
+
send_fire(other.ws, event);
}
}
diff --git a/server/serializer.js b/server/serializer.js
index fdd27f4..936e057 100644
--- a/server/serializer.js
+++ b/server/serializer.js
@@ -60,6 +60,13 @@ export function event(s, event) {
break;
}
+ case EVENT.MOVE_CANVAS: {
+ u32(s, event.offset_x);
+ u32(s, event.offset_y);
+ f32(s, event.zoom);
+ break;
+ }
+
case EVENT.LEAVE:
case EVENT.CLEAR: {
break;
diff --git a/server/storage.js b/server/storage.js
index 03d17a0..dd959f4 100644
--- a/server/storage.js
+++ b/server/storage.js
@@ -132,6 +132,7 @@ export function startup() {
session.state = SESSION.CLOSED;
session.ws = null;
session.sync_attempts = 0;
+ session.follow = null;
sessions[session.id] = session;
}
}