Bart
Bart

Reputation: 1

How would I synchronize a multiplayer pong game for all players in firebase realtime database?

I've been working on a simple multiplayer pong game for the browser using javascript but can't for the life of me figure out how to start the game for all players without everything getting desynced.

gif of the ball on both players sides not being synced

I really do not want to save the balls' position every millisecond to firebase as it would probably just be inefficient and waste space.

I did however sync up the players movements by saving their corresponding Y value to firebase and that works fine.

I am hosting this on github pages so I cannot use something like socket.io or websockets for this (atleast I think I can't) so I am resorting to firebase.

I have tried setting a 'ready' boolean to each player after they have loaded the pong game and only starting it once both players are ready but that still doesn't work to sync the ball up.

image of realtime database

Here is my code that handles multiplayer and the pong game, anything below //GAME CODE is pong, anything above is firebase related


       function handleMultiplayer(gameref) {
            const ready = ref(database, "public/ongoingGames/pong/" + gameref.key + "/players/plr" + currentPlayer + "/ready")
            const yRef = ref(database, "public/ongoingGames/pong/" + gameref.key + "/players/plr" + currentPlayer + "/y")
            const otherPlayer = currentPlayer === 1 ? 2 : 1
            const otherPlayerYRef = ref(database, "public/ongoingGames/pong/" + gameref.key + "/players/plr" + otherPlayer + "/y")
            const otherPlayerReadyRef = ref(database, "public/ongoingGames/pong/" + gameref.key + "/players/plr" + otherPlayer + "/ready")

            let interval = setInterval(() => {
                if (currentPlayer == 1) {
                    set(yRef, player1Y)
                }
                if (currentPlayer == 2) {
                    set(yRef, player2Y)
                }
            }, 10);

            onDisconnect(gameref).remove().then(() => {
                //stops it spamming the yref
                clearInterval(interval)
            })

            //when the other player moves up or down set their current Y value in game
            onValue(otherPlayerYRef, snapshot => {
                const data = snapshot.val()

                if (otherPlayer === 1) {
                    player1Y = data
                }
                if (otherPlayer === 2) {
                    player2Y = data
                }
            })

            let currentPlayerReady

            onValue(gameref, (snapshot) => {
                const data = snapshot.val()
                if (!data) { location.reload() }

                // if 2 players are in the game, start it and make the canvas visible
                if (Object.keys(data.players).length == maxPlayers) {

                    // this only triggers once as shown by the if statement
                    if (canvas.style.display != "flex") {
                        canvas.style.display = "flex"
                        matchmaking.remove()
                        document.getElementById("canvasContainer").style.display = "block"
                        document.getElementById("name1").textContent = data.players.plr1.name + " score : "
                        document.getElementById("name2").textContent = data.players.plr2.name + " score : "
                        //delay to make sure the player has loaded in
                        setTimeout(() => {
                            set(ready, true)
                            currentPlayerReady = true
                        }, 2000);
                        setInterval(interval)
                    }
                }
            })

            // wait for condition to turn true
            function waitFor(conditionFunction) {

                const poll = resolve => {
                    if (conditionFunction()) resolve();
                    else setTimeout(_ => poll(resolve), 400);
                }

                return new Promise(poll);
            }

            // trigger once the other players ready value has changed
            onValue(otherPlayerReadyRef, async (snapshot) => {
                const otherPlayerReady = snapshot.val()

                console.log("A")
                await waitFor(_ => currentPlayerReady === true)
                await waitFor(_ => otherPlayerReady === true)

                //calls when both players are ready, starts game
                console.log("B")

                //GAME CODE
                // i'll be honest I used AI to generate the code below this comment as a test for multiplayer, it works fine

                let dx = 2;
                let dy = -2;

                let player1UpPressed = false;
                let player1DownPressed = false;
                let player2UpPressed = false;
                let player2DownPressed = false;

                let player1Score = 0;
                let player2Score = 0;


                function drawPaddle(y1, y2) {
                    ctx.fillStyle = '#000'; // Set fill style for paddles
                    ctx.fillRect(0, y1, paddleWidth, paddleHeight); // Draw paddle for player 1
                    ctx.fillRect(canvas.width - paddleWidth, y2, paddleWidth, paddleHeight); // Draw paddle for player 2
                }

                function drawBall() {
                    ctx.beginPath();
                    ctx.arc(ballX, ballY, ballRadius, 0, Math.PI * 2);
                    ctx.fillStyle = '#0095DD';
                    ctx.fill();
                    ctx.closePath();
                }

                function drawScores() {
                    let player1Text = document.getElementById("name1").textContent;
                    let player2Text = document.getElementById("name2").textContent;

                    // Extract the player names
                    let playerName1 = player1Text.substring(0, player1Text.indexOf(':') + 1);
                    let playerName2 = player2Text.substring(0, player2Text.indexOf(':') + 1);

                    // Update only the score values
                    document.getElementById("name1").textContent = playerName1 + " " + player1Score;
                    document.getElementById("name2").textContent = playerName2 + " " + player2Score;
                }

                function updatePaddlePositions() {

                    if (currentPlayer === 1) {
                        if (player1UpPressed && player1Y > 0) {
                            player1Y -= 5;
                        }
                        if (player1DownPressed && player1Y < canvas.height - paddleHeight) {
                            player1Y += 5;
                        }
                    } else if (currentPlayer === 2) {
                        if (player2UpPressed && player2Y > 0) {
                            player2Y -= 5;
                        }
                        if (player2DownPressed && player2Y < canvas.height - paddleHeight) {
                            player2Y += 5;
                        }
                    }
                }

                function updateScores() {
                    // Check if the ball went past the paddles
                    if (ballX - ballRadius <= 0) {
                        player2Score++; // Player 2 scored
                        resetBall();
                    } else if (ballX + ballRadius >= canvas.width) {
                        player1Score++; // Player 1 scored
                        resetBall();
                    }
                }

                function resetBall() {
                    ballX = canvas.width / 2;
                    ballY = canvas.height / 2;
                    dx = -dx; // Change ball direction
                }

                function draw() {
                    // Draw trail effect by filling canvas with transparent color
                    ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
                    ctx.fillRect(0, 0, canvas.width, canvas.height);

                    // Draw paddles and ball
                    drawPaddle(player1Y, player2Y);
                    drawBall();
                    drawScores(); // Draw scores

                    // Bounce the ball off the walls
                    if (ballY + dy > canvas.height - ballRadius || ballY + dy < ballRadius) {
                        dy = -dy;
                    }

                    // Bounce the ball off the paddles
                    if (
                        (ballX - ballRadius <= paddleWidth && ballY >= player1Y && ballY <= player1Y + paddleHeight) ||
                        (ballX + ballRadius >= canvas.width - paddleWidth && ballY >= player2Y && ballY <= player2Y + paddleHeight)
                    ) {
                        dx = -dx;
                    }

                    // Update ball position
                    ballX += dx;
                    ballY += dy;

                    // Update paddle positions
                    updatePaddlePositions();

                    // Update scores
                    updateScores();
                }

                document.addEventListener('keydown', function (e) {
                    if (e.key === 'w') {
                        if (currentPlayer === 1) {
                            player1UpPressed = true;
                        } else if (currentPlayer === 2) {
                            player2UpPressed = true;
                        }
                    } else if (e.key === 's') {
                        if (currentPlayer === 1) {
                            player1DownPressed = true;
                        } else if (currentPlayer === 2) {
                            player2DownPressed = true;
                        }
                    }
                });

                document.addEventListener('keyup', function (e) {
                    if (e.key === 'w') {
                        if (currentPlayer === 1) {
                            player1UpPressed = false;
                        } else if (currentPlayer === 2) {
                            player2UpPressed = false;
                        }
                    } else if (e.key === 's') {
                        if (currentPlayer === 1) {
                            player1DownPressed = false;
                        } else if (currentPlayer === 2) {
                            player2DownPressed = false;
                        }
                    }
                });

                setInterval(draw, 10);
            })
        }

Upvotes: 0

Views: 99

Answers (0)

Related Questions