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

View file

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

View file

@ -105,11 +105,58 @@ export function session_id() {
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.
*/
export function servertime_now_ns() {
servertime_now_ns() {
const now_ms = performance.now()
const delta_ns = ms_ns * (now_ms - toffset_ms)
return servertime + delta_ns
const delta_ns = ms_ns * (now_ms - this.toffset_ms)
return this.servertime + delta_ns
}
send(type, value) {
// console.debug('sending', value)
this.socket.send(JSON.stringify({ type, value }))
}
}