Skip to content

Commit

Permalink
feat: store ws client's current url (#123)
Browse files Browse the repository at this point in the history
  • Loading branch information
garrappachc authored Dec 21, 2024
1 parent 6c77c0e commit 363b1ae
Show file tree
Hide file tree
Showing 10 changed files with 259 additions and 158 deletions.
1 change: 1 addition & 0 deletions public/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ import './fade-scroll.js'
import './flash-message.js'
import './map-thumbnail.js'
import './notification.js'
import './navigation.js'
33 changes: 33 additions & 0 deletions public/js/navigation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import htmx from './htmx.js'

/**
* @typedef WsSend
* @type {function}
* @param {string} message
*/

/**
* @typedef SocketWrapper
* @type {object}
* @property {WsSend} send
*/

/** @type {SocketWrapper} */
let socket

export function reportNavigation(/** @type {string} */ path) {
const msg = JSON.stringify({ navigated: path })
socket?.send(msg)
}

/**
* @param {{detail: {socketWrapper: SocketWrapper}}} event
*/
htmx.on('htmx:wsOpen', event => {
socket = event.detail.socketWrapper
reportNavigation(window.location.pathname)
})

htmx.on('htmx:pushedIntoHistory', ({ detail }) => {
reportNavigation(detail.path)
})
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export default fp(
events.on('player:updated', ({ before, after }) => {
if (before.activeGame === undefined && after.activeGame !== undefined) {
app.gateway
.toPlayers(after.steamId)
.broadcast(async () => await GoToGame(after.activeGame!))
.to({ player: after.steamId })
.send(async () => await GoToGame(after.activeGame!))
}
})
},
Expand Down
54 changes: 39 additions & 15 deletions src/games/plugins/sync-clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,54 @@ import { GameScore } from '../views/html/game-score'
export default fp(async app => {
events.on('game:updated', async ({ before, after }) => {
if (before.state !== after.state) {
app.gateway.broadcast(async () => await GameStateIndicator({ game: after }))
app.gateway.broadcast(async actor => await ConnectInfo({ game: after, actor }))
app.gateway
.to({ url: `/games/${after.number}` })
.send(async () => await GameStateIndicator({ game: after }))
app.gateway
.to({ url: `/games/${after.number}` })
.send(async actor => await ConnectInfo({ game: after, actor }))

if ([GameState.launching, GameState.ended, GameState.interrupted].includes(after.state)) {
app.gateway.broadcast(async actor => await GameSlotList({ game: after, actor }))
app.gateway
.to({ url: `/games/${after.number}` })
.send(async actor => await GameSlotList({ game: after, actor }))
}
}

if (before.score?.blu !== after.score?.blu || before.score?.red !== after.score?.red) {
app.gateway.broadcast(async () => await GameScore({ game: after }))
app.gateway
.to({ url: `/games/${after.number}` })
.send(async () => await GameScore({ game: after }))
}

if (
before.connectString !== after.connectString ||
before.stvConnectString !== after.stvConnectString
) {
app.gateway.broadcast(async actor => await ConnectInfo({ game: after, actor }))
app.gateway
.to({ url: `/games/${after.number}` })
.send(async actor => await ConnectInfo({ game: after, actor }))
}

if (before.logsUrl !== after.logsUrl) {
app.gateway.broadcast(async () => await LogsLink({ game: after }))
app.gateway
.to({ url: `/games/${after.number}` })
.send(async () => await LogsLink({ game: after }))
}

if (before.demoUrl !== after.demoUrl) {
app.gateway.broadcast(async () => await DemoLink({ game: after }))
app.gateway
.to({ url: `/games/${after.number}` })
.send(async () => await DemoLink({ game: after }))
}

if (before.events.length < after.events.length) {
const n = before.events.length - after.events.length
const newEvents = after.events.slice(n)
for (const event of newEvents) {
app.gateway.broadcast(async () => await GameEventList.append({ game: after, event }))
app.gateway
.to({ url: `/games/${after.number}` })
.send(async () => await GameEventList.append({ game: after, event }))
}
}

Expand All @@ -62,8 +78,9 @@ export default fp(async app => {

if (beforeSlot.shouldJoinBy !== slot.shouldJoinBy) {
app.gateway
.toPlayers(slot.player)
.broadcast(async actor => await ConnectInfo({ game: after, actor }))
.to({ url: `/games/${after.number}` })
.to({ player: slot.player })
.send(async actor => await ConnectInfo({ game: after, actor }))
}
}),
)
Expand All @@ -76,9 +93,9 @@ export default fp(async app => {

events.on(
'game:updated',
whenGameEnds(async () => {
whenGameEnds(async ({ after }) => {
const cmp = await GamesLink()
app.gateway.broadcast(() => cmp)
app.gateway.to({ url: `/games/${after.number}` }).send(() => cmp)
}),
)

Expand All @@ -92,7 +109,10 @@ export default fp(async app => {
connectionStatus: playerConnectionStatus,
}),
)
app.gateway.toPlayers(player).broadcast(async actor => await ConnectInfo({ game, actor }))
app.gateway
.to({ url: `/games/${game.number}` })
.to({ player })
.send(async actor => await ConnectInfo({ game, actor }))
}),
)

Expand All @@ -102,11 +122,15 @@ export default fp(async app => {
throw new Error(`no such game slot: ${game.number} ${replacee}`)
}

app.gateway.broadcast(async actor => await GameSlot({ game, slot, actor }))
app.gateway
.to({ url: `/games/${game.number}` })
.send(async actor => await GameSlot({ game, slot, actor }))
})

events.on('game:playerReplaced', ({ game }) => {
// fixme refresh only one slot
app.gateway.broadcast(async actor => await GameSlotList({ game, actor }))
app.gateway
.to({ url: `/games/${game.number}` })
.send(async actor => await GameSlotList({ game, actor }))
})
})
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { nanoid } from 'nanoid'
import type { GameNumber } from '../../../database/models/game.model'
import { environment } from '../../../environment'

