fix connection time syncing handling

Share the time syncing code, and while we're at it wrap the whole
connection thing in a class.  Makes it easier to pass the connection
around & later on add more advanced handler registration if we want to.
This commit is contained in:
ducklet 2021-02-02 21:02:30 +01:00
parent f900542765
commit 9e7000054b
3 changed files with 132 additions and 112 deletions

View file

@ -12,6 +12,7 @@ const storage = window.sessionStorage
import { import {
clear, clear,
Connection,
isEmpty, isEmpty,
isString, isString,
ms_ns, ms_ns,
@ -20,7 +21,6 @@ import {
on, on,
q, q,
s_ns, s_ns,
servertime_now_ns,
session_id, session_id,
session_id_from_url, session_id_from_url,
session_url, session_url,
@ -32,8 +32,7 @@ const buzzer_key = {
} }
let socket, let socket,
servertime, conn,
toffset_ms,
clients = [], clients = [],
me, me,
session_key session_key
@ -66,11 +65,6 @@ function setup_url() {
location.hash = sid location.hash = sid
} }
function send(type, value) {
// console.debug('sending', value)
socket.send(JSON.stringify({ type, value }))
}
const player_list = q("#info ul") const player_list = q("#info ul")
function redraw_clients(me, clients) { function redraw_clients(me, clients) {
if (!me) { if (!me) {
@ -98,7 +92,7 @@ function highlight(client_id, until_ns) {
if (highlights[client_id]) { if (highlights[client_id]) {
return return
} }
const timeout_ms = (until_ns - servertime_now_ns()) / ms_ns const timeout_ms = (until_ns - conn.servertime_now_ns()) / ms_ns
if (timeout_ms <= 10) { if (timeout_ms <= 10) {
console.warn("That highlight timeout was ridiculously low:", client_id, timeout_ms) console.warn("That highlight timeout was ridiculously low:", client_id, timeout_ms)
return return
@ -144,7 +138,7 @@ function setup_ui() {
on("keydown", (event) => { on("keydown", (event) => {
if (!buzzing && event.keyCode === buzzer_key.code) { if (!buzzing && event.keyCode === buzzer_key.code) {
buzzing = true buzzing = true
send("buzz", servertime_now_ns()) conn.send("buzz", conn.servertime_now_ns())
show("active") show("active")
hide("ready") hide("ready")
} }
@ -220,48 +214,37 @@ function enable_admin_ui() {
function set_name(name) { function set_name(name) {
storage["my_name"] = name storage["my_name"] = name
send("name", name) conn.send("name", name)
} }
function setup_ws() { function setup_ws() {
const sid = session_id() const sid = session_id()
const credentials = { id: storage["my_uid"], key: storage["my_key"] } const credentials = { id: storage["my_uid"], key: storage["my_key"] }
socket = new WebSocket(session_url(sid)) conn = new Connection()
socket.addEventListener("open", function (event) { conn.on("helo", () => {
if (sid === storage["my_sid"]) { if (sid === storage["my_sid"]) {
send("login", credentials) conn.send("login", credentials)
} }
set_name(q("#username").value) set_name(q("#username").value)
}) })
socket.addEventListener("message", function (event) { conn.on("id", ({ value }) => {
const msg = JSON.parse(event.data)
const { type, value } = msg
if (type === "time") {
servertime = value.time
toffset_ms = performance.now()
} else if (type === "id") {
me = value me = value
storage["my_uid"] = me.id storage["my_uid"] = me.id
storage["my_key"] = me.key storage["my_key"] = me.key
storage["my_sid"] = session_id_from_url(me.path) storage["my_sid"] = session_id_from_url(me.path)
redraw_clients(me, clients) redraw_clients(me, clients)
} else if (type === "session_key") { })
session_key = value conn.on("buzz", ({ value }) => {
storage["session_path"] = session_key.path const { time: buzztime_ns, client: client_id } = value
storage["session_key"] = session_key.key
disable_player_ui()
enable_admin_ui()
} else if (type === "buzz") {
const buzztime_ns = value.time
const client_id = value.client
const duration_ns = 3 * s_ns const duration_ns = 3 * s_ns
const until_ns = buzztime_ns + duration_ns const until_ns = buzztime_ns + duration_ns
highlight(client_id, until_ns) highlight(client_id, until_ns)
} else if (type === "clients") { })
conn.on("clients", ({ value }) => {
clients = value.clients clients = value.clients
redraw_clients(me, clients) redraw_clients(me, clients)
} else if (type === "client") { })
const client = value.client conn.on("client", ({ value: { client } }) => {
const idx = clients.findIndex((c) => c.id === client.id) const idx = clients.findIndex((c) => c.id === client.id)
if (idx >= 0) { if (idx >= 0) {
clients[idx] = client clients[idx] = client
@ -269,17 +252,24 @@ function setup_ws() {
clients.push(client) clients.push(client)
} }
redraw_clients(me, clients) redraw_clients(me, clients)
} else if (type === "control") { })
conn.on("session_key", ({ value }) => {
session_key = value
storage["session_path"] = session_key.path
storage["session_key"] = session_key.key
disable_player_ui()
enable_admin_ui()
})
conn.on("control", ({ value }) => {
// ignore // ignore
} else if (type === "error") { })
console.error(`Error: ${value.reason}`) conn.on("error", (msg) => {
console.error(`Error: ${msg.value.reason}`)
const errorbox = q("#error") const errorbox = q("#error")
q("code", errorbox).textContent = JSON.stringify(msg, null, 2) q("code", errorbox).textContent = JSON.stringify(msg, null, 2)
q("body").classList.add("error") q("body").classList.add("error")
} else {
console.error(`Unknown message: ${event.data}`)
}
}) })
conn.connect(session_url(sid))
} }
setup_url() setup_url()

View file

@ -6,18 +6,16 @@ const performance = window.performance
import { import {
clear, clear,
Connection,
ms_ns, ms_ns,
node, node,
q, q,
s_ns, s_ns,
servertime_now_ns,
session_id, session_id,
session_url, session_url,
} from "./shared.js" } from "./shared.js"
let socket, let conn,
servertime,
toffset_ms,
clients = {}, clients = {},
me me
@ -26,11 +24,6 @@ function setup_url() {
location.hash = sid location.hash = sid
} }
function send(type, value) {
// console.debug('sending', value)
socket.send(JSON.stringify({ type, value }))
}
function prettynum(n) { function prettynum(n) {
let i = Math.abs(n) | 0 let i = Math.abs(n) | 0
let s = [] let s = []
@ -84,7 +77,7 @@ function redraw_clients(me, clients) {
q(".name", player_el).textContent = c.name q(".name", player_el).textContent = c.name
q(".points.fg", player_el).textContent = prettynum(c.points) q(".points.fg", player_el).textContent = prettynum(c.points)
q(".points.bg", player_el).textContent = prettynum(c.points) q(".points.bg", player_el).textContent = prettynum(c.points)
q(".tokens", player_el).textContent = '🔴 '.repeat(c.tokens) q(".tokens", player_el).textContent = "🔴 ".repeat(c.tokens)
} }
} }
@ -93,7 +86,7 @@ function highlight(client_id, until_ns) {
if (highlighted) { if (highlighted) {
return return
} }
const timeout_ms = (until_ns - servertime_now_ns()) / ms_ns const timeout_ms = (until_ns - conn.servertime_now_ns()) / ms_ns
if (timeout_ms <= 10) { if (timeout_ms <= 10) {
console.warn("That highlight timeout was ridiculously low:", client_id, timeout_ms) console.warn("That highlight timeout was ridiculously low:", client_id, timeout_ms)
return return
@ -113,42 +106,32 @@ function highlight(client_id, until_ns) {
function setup_ws() { function setup_ws() {
const sid = session_id() const sid = session_id()
socket = new WebSocket(session_url(sid)) conn = new Connection()
socket.addEventListener("open", function (event) { conn.on("helo", () => {
send("name", "Monitor") conn.send("name", "Monitor")
}) })
socket.addEventListener("message", function (event) { conn.on("id", ({ value }) => {
const msg = JSON.parse(event.data)
const { type, value } = msg
if (msg.type === "time") {
servertime = value.time
toffset_ms = performance.now()
} else if (msg.type === "id") {
me = value me = value
redraw_clients(me, clients) redraw_clients(me, clients)
} else if (msg.type === "buzz") { })
const buzztime_ns = value.time conn.on("buzz", ({ value }) => {
const client_id = value.client const { time: buzztime_ns, client: client_id } = value
const duration_ns = 12 * s_ns const duration_ns = 12 * s_ns
const until_ns = buzztime_ns + duration_ns const until_ns = buzztime_ns + duration_ns
highlight(client_id, until_ns) highlight(client_id, until_ns)
} else if (msg.type === "clients") { })
conn.on("clients", ({ value }) => {
clients = Object.fromEntries(value.clients.map((c) => [c.id, c])) clients = Object.fromEntries(value.clients.map((c) => [c.id, c]))
redraw_clients(me, clients) redraw_clients(me, clients)
} else if (msg.type === "client") { })
const client = value.client conn.on("client", ({ value: { client } }) => {
clients[client.id] = client clients[client.id] = client
redraw_clients(me, clients) redraw_clients(me, clients)
} else if (type === "control") {
// ignore
} else if (type === "session_key") {
// ignore
} else if (type === "error") {
console.error(`Error: ${value.reason}`)
} else {
console.error(`Unknown message: ${event.data}`)
}
}) })
conn.on("control", ({ value }) => {
// ...
})
conn.connect(session_url(sid))
} }
setup_url() setup_url()

View file

@ -105,11 +105,58 @@ export function session_id() {
return match ? match[1] : null return match ? match[1] : null
} }
/** export class Connection {
constructor() {
this.socket = null
this.toffset_ms = null
this.servertime = null
this.handlers = {
time: this._handler_time.bind(this),
error: this._handler_error.bind(this),
}
}
_handler_time({ value: { time } }) {
this.servertime = time
this.toffset_ms = performance.now()
}
_handler_error({ value: { reason } }) {
console.error(`Error: ${reason}`)
}
connect(url) {
this.socket = new WebSocket(url)
this.socket.addEventListener("open", (event) => {
if ("helo" in this.handlers) {
this.handlers["helo"](event)
}
})
this.socket.addEventListener("message", (event) => {
const msg = JSON.parse(event.data)
if (msg.type in this.handlers) {
this.handlers[msg.type](msg)
} else {
console.error(`Unhandled message: ${event.data}`)
}
})
}
on(type, callback) {
this.handlers[type] = callback
}
/**
* Guess the exact current server time. * Guess the exact current server time.
*/ */
export function servertime_now_ns() { servertime_now_ns() {
const now_ms = performance.now() const now_ms = performance.now()
const delta_ns = ms_ns * (now_ms - toffset_ms) const delta_ns = ms_ns * (now_ms - this.toffset_ms)
return servertime + delta_ns return this.servertime + delta_ns
}
send(type, value) {
// console.debug('sending', value)
this.socket.send(JSON.stringify({ type, value }))
}
} }