import { addRoute, setTitle, type RouteHandlerParams, type RouteHandlerReturn } from "@app/routing"; import authHtml from './pong.html?raw'; import io from 'socket.io-client'; import type { CSocket, GameMove, GameUpdate } from "./socket"; import { showError, showInfo } from "@app/toast"; // TODO: local game (2player -> server -> 2player : current setup) // TODO: tournament via remote (dedicated queu? idk) // // get the name of the machine used to connect declare module 'ft_state' { interface State { pongSock?: CSocket; } } document.addEventListener("ft:pageChange", (newUrl) => { // we are still on a pong page => keep the socket around ! if (newUrl.detail.startsWith('/app/pong') || newUrl.detail.startsWith('/pong')) return; if (window.__state.pongSock !== undefined) window.__state.pongSock.close(); window.__state.pongSock = undefined; }); export function getSocket(): CSocket { if (window.__state.pongSock === undefined) window.__state.pongSock = io(window.location.host, { path: "/api/pong/socket.io/" }) as any as CSocket; return window.__state.pongSock; } function pongClient(_url: string, _args: RouteHandlerParams): RouteHandlerReturn { setTitle('Pong Game Page'); return { html: authHtml, postInsert: async (app) => { const checkbox = document.getElementById("modeToggle") as HTMLInputElement; const label = document.getElementById("toggleLabel") as HTMLSpanElement; const track = document.getElementById("toggleTrack") as HTMLDivElement; const knob = document.getElementById("toggleKnob") as HTMLSpanElement; checkbox.addEventListener("change", () => { // web vs local if (checkbox.checked) { label.textContent = "Web"; track.classList.replace("bg-gray-300", "bg-blue-600"); knob.classList.add("translate-x-7"); } else { label.textContent = "Local"; track.classList.replace("bg-blue-600", "bg-gray-300"); knob.classList.remove("translate-x-7"); } }); const batLeft = document.querySelector("#batleft"); const batRight = document.querySelector("#batright"); const ball = document.querySelector("#ball"); const score = document.querySelector("#score-board"); if (!batLeft || !batRight || !ball || !score) return showError('fatal error'); let socket = getSocket(); let currentGame: GameUpdate | null = null; // keys handler const keys: Record = {}; document.addEventListener("keydown", (e) => { keys[e.key.toLowerCase()] = true; }); document.addEventListener("keyup", (e) => { keys[e.key.toLowerCase()] = false; }); setInterval(() => { // key sender if (currentGame === null) return; let packet: GameMove = { move: null, moveRight: null, } if ((keys['w'] !== keys['s'])) { packet.move = keys['w'] ? 'up' : 'down'; } if (currentGame.local && (keys['o'] !== keys['l'])) { packet.moveRight = keys['o'] ? 'up' : 'down'; } socket.emit('gameMove', packet); }, 1000 / 60); const render = (state: GameUpdate) => { currentGame = state; //batLeft.style.transform = `translateY(${state.left.paddle.y}px) translateX(${state.left.paddle.x}px)`; batLeft.style.top = `${state.left.paddle.y}px`; batLeft.style.left = `${state.left.paddle.x}px`; batLeft.style.width = `${state.left.paddle.width}px`; batLeft.style.height = `${state.left.paddle.height}px`; //batRight.style.transform = `translateY(${state.right.paddle.y}px) translateX(-${state.left.paddle.x}px)`; batRight.style.top = `${state.right.paddle.y}px`; batRight.style.left = `${state.right.paddle.x}px`; batRight.style.width = `${state.right.paddle.width}px`; batRight.style.height = `${state.right.paddle.height}px`; ball.style.transform = `translateX(${state.ball.x - state.ball.size}px) translateY(${state.ball.y - state.ball.size}px)`; ball.style.height = `${state.ball.size * 2}px`; ball.style.width = `${state.ball.size * 2}px`; score.innerText = `${state.left.score} | ${state.right.score}` } socket.on('gameEnd', () => currentGame = null); socket.on('gameUpdate', (state: GameUpdate) => render(state)); socket.on('newGame', (state: GameUpdate) => render(state)); socket.on('updateInformation', (e) => showInfo(`UpdateInformation: t=${e.totalUser};q=${e.inQueue}`)); socket.on('queueEvent', (e) => showInfo(`QueueEvent: ${e}`)); //socket.emit('enqueue'); socket.emit('localGame'); } } }; addRoute('/pong', pongClient);