diff --git a/frontend/src/pages/pong/broadcastMsg.ts b/frontend/src/pages/pong/broadcastMsg.ts index ae049a5..2443278 100644 --- a/frontend/src/pages/pong/broadcastMsg.ts +++ b/frontend/src/pages/pong/broadcastMsg.ts @@ -9,7 +9,7 @@ import { getUser } from "@app/auth"; * @param msgCommand */ export function broadcastMsg (socket: Socket, msgCommand: string[]): void { - let msgText = msgCommand[1] ?? ""; + let msgText = msgCommand[1] ?? ""; addPongMessage(msgText); const user = getUser(); if (user && socket?.connected) { diff --git a/frontend/src/pages/pong/pong.html b/frontend/src/pages/pong/pong.html index c46c9cd..0c9d2c3 100644 --- a/frontend/src/pages/pong/pong.html +++ b/frontend/src/pages/pong/pong.html @@ -1,25 +1,26 @@
-
- -

- Pong Box -


- -
System: connecting ...
- -
-
-
-
-
-
-
-
-
-
-
+
+ +
+ +
+

+ Pong Box +


+ +
System: connecting ...
+ +
+
+
+
+
+
+
+
+
+
+
+
+
- - - - diff --git a/frontend/src/pages/pong/pong.ts b/frontend/src/pages/pong/pong.ts index f960665..8a2a40b 100644 --- a/frontend/src/pages/pong/pong.ts +++ b/frontend/src/pages/pong/pong.ts @@ -7,6 +7,7 @@ import io, { Socket } from 'socket.io-client'; import { addPongMessage } from './addPongMessage'; import { isLoggedIn } from './isLoggedIn'; import type { ClientMessage, ClientProfil } from './types_front'; +import { isNullish } from "@app/utils"; export const color = { red: 'color: red;', @@ -21,7 +22,8 @@ const machineHostName = window.location.hostname; console.log('connect to login at %chttps://' + machineHostName + ':8888/app/login',color.yellow); export let __socket: Socket | undefined = undefined; -document.addEventListener('ft:pageChange', () => { + +document.addEventListener('ft:pageChange', () => { // dont regen socket on page change from forward/backward navigation arrows if (__socket !== undefined) __socket.close(); __socket = undefined; @@ -30,9 +32,8 @@ document.addEventListener('ft:pageChange', () => { export function getSocket(): Socket { let addressHost = `wss://${machineHostName}:8888`; - // let addressHost = `wss://localhost:8888`; - if (__socket === undefined) + if (__socket === undefined) __socket = io(addressHost, { path: "/api/pong/socket.io/", secure: false, @@ -41,19 +42,16 @@ export function getSocket(): Socket { return __socket; }; - function waitSocketConnected(socket: Socket): Promise { - return new Promise(resolve => { - if (socket.connected) return resolve(); - socket.on("connect", () => resolve()); - }); + return new Promise(resolve => { + if (socket.connected) return resolve(); + socket.on("connect", () => resolve()); + }); }; - async function whoami(socket: Socket) { try { const chatWindow = document.getElementById("t-chatbox") as HTMLDivElement; - const loggedIn = isLoggedIn(); const res = await client.guestLogin(); switch (res.kind) { @@ -80,148 +78,172 @@ async function whoami(socket: Socket) { } }; - - - - -function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn { - +function pongClient(_url: string, _args: RouteHandlerParams): RouteHandlerReturn { let socket = getSocket(); - /** - * on connection plays this part - */ - socket.on("connect", async () => { const systemWindow = document.getElementById('system-box') as HTMLDivElement; await waitSocketConnected(socket); console.log("I AM Connected to the server:", socket.id); - const message = { - command: "", - destination: 'system-info', - type: "chat", - user: getUser()?.name, - token: document.cookie ?? "", - text: " has Just ARRIVED in the chat", - timestamp: Date.now(), - SenderWindowID: socket.id, - }; - socket.emit('message', JSON.stringify(message)); + // const message = { + // command: "", + // destination: 'system-info', + // type: "chat", + // user: getUser()?.name, + // token: document.cookie ?? "", + // text: " has Just ARRIVED in the chat", + // timestamp: Date.now(), + // SenderWindowID: socket.id, + // }; + // socket.emit('message', JSON.stringify(message)); const messageElement = document.createElement("div"); - messageElement.textContent = `${message.user}: is connected au server`; - systemWindow.appendChild(messageElement); + // messageElement.textContent = `${message.user}: is connected au server`; + messageElement.textContent = `${getUser()?.name ?? "unkown user"}: is connected au server`; + systemWindow.appendChild(messageElement); systemWindow.scrollTop = systemWindow.scrollHeight; }); - + // Queu handler + async function joinQueu(socket : Socket) { + try { + const res = await client.guestLogin(); + + switch (res.kind) { + case 'success': { + let user = await updateUser(); + + if (user === null) + return showError('Failed to get user: no user ?'); + socket.emit('queuJoin', user.id); + console.log('queu join sent for : ', user.id); + break; + } + case 'failed': { + showError(`Failed to Join Game Queu: ${res.msg}`); + console.log('Failed to Join Game Queu'); + } + } + } catch (err ) { + showError(`Failed to Join Game Queu`); + console.log('Failed to Join Game Queu'); + } + } + + // keys handler const keys: Record = {}; document.addEventListener("keydown", (e) => { keys[e.key.toLowerCase()] = true; }); - document.addEventListener("keyup", (e) => { keys[e.key.toLowerCase()] = false; }); - - setInterval(() => { - if (keys['w']) { - socket.emit("batmove_Left", "up"); - console.log('north key pressed - emit batmove_Left up'); + setInterval(() => { // key sender + if ((keys['w'] || keys['s']) && !(keys['w'] && keys['s'])) { // exclusive or to filter requests + if (keys['w']) { + socket.emit("batmove_Left", "up"); + console.log('north key pressed - emit batmove_Left up'); + } + if (keys['s']) { + socket.emit("batmove_Left", "down"); + console.log('south key pressed - emit batmove_Left down'); + } } - if (keys['s']) { - socket.emit("batmove_Left", "down"); - console.log('south key pressed - emit batmove_Left down'); + if ((keys['p'] || keys['l']) && !(keys['p'] && keys['l'])) { // exclusive or to filter requests + if (keys['p']) { + socket.emit("batmove_Right", "up"); + console.log('north key pressed - emit batmove_Right up'); + } + if (keys['l']) { + socket.emit("batmove_Right", "down"); + console.log('south key pressed - emit batmove_Right down'); + } } - if (keys['p']) { - socket.emit("batmove_Right", "up"); - console.log('north key pressed - emit batmove_Right up'); - } - if (keys['l']) { - socket.emit("batmove_Right", "down"); - console.log('south key pressed - emit batmove_Right down'); - } }, 16); - - - // Listen for Left bat updates - socket.on("batLeft_update", (y: number) => { - console.log('batLeft_update received y: ', y); + // Pong Objects updators + socket.on("batLeft_update", (y: number) => { + console.log('batLeft_update received y: ', y); const bat = document.getElementById("batleft") as HTMLDivElement | null; - if (!bat) { - console.error("FATAL ERROR: Bat element with ID 'bat-left' not found. Check HTML."); + if (!bat) { + console.error("FATAL ERROR: Bat element with ID 'bat-left' not found. Check HTML."); return ; - } - if (typeof y === 'number' && !isNaN(y)) { - bat.style.transform = `translateY(${y}px)`; - } else { - console.warn(`Received invalid Y value: ${y}`); - } - }); - - // Listen for Right bat updates - socket.on("batRight_update", (y: number) => { - console.log('batRight_update received y: ', y); - const bat = document.getElementById("batright") as HTMLDivElement | null; - if (!bat) { - console.error("FATAL ERROR: Bat element with ID 'bat-Right' not found. Check HTML."); - return ; - } - if (typeof y === 'number' && !isNaN(y)) { - bat.style.transform = `translateY(${y}px)`; - } else { - console.warn(`Received invalid Y value: ${y}`); - } - }); - - - - - - socket.once('welcome', (data) => { - console.log('%cWelcome PONG PAGE', color.yellow ); - addPongMessage('socket.once \'Welcome\' called') + } + if (typeof y === 'number' && !isNaN(y)) { + bat.style.transform = `translateY(${y}px)`; + } else { + console.warn(`Received invalid Y value: ${y}`); + } }); + socket.on("batRight_update", (y: number) => { + console.log('batRight_update received y: ', y); + const bat = document.getElementById("batright") as HTMLDivElement | null; + if (!bat) { + console.error("FATAL ERROR: Bat element with ID 'bat-Right' not found. Check HTML."); + return ; + } + if (typeof y === 'number' && !isNaN(y)) { + bat.style.transform = `translateY(${y}px)`; + } else { + console.warn(`Received invalid Y value: ${y}`); + } + }); + socket.on("ballPos_update", (x:number, y : number) => { + console.log('ballPos_update recieved: ', x, ' / ', y); + const ball = document.getElementById("ball") as HTMLDivElement | null; + if (!ball) { + console.error("FATAL ERROR: Bat element with ID 'bat-Right' not found. Check HTML."); + return ; + } + if (typeof y !== 'number' || isNaN(y) || typeof x !== 'number' || isNaN(x)) { + console.warn(`Received invalid X/Y value: ${x} / ${y}`); + return ; + } + ball.style.transform = `translateY(${y}px)`; + ball.style.transform += `translateX(${x}px)`; + }); + + // socket.once('welcome', (data) => { + // console.log('%cWelcome PONG PAGE', color.yellow ); + // addPongMessage('socket.once \'Welcome\' called') + // }); - - // Listen for messages from the server "MsgObjectServer" + // Listen for messages from the server "MsgObjectServer" socket.on("MsgObjectServer", (data: { message: ClientMessage}) => { // Display the message in the chat window const systemWindow = document.getElementById('system-box') as HTMLDivElement; - - const MAX_SYSTEM_MESSAGES = 10; if (systemWindow && data.message.destination === "system-info") { - const messageElement = document.createElement("div"); - messageElement.textContent = `${data.message.user}: ${data.message.text}`; - systemWindow.appendChild(messageElement); + const messageElement = document.createElement("div"); + messageElement.textContent = `${data.message.user}: ${data.message.text}`; + systemWindow.appendChild(messageElement); - // keep only last 10 - while (systemWindow.children.length > MAX_SYSTEM_MESSAGES) { - systemWindow.removeChild(systemWindow.firstChild!); - } - systemWindow.scrollTop = systemWindow.scrollHeight; + // keep only last 10 + while (systemWindow.children.length > MAX_SYSTEM_MESSAGES) { + systemWindow.removeChild(systemWindow.firstChild!); + } + systemWindow.scrollTop = systemWindow.scrollHeight; } console.log("Getuser():", getUser()); }); - - - - - setTitle('Pong Page'); + setTitle('Pong Game Page'); return { html: authHtml, postInsert: async (app) => { const bwhoami = document.getElementById('b-whoami') as HTMLButtonElement; - + const bqueu = document.getElementById('b-joinQueu') as HTMLButtonElement; + bwhoami?.addEventListener('click', async () => { whoami(socket); }); + bqueu?.addEventListener('click', async () => { + joinQueu(socket); + }); + } } }; -addRoute('/pong', handleChat, { bypass_auth: true }); \ No newline at end of file +addRoute('/pong', pongClient, { bypass_auth: true }); \ No newline at end of file diff --git a/frontend/src/pages/pong/types_front.ts b/frontend/src/pages/pong/types_front.ts index 807d071..21ae2d9 100644 --- a/frontend/src/pages/pong/types_front.ts +++ b/frontend/src/pages/pong/types_front.ts @@ -6,7 +6,6 @@ export type ClientMessage = { SenderWindowID: string; }; - export type ClientProfil = { command: string, destination: string, diff --git a/frontend/src/pong/pong.css b/frontend/src/pong/pong.css index 4df8ad0..c22229c 100644 --- a/frontend/src/pong/pong.css +++ b/frontend/src/pong/pong.css @@ -100,7 +100,6 @@ justify-center; } - .text-style { @apply text-black @@ -123,7 +122,6 @@ @apply absolute right-4 w-[12px] h-[80px] top-[0px]; } - .pong-center-line { @apply absolute diff --git a/src/pong/package.json b/src/pong/package.json index a3fd0e8..ecc4cab 100644 --- a/src/pong/package.json +++ b/src/pong/package.json @@ -30,7 +30,7 @@ "typebox": "^1.0.62" }, "devDependencies": { - "@types/node": "^22.19.2", + "@types/node": "^22.19.3", "rollup-plugin-node-externals": "^8.1.2", "vite": "^7.2.7", "vite-tsconfig-paths": "^5.1.4" diff --git a/src/pong/src/app.ts b/src/pong/src/app.ts index 2bfc2c7..3277db9 100644 --- a/src/pong/src/app.ts +++ b/src/pong/src/app.ts @@ -10,6 +10,9 @@ import { broadcast } from './broadcast'; import type { ClientProfil, ClientMessage } from './chat_types'; import { sendInvite } from './sendInvite'; import { setGameLink } from './setGameLink'; +import { emit } from 'process'; +import { Record } from 'typebox/type'; +import { UserId } from '@shared/database/mixin/user'; @@ -80,7 +83,9 @@ declare module 'fastify' { batmove_Right: (direction: "up" | "down") => void; batLeft_update: (y:number) => void; batRight_update: (y:number) => void; + ballPos_update: (x:number, y:number) => void; MsgObjectServer: (data: { message: ClientMessage }) => void; + queuJoin: (userID : UserId) => void; }>; } } @@ -96,66 +101,116 @@ async function onReady(fastify: FastifyInstance) { } const SPEED = 20; // bat speed - const BOTTOM_EDGE = 370; // bottom edge of the field; - const TOP_EDGE = 0; // top edge of the field - const START_POS_Y = 178; // bat y in the middle of the field - let batLeft = START_POS_Y; //shared bat position - let batRight = START_POS_Y; //shared bat position - - fastify.io.on('connection', (socket: Socket) => { + const TOP_EDGE = 0; // top edge of the field + const BOTTOM_EDGE = 450; // bottom edge of the field; + const LEFT_EDGE = 0; + const RIGHT_EDGE = 800; + + const MAX_PADDLE_Y = 370; // BOTTOM_EDGE - padle_height + const START_POS_Y = 178; // bat y in the middle of the field + + const START_BALLX = 364; //(RIGHT_EDGE / 2) - (ballradius*2 + ballBorder); + const START_BALLY = 189; //(BOTTOM_EDGE / 2) - (ballradius*2 + ballBorder); + const ACCELERATION_FACTOR = 1.15; + + let batLeft = START_POS_Y; //shared start bat position + let batRight = START_POS_Y; //shared start bat position + + let ballPosX = START_BALLX; + let ballPosY = START_BALLY; + let ballSpeedX = 1; + let ballSpeedY = 1; + + let games : Record = {}; // uuid, game uid - if not in game empty string + + fastify.io.on('connection', (socket: Socket) => { socket.emit("batLeft_update", batLeft); socket.emit("batRight_update", batRight); + socket.emit("ballPos_update", ballPosX, ballPosY); - socket.on('batmove_Left', (direction: "up" | "down") => { - if (direction === "up") { - batLeft -= SPEED; - console.log('w pressed UP'); - } - if (direction === "down") { - console.log('s pressed DOWN'); + // GAME + // paddle handling + socket.on('batmove_Left', (direction: "up" | "down") => { + if (direction === "up") { + batLeft -= SPEED; + console.log('w pressed UP'); + } + if (direction === "down") { + console.log('s pressed DOWN'); - batLeft += SPEED; - } - // position of bat leftplokoplpl - batLeft = Math.max(TOP_EDGE, Math.min(BOTTOM_EDGE, batLeft)); + batLeft += SPEED; + } + // position of bat leftplokoplpl + batLeft = Math.max(TOP_EDGE, Math.min(MAX_PADDLE_Y, batLeft)); + console.log('batLeft_update is called y:',batLeft); + socket.emit("batLeft_update", batLeft); + }); + socket.on('batmove_Right', (direction: "up" | "down") => { + if (direction === "up") { + batRight -= SPEED; + console.log('p pressed UP'); + } + if (direction === "down") { + console.log('l pressed DOWN'); + batRight += SPEED; + } + // position of bat left + batRight = Math.max(TOP_EDGE, Math.min(MAX_PADDLE_Y, batRight)); + console.log('batRight_update is called y:',batRight); + socket.emit("batRight_update", batRight); + }); + // ball handling: + // TODO 1: l/r bat hit + // TODO 2: l/r wall hit : score + setInterval(async () => { + const new_ballPosX = ballPosX + ballSpeedX; + const new_ballPosY = ballPosY + ballSpeedY; + + if (new_ballPosX < 0 || new_ballPosX + 36*2 > RIGHT_EDGE) { + ballSpeedX *= -1; + ballSpeedX *= ACCELERATION_FACTOR; + ballSpeedY *= ACCELERATION_FACTOR; + // TODO: score point + ball reset + spd reset + + } + if (new_ballPosY < 0 || new_ballPosY + 36*2 > BOTTOM_EDGE) { + ballSpeedY *= -1; + ballSpeedX *= ACCELERATION_FACTOR; + ballSpeedY *= ACCELERATION_FACTOR; + } + ballSpeedX = Math.max(-10, Math.min(ballSpeedX, 10)); + ballSpeedY = Math.max(-10, Math.min(ballSpeedY, 10)); + + ballPosX += ballSpeedX; + ballPosY += ballSpeedY; + + socket.emit("ballPos_update", ballPosX, ballPosY); + }, 1024) - console.log('batLeft_update is called y:',batLeft); + // QUEU HANDL + socket.on('queuJoin', async (uuid: UserId) => { + console.log('queu join recieved for : ', uuid); + if (!games.hasOwnProperty(uuid)) { + console.log("new user in game search queu"); + games[uuid] = ""; + } else if (games.hasOwnProperty(uuid) && games[uuid] == "") { + console.log('already searching for game'); + } else { // (games.hasOwnProperty(uuid) && games[uuid] != "") { + console.log('user alredy in game'); + return ; + } + // TODO: step2 : sesrch in record<> find guid w/ "" &/ pair them up + // TODO: step3 : move game logic to lifecycle of queu'ed game + }) - socket.emit("batLeft_update", batLeft); - }); - - socket.on('batmove_Right', (direction: "up" | "down") => { - if (direction === "up") { - batRight -= SPEED; - console.log('p pressed UP'); - } - if (direction === "down") { - console.log('l pressed DOWN'); - - batRight += SPEED; - } - // position of bat left - batRight = Math.max(TOP_EDGE, Math.min(BOTTOM_EDGE, batRight)); - - console.log('batRight_update is called y:',batRight); - - socket.emit("batRight_update", batRight); - }); - - - - - - - + // other: socket.on('message', (message: string) => { const obj: ClientMessage = JSON.parse(message) as ClientMessage; clientChat.set(socket.id, { user: obj.user, lastSeen: Date.now() }); socket.emit('welcome', {msg: 'Welcome to the chat! : '}); broadcast(fastify, obj, obj.SenderWindowID); }); - socket.on('inviteGame', async (data: string) => { const clientName: string = clientChat.get(socket.id)?.user || ''; const profilInvite: ClientProfil = JSON.parse(data) || ''; @@ -164,5 +219,6 @@ async function onReady(fastify: FastifyInstance) { sendInvite(fastify, inviteHtml, profilInvite); } }); + }); }