Search code examples
c#firebaseunity-game-enginefirebase-realtime-databasematchmaking

Problem with Matchmaking two players in realtime database


I'm developing an app like a quiz game with unity which include some matchmaking and multiplayer feature. my problem is I have set the firebase auth, and then I have set the firebase Realtime database to take the player who click on the start button after that he will see a searching for game label to match with the other players , I'm testing now with different devices , so when the player1 clicks on start button and then the player2 clicks on the start button both of them should go to the gameplay scene but in my situation just the player2 who can goes to the gameplay scene and the player1 stuck at the same scene. I would like to know what's the problem here, it's my first time making an online app/game, so I hadn't had

using UnityEngine;
using UnityEngine.SceneManagement;
using Firebase;
using Firebase.Database;
using Firebase.Auth;
using System;
using System.Collections.Generic;

public class MatchmakingManager : MonoBehaviour
{
    private DatabaseReference databaseReference;
    private FirebaseAuth auth;
    private FirebaseUser currentUser;
    private bool isMatchmaking = false;
    private bool sceneTransitioned = false;

    void Start()
    {
        // Initialize Firebase components
        databaseReference = FirebaseDatabase.DefaultInstance.RootReference;
        auth = FirebaseAuth.DefaultInstance;
        currentUser = auth.CurrentUser;

        if (currentUser != null)
        {
            // Check if the current user is already in matchmaking
            CheckMatchmakingStatus();
        }
        else
        {
            Debug.LogError("User is not logged in.");
        }
    }

    private void CheckMatchmakingStatus()
    {
        // Check if the current user is already in matchmaking
        databaseReference.Child("matchmaking").Child(currentUser.UserId).GetValueAsync().ContinueWith(task =>
        {
            if (task.IsCompleted && !task.IsFaulted && !task.IsCanceled)
            {
                DataSnapshot snapshot = task.Result;
                if (snapshot != null && snapshot.Exists)
                {
                    // User is already in matchmaking
                    isMatchmaking = true;
                }
                else
                {
                    // User is not in matchmaking
                    isMatchmaking = false;
                }
            }
            else
            {
                Debug.LogError("Failed to check matchmaking status: " + task.Exception);
            }
        });
    }

    public void StartMatchmaking()
    {
        if (!isMatchmaking)
        {
            // Add the current user to matchmaking
            databaseReference.Child("matchmaking").Child(currentUser.UserId).SetValueAsync(true).ContinueWith(task =>
            {
                if (task.IsCompleted && !task.IsFaulted && !task.IsCanceled)
                {
                    // Successfully added to matchmaking
                    isMatchmaking = true;

                    // Check for available matches
                    FindMatch();
                }
                else
                {
                    Debug.LogError("Failed to start matchmaking: " + task.Exception);
                }
            });
        }
        else
        {
            Debug.LogWarning("User is already in matchmaking.");
        }
    }

    public void CancelMatchmaking()
    {
        // Remove the player from the matchmaking pool
        string playerId = auth.CurrentUser.UserId;
        databaseReference.Child("matchmaking").Child(playerId).RemoveValueAsync().ContinueWith(task =>
        {
            if (task.IsCompleted && !task.IsFaulted && !task.IsCanceled)
            {
                // Successfully removed from matchmaking
                isMatchmaking = false;
            }
            else
            {
                Debug.LogError("Failed to cancel matchmaking: " + task.Exception);
            }
        });
    }

    private void FindMatch()
    {
        // Query the database for game rooms with two players
        databaseReference.Child("matchmaking").OrderByKey().LimitToFirst(2).GetValueAsync().ContinueWith(task =>
        {
            if (task.IsCompleted && !task.IsFaulted && !task.IsCanceled)
            {
                DataSnapshot snapshot = task.Result;
                if (snapshot != null && snapshot.HasChildren && snapshot.ChildrenCount == 2)
                {
                    // Found two players, remove them from matchmaking
                    List<string> playerIds = new List<string>();
                    foreach (DataSnapshot playerSnapshot in snapshot.Children)
                    {
                        string playerId = playerSnapshot.Key;
                        playerIds.Add(playerId);
                    }

                    // Create a game room for the matched players
                    CreateGameRoom(playerIds[0], playerIds[1]);
                }
                else
                {
                    // No available matches yet or insufficient players in the queue
                    Debug.Log("No available matches yet or insufficient players in the queue, continuing waiting...");
                }
            }
            else
            {
                Debug.LogError("Failed to find a match: " + task.Exception);
            }
        });
    }

    private void CreateGameRoom(string player1Id, string player2Id)
    {
        // Generate a unique ID for the game room
        string roomId = databaseReference.Child("gameRooms").Push().Key;

        // Create a data structure representing the game room
        Dictionary<string, object> roomData = new Dictionary<string, object>();
        roomData["player1Id"] = player1Id;
        roomData["player2Id"] = player2Id;
        roomData["status"] = "active"; // You can set initial status to "active" or "waiting" or any other status you prefer

        // Set the data in the database under the generated room ID
        databaseReference.Child("gameRooms").Child(roomId).SetValueAsync(roomData)
            .ContinueWith(task =>
            {
                if (task.IsCompleted && !task.IsFaulted && !task.IsCanceled)
                {
                    Debug.Log("Game room created successfully!");

                    // Transition the matched players to the gameplay scene only if it hasn't been done before
                    if (!sceneTransitioned)
                    {
                        sceneTransitioned = true;
                        SceneManager.LoadScene("GamePlay");
                    }
                }
                else if (task.IsFaulted)
                {
                    // Handle the error
                    foreach (Exception exception in task.Exception.InnerExceptions)
                    {
                        Debug.LogError("Failed to create game room: " + exception.Message);
                    }
                }
                else if (task.IsCanceled)
                {
                    Debug.LogWarning("Creating game room task was canceled.");
                }
                else
                {
                    Debug.LogError("Unknown error occurred while creating game room.");
                }
            });
    }
}

any experiences before.

i want both of players to go to the gameplay scene after that I'll need to handle the other features like scores,players name etc.


Solution

  • Well. When the first player enters the game, he doesn't find any other players and is added to the waiting list.

    When the second player enters, he sees the first and creates a room - and then goes to the "game scene" - and the first player does not know about it.

    Solution: The first player - if he can't find any available rooms - should create a room - and listen to the changes in it. When the second player enters, he will find this room and join and listen to this node - when both have joined and the update has come, start the game scene.

    Also, I can't tell without debugging, but it looks like your players are creating two rooms with different id's at the same time.

    string roomId = databaseReference.Child("gameRooms").Push().Key;
        //two rooms will be created
        // you must have a common room id for two players.
        // For example String(player1Id+player2Id)
    

    You also use databaseReference.Child("matchmaking").Child(currentUser.UserId).GetValueAsync()

    suitable for one-time data retrieval:

    You can use the GetValueAsync method to read a static snapshot of the contents at a given path once. The task result will contain a snapshot containing all data at that location, including child data. If there is no data, the snapshot returned is null.

    And you need to listen to the changes

    In any case, this is a misconception - you can get two random players in this way, not the one who initiates the creation of the room.

    databaseReference.Child("matchmaking").OrderByKey().LimitToFirst(2)