diff --git a/src/endpoints/guess_letter.ts b/src/endpoints/guess_letter.ts index a157a47..22b68aa 100644 --- a/src/endpoints/guess_letter.ts +++ b/src/endpoints/guess_letter.ts @@ -25,15 +25,42 @@ const route: ServerRoute = { if (data.key[guess] != null) { data.current = addLetter(data.key, data.current, guess); + /* + The player(s) won the game, so we want to tell the overlay(s) and + then respond to the request. + */ if (data.current == data.solution) { - return `Congrats! You won. Merry Chatmanmas!`; + request.server.app.io.to(channel).emit(`finish`, { + win: true, + solution: data.solution, + }); + return `Congrats! You won. Merry Chatmanmas! Answer: ${data.current}`; }; } else { data.incorrect++; }; + + + /* + The guess caused the player(s) to lose, so we want to tell all of the + overlays and then the user. + */ if (data.incorrect >= config.game.max_incorrect) { + request.server.app.io.to(channel).emit(`finish`, { + win: false, + solution: data.solution, + }); return `Oop, you ded. Answer: ${data.solution}`; }; + + // Update all the overlays for the channel + request.server.app.io.to(channel).emit(`update`, { + current: data.current, + incorrect: { + current: data.incorrect, + max: config.game.max_incorrect, + }, + }); return `${data.current} (incorrect: ${data.incorrect}/${config.game.max_incorrect})`; }, }; diff --git a/src/endpoints/new_game.ts b/src/endpoints/new_game.ts index 2d8c497..c24c1a4 100644 --- a/src/endpoints/new_game.ts +++ b/src/endpoints/new_game.ts @@ -38,6 +38,16 @@ const route: ServerRoute = { data.incorrect = 0; data.key = convertToKey(spaced); + // Tell the overlay(s) to start a new game + request.server.app.io.to(channel).emit(`state`, { + active: true, + current: data.current, + incorrect: { + current: data.incorrect, + max: config.game.max_incorrect, + }, + }) + return `${data.current} (incorrect: ${data.incorrect}/${config.game.max_incorrect})`; }, }; diff --git a/src/main.ts b/src/main.ts index 6bd926f..630ea24 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,8 +3,9 @@ import "module-alias/register"; import { JSONDatabase } from "./utils/database/json"; -import { Server, Request } from "@hapi/hapi"; +import { Server as wsServer } from "socket.io"; import { loadConfig } from "./utils/config"; +import { Server } from "@hapi/hapi"; import inert from "@hapi/inert"; import { Logger } from "tslog"; import path from "path"; @@ -33,6 +34,9 @@ async function init() { port: config.server.port, routes: { cors: true, + files: { + relativeTo: path.join(process.cwd(), `site`), + }, }, }); @@ -49,6 +53,40 @@ async function init() { server.route(route); }; + /* + This is the Socket IO server instantiation, this is used by the overlays to + get updates in real-time without needing to have a polling request unless + it fails to upgrade to websockets. + */ + const io = new wsServer( server.listener ); + server.app.io = io; + + io.on(`connection`, (socket) => { + log.debug(`New connection from: ${socket.id}`); + + /* + The overlay is requesting state info for a specific channel, give it and + add the socket to the channel's room. + */ + socket.on(`state`, async (channel: string) => { + let data = await database.getChannel(channel); + + if (data == null) { + return socket.emit(`state`, { active: false }); + }; + + socket.join(channel); + return socket.emit(`state`, { + active: data.current != null, + incorrect: { + current: data.incorrect, + max: config.game.max_incorrect, + }, + current: data.current, + }); + }); + }); + server.start().then(() => { log.info(`Server listening on ${server.info.uri}`); }); diff --git a/src/modules/hapi.ts b/src/modules/hapi.ts new file mode 100644 index 0000000..e1709ac --- /dev/null +++ b/src/modules/hapi.ts @@ -0,0 +1,7 @@ +import { Server as wsServer } from "socket.io"; + +declare module "@hapi/hapi" { + interface ServerApplicationState { + io: wsServer; + } +} \ No newline at end of file