export function GoToGame(number: GameNumber) {
const id = nanoid()
return (
<div id="notify-container" hx-swap-oob="beforeend">
<script type="module" id={id}>{`
import htmx from '/js/htmx.js';
const path = '${environment.WEBSITE_URL}/games/${number}';
import { reportNavigation } from '/js/navigation.js';
const path = '/games/${number}';
htmx.ajax('get', path).then(() => {
history.pushState({}, '', path);
reportNavigation(path);
});
document.getElementById('${id}').remove();
`}</script>
Expand Down
4 changes: 0 additions & 4 deletions src/players/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import fp from 'fastify-plugin'
import { bySteamId } from './by-steam-id'
import { getPlayerGameCountOnClasses } from './get-player-game-count-on-classes'
import { update } from './update'
import { resolve } from 'node:path'

export const players = {
bySteamId,
Expand All @@ -12,9 +11,6 @@ export const players = {

export default fp(
async app => {
await app.register((await import('@fastify/autoload')).default, {
dir: resolve(import.meta.dirname, 'plugins'),
})
await app.register((await import('./routes')).default)
},
{ name: 'players' },
Expand Down
26 changes: 11 additions & 15 deletions src/queue/plugins/gateway-listeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default fp(
async function refreshTakenSlots(actor: SteamId64) {
const slots = await collections.queueSlots.find({ player: { $ne: null } }).toArray()
const cmps = await Promise.all(slots.map(async slot => await QueueSlot({ slot, actor })))
app.gateway.toPlayers(actor).broadcast(() => cmps)
app.gateway.to({ player: actor }).send(() => cmps)
}

app.gateway.on('queue:join', async (socket, slotId) => {
Expand All @@ -31,10 +31,10 @@ export default fp(

try {
const slots = await join(slotId, socket.player.steamId)
app.gateway.toPlayers(socket.player.steamId).broadcast(async () => await MapVote.enable())
app.gateway.to({ player: socket.player.steamId }).send(async () => await MapVote.enable())
app.gateway
.toPlayers(socket.player.steamId)
.broadcast(async () => await PreReadyUpButton.enable())
.to({ player: socket.player.steamId })
.send(async () => await PreReadyUpButton.enable())

if (slots.find(s => s.canMakeFriendsWith?.length)) {
await refreshTakenSlots(socket.player.steamId)
Expand All @@ -51,14 +51,10 @@ export default fp(

try {
const slot = await leave(socket.player.steamId)
app.gateway.toPlayers(socket.player.steamId).broadcast(async () => await MapVote.disable())
app.gateway.to({ player: socket.player.steamId }).send(async () => await MapVote.disable())
app.gateway
.toPlayers(socket.player.steamId)
.broadcast(async () => await PreReadyUpButton.disable())

app.gateway
.toPlayers(socket.player.steamId)
.broadcast(async actor => await MapVote({ actor }))
.to({ player: socket.player.steamId })
.send(async () => await PreReadyUpButton.disable())

if (slot.canMakeFriendsWith?.length) {
await refreshTakenSlots(socket.player.steamId)
Expand All @@ -67,7 +63,7 @@ export default fp(
const queueState = await getState()
if (queueState === QueueState.ready) {
const close = await ReadyUpDialog.close()
app.gateway.toPlayers(socket.player.steamId).broadcast(() => close)
app.gateway.to({ player: socket.player.steamId }).send(() => close)
}
} catch (error) {
logger.error(error)
Expand All @@ -81,7 +77,7 @@ export default fp(

try {
const [, close] = await Promise.all([readyUp(socket.player.steamId), ReadyUpDialog.close()])
app.gateway.toPlayers(socket.player.steamId).broadcast(() => close)
app.gateway.to({ player: socket.player.steamId }).send(() => close)
} catch (error) {
logger.error(error)
}
Expand All @@ -95,8 +91,8 @@ export default fp(
try {
await voteMap(socket.player.steamId, map)
app.gateway
.toPlayers(socket.player.steamId)
.broadcast(async actor => await MapVote({ actor }))
.to({ player: socket.player.steamId })
.send(async actor => await MapVote({ actor }))
} catch (error) {
logger.error(error)
}
Expand Down
54 changes: 29 additions & 25 deletions src/queue/plugins/sync-clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ export default fp(
async function syncAllSlots(...players: SteamId64[]) {
const slots = await collections.queueSlots.find().toArray()
slots.forEach(async slot => {
app.gateway.toPlayers(...players).broadcast(async actor => await QueueSlot({ slot, actor }))
app.gateway.to({ players }).send(async actor => await QueueSlot({ slot, actor }))
})
}

app.gateway.on('connected', async socket => {
app.gateway.on('ready', async socket => {
if (socket.currentUrl !== '/') {
return
}

const slots = await collections.queueSlots.find().toArray()
slots.forEach(async slot => {
socket.send(await QueueSlot({ slot, actor: socket.player?.steamId }))
Expand Down Expand Up @@ -62,14 +66,14 @@ export default fp(
safe(async ({ before, after }) => {
if (before.activeGame !== after.activeGame) {
const cmp = await RunningGameSnackbar({ gameNumber: after.activeGame })
app.gateway.toPlayers(after.steamId).broadcast(() => cmp)
app.gateway.to({ players: [after.steamId] }).send(() => cmp)
await syncAllSlots(after.steamId)
}

if (before.preReadyUntil !== after.preReadyUntil) {
app.gateway
.toPlayers(after.steamId)
.broadcast(async actor => await PreReadyUpButton({ actor }))
.to({ players: [after.steamId] })
.send(async actor => await PreReadyUpButton({ actor }))
}
}),
)
Expand Down Expand Up @@ -107,7 +111,7 @@ export default fp(
.filter(Boolean) as SteamId64[]

const show = await ReadyUpDialog.show()
app.gateway.toPlayers(...players).broadcast(() => show)
app.gateway.to({ players }).send(() => show)
}
}),
)
Expand All @@ -133,29 +137,29 @@ export default fp(
if (!slot) {
return
}
const actors = await collections.queueSlots
.find({ 'canMakeFriendsWith.0': { $exists: true }, player: { $ne: null } })
.toArray()
app.gateway
.toPlayers(...actors.map(a => a.player!))
.broadcast(async actor => await QueueSlot({ slot, actor }))
const players = (
await collections.queueSlots
.find({ 'canMakeFriendsWith.0': { $exists: true }, player: { $ne: null } })
.toArray()
).map(({ player }) => player!)
app.gateway.to({ players }).send(async actor => await QueueSlot({ slot, actor }))
}),
)

events.on(
'queue/friendship:updated',
safe(async ({ target }) => {
const actors = await collections.queueSlots
.find({ 'canMakeFriendsWith.0': { $exists: true }, player: { $ne: null } })
.toArray()
const players = (
await collections.queueSlots
.find({ 'canMakeFriendsWith.0': { $exists: true }, player: { $ne: null } })
.toArray()
).map(({ player }) => player!)

const slots = await collections.queueSlots
.find({ player: { $in: [target.before, target.after] } })
.toArray()
slots.forEach(slot => {
app.gateway
.toPlayers(...actors.map(a => a.player!))
.broadcast(async actor => await QueueSlot({ slot, actor }))
app.gateway.to({ players }).send(async actor => await QueueSlot({ slot, actor }))
})
}),
)
Expand All @@ -167,12 +171,12 @@ export default fp(
if (!slot) {
return
}
const actors = await collections.queueSlots
.find({ 'canMakeFriendsWith.0': { $exists: true }, player: { $ne: null } })
.toArray()
app.gateway
.toPlayers(...actors.map(a => a.player!))
.broadcast(async actor => await QueueSlot({ slot, actor }))
const players = (
await collections.queueSlots
.find({ 'canMakeFriendsWith.0': { $exists: true }, player: { $ne: null } })
.toArray()
).map(({ player }) => player!)
app.gateway.to({ players }).send(async actor => await QueueSlot({ slot, actor }))
}),
)

Expand All @@ -193,7 +197,7 @@ export default fp(

const refreshBanAlerts = async (player: SteamId64) => {
const cmp = await BanAlerts({ actor: player })
app.gateway.toPlayers(player).broadcast(() => cmp)
app.gateway.to({ players: [player] }).send(() => cmp)

setImmediate(async () => {
await syncAllSlots(player)
Expand Down
Loading

0 comments on commit 363b1ae

Please sign in to comment.