Turn-based game like scrabble different prefabs, urgent help.

Hello, this is my first thread here in unity, I’m a student who struggles making an online feature for my project that needs to defend it next week (basically a thesis defense based on this) and I really need your help. I tried multiple multiplayer systems like Photon, coherence, Alteruna, and so on. And so far, nothing works for me. I’m not good at coding C# cuz it is my first time using that. I’m literally crying about this for days trying to figured out why I can’t code online multiplayer for this.

It’s basically a scrabble that I just downloaded off on unity, based on this “https://assetstore.unity.com/packages/templates/packs/words-mobile-ready-game-57214” and I tried making online multiplayer out of it based on the gamecontroller.cs. (tbh, I didn’t bought it cuz I’m broke, but I don’t have plans to post it publicly as soon as this thing got defended.)

(I’m just gonna paste the whole gamecontroller.cs here for scrabble here, cuz I don’t know how to code online multiplayer in this code)

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Linq;
using UnityEngine.UI;

public class GameController : MonoBehaviour {

    public static GameController data;
    public GameObject GameUI;
    public GameObject GameOverUI;
    public GameObject UILettersPanel_p1, UILettersPanel_p2, UILettersPanel_p3, UILettersPanel_p4, ButtonsPanel;
    public List<GameObject> BoardSlots;
    public GameObject menuPanel;
    public GameObject SelectJokerLetter;
    public GameObject SwapBlock;
    public GameObject ErrorAlert;
    public GameObject confirmationDialog;
    public GameObject newPlayerTitle;
    public GameObject newScoreBlock;
    public GameObject ready;
    public GameObject go;
    public Text letterLeftTxt;
    public Text currentPlayerTxt;
    public Text confirmationDialogTxt;
    public Text gameOverText;
    public InputField inputPlayersCount;
    public Toggle soundToggle;
    public bool dictChecking;
    public List<PlayerData> players;


    [HideInInspector]
    public bool uiTouched, letterDragging, canBePlant, paused, swapMode, pointerOverPanel;
    [HideInInspector]
    public List<GameObject> BoardTiles;

    private List<GameObject> UITileSlots;
    private List<GameObject> UITiles;
    private List<GameObject> boardTilesMatters;
    private GameObject targetBoardSlot;
    private GameObject tempJokerTile;
    private List<string> newWords;
    private List<string> addedWords;
    private List<int> newLetterIds;
    private string preApplyInfo;
    private string confirmationID;
    private int playersCount = 2;
    private int currentPlayer;
    private int currentScore;
    private int errorCode;
    private int skipCount;
    private float canvasWidth;

    [System.Serializable]
    public class PlayerData
    {
        public string name;
        public bool active;
        public bool complete;
        public int score;
        public Text scoreTxt;
        public GameObject UILettersPanel;
        public List<GameObject> UITileSlots;
        public List<GameObject> UITiles;
    }

    public void ClearPrefs()
    {
        PlayerPrefs.DeleteAll();
        Debug.Log("here");
    }

    private Alteruna.Avatar _avatar;

    void Start() {
        data = this;
        StartCoroutine(ReadyGoThing());
    }


    private IEnumerator ReadyGoThing()
    {
        yield return new WaitForSeconds(1f);

        SoundController.data.playReady();
        ready.SetActive(true);
        go.SetActive(false);

        yield return new WaitForSeconds(1f);

        SoundController.data.playGo();
        ready.SetActive(false);
        go.SetActive(true);

        yield return new WaitForSeconds(1.2f);

        ready.SetActive(false);
        go.SetActive(false);
        StartGame();
    }
    public void StartGame()
    {
        ResetData();
        FillUITiles();
        for (int i =0; i <= playersCount-1; i++)
        {
            players[i].active = true;
            players[i].scoreTxt.text = "0";
        }
        SwitchPlayer();
        UpdateTxts();
        GameUI.SetActive(true);
        GameOverUI.SetActive(false);
    }

    void FitUIElements() {
        canvasWidth = GameObject.FindObjectOfType<Canvas>().GetComponent<RectTransform>().rect.width;
        float canvasHeight = GameObject.FindObjectOfType<Canvas>().GetComponent<RectTransform>().rect.height;
        float slotSize = canvasWidth / 7.0f;

        float ratio = (float)Screen.width / Screen.height;
        ButtonsPanel.GetComponent<RectTransform>().anchorMax = new Vector2(1, 1.0f - ratio);
        menuPanel.GetComponent<RectTransform>().sizeDelta = new Vector2(canvasWidth, canvasHeight);
        menuPanel.GetComponent<RectTransform>().anchoredPosition = new Vector2(-canvasWidth, 0);
        menuPanel.SetActive(true);

        for (int i = 0; i <= playersCount - 1; i++)
        {
            float dx = -3 * slotSize;
            foreach (GameObject slot in players[i].UITileSlots)
            {
                RectTransform slotRT = slot.GetComponent<RectTransform>();
                slotRT.anchoredPosition = new Vector2(dx, 0);
                dx += slotSize;
                slotRT.sizeDelta = new Vector2(slotSize, slotSize);
                slot.GetComponent<UISlot>().UITile.GetComponent<RectTransform>().sizeDelta = new Vector2(slotSize - 2, slotSize - 2);
                slot.GetComponent<UISlot>().UITile.GetComponent<BoxCollider2D>().size = new Vector2(slotSize - 2, slotSize - 2);
                slot.GetComponent<UISlot>().UITile.GetComponent<RectTransform>().anchoredPosition = slot.GetComponent<RectTransform>().anchoredPosition;
                slot.GetComponent<UISlot>().UITile.GetComponent<UITile>().lastPosition = slot.GetComponent<RectTransform>().anchoredPosition;
            }

            players[i].UILettersPanel.SetActive(true);
            players[i].UILettersPanel.GetComponent<RectTransform>().anchorMax = new Vector2(1, 1.0f - ratio);
        }
    }

    void FillUITiles()
    {
        for (int i = 0; i <= playersCount - 1; i++)
        {
            foreach (GameObject slot in players[i].UITileSlots)
            {
                players[i].UITiles.Add(slot.GetComponent<UISlot>().UITile);
            }

            foreach (GameObject tile in players[i].UITiles)
            {
                tile.SetActive(true);
                tile.GetComponent<UITile>().GetNewLetter();
            }
        }

    }

    void Update()
    {
        //if (Input.GetKey("escape"))
            //Application.Quit();
        //if (Input.GetKey(KeyCode.R))
            //SceneManager.LoadScene(0);
        if (paused)
            return;
#if UNITY_EDITOR
        if (Input.GetMouseButtonDown(0))
        {
            if (EventSystem.current.IsPointerOverGameObject())
                uiTouched = true;
        }
#endif

#if UNITY_ANDROID
        if (Input.touchCount == 1)
        {
            if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
                uiTouched = true;
        }
#endif
        if (Input.GetMouseButtonUp(0))
            uiTouched = false;


        if (letterDragging)
        {
            if (newScoreBlock.activeInHierarchy)
                newScoreBlock.SetActive(false);
            CheckifPointerOverPanel();
            if (pointerOverPanel)
            {
                canBePlant = false;
                return;
            }
               
            RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero, Mathf.Infinity, 1 << 9);
            if (hit.collider != null)
            {
                targetBoardSlot = hit.collider.gameObject;
                if (targetBoardSlot.GetComponent<BoardSlot>().free)
                    canBePlant = true;
                else
                    canBePlant = false;
            }
            else
            {
                canBePlant = false;
            }
        }
    }

    public void CheckifPointerOverPanel()
    {
        PointerEventData pointer = new PointerEventData(EventSystem.current);
        pointer.position = Input.mousePosition;

        List<RaycastResult> raycastResults = new List<RaycastResult>();
        EventSystem.current.RaycastAll(pointer, raycastResults);
        pointerOverPanel = false;

        if (raycastResults.Count > 0)
        {
            foreach (RaycastResult res in raycastResults)
            {
                if (res.gameObject.tag == "UIPanel")
                    pointerOverPanel = true;
            }
        }
    }

    public void PlantTile(GameObject tile)
    {
        tile.transform.parent = targetBoardSlot.transform;
        tile.transform.localPosition = new Vector3(0,0,-0.1f);
        tile.SetActive(true);
        tile.GetComponent<BoardTile>().currentslot = targetBoardSlot.GetComponent<BoardSlot>();
        targetBoardSlot.GetComponent<BoardSlot>().free = false;
        Camera.main.BroadcastMessage("ZoomIn", targetBoardSlot.transform.position);
        PreApply();
        SoundController.data.playTap();
    }

    public GameObject GetFreeUISlot()
    {
        foreach(GameObject slot in UITileSlots)
        {
            if (slot.GetComponent<UISlot>().UITile == null)
                return slot;
        }
        return null;
    }

    public void CancelLetters()
    {
        foreach (GameObject tile in UITiles)
        {
            if (!tile.activeInHierarchy && !tile.GetComponent<UITile>().finished)
            {
                tile.SetActive(true);
                tile.GetComponent<UITile>().CancelTile();
            }
        }

        foreach (GameObject bs in BoardSlots) {
            if(bs.GetComponent<BoardSlot>().completed != true)
                bs.GetComponent<BoardSlot>().free = true;
        }

        GameController.data.PreApply();
    }

    public void ShuffleUITiles()
    {
        foreach (GameObject slot in UITileSlots)
        {
            slot.GetComponent<UISlot>().UITile = null;
        }

        for (int i = 0; i < UITiles.Count; i++)
        {
            if (UITiles[i] != null)
            {
                GameObject tempObj = UITiles[i];
                int randomIndex = UnityEngine.Random.Range(i, UITiles.Count);
                UITiles[i] = UITiles[randomIndex];
                UITiles[randomIndex] = tempObj;
            }
        }

        for (int i = 0; i < UITiles.Count; i++)
        {
            UITiles[i].GetComponent<UITile>().GoToSlot(UITileSlots[i]);
        }
    }

    public void OpenSelectJokerLetter(GameObject jt)
    {
        paused = true;
        tempJokerTile = jt;
        SelectJokerLetter.SetActive(true);
    }

    public void ApplyJokerTile(string letter)
    {
        tempJokerTile.GetComponent<BoardTile>().letter = letter;
        tempJokerTile.GetComponentInChildren<TextMesh>().text = letter;
        PlantTile(tempJokerTile);
        SelectJokerLetter.SetActive(false);
        paused = false;
    }

    public void PreApply()
    {
        errorCode = 0;
        preApplyInfo = "";
        newScoreBlock.SetActive(false);
        newLetterIds = new List<int>();
        newWords = new List<string>();
        boardTilesMatters = new List<GameObject>();
        bool firstWord = true;
        //Checking if tiles are not alone
        for (int i = 0; i < BoardSlots.Count; i++)
        {
            if (!BoardSlots[i].GetComponent<BoardSlot>().free && !BoardSlots[i].GetComponent<BoardSlot>().completed)
            {
                if (!CheckIfNewTileConnected(i))
                {
                    errorCode = 1;
                    return;
                }
                newLetterIds.Add(i);
            }

            if (BoardSlots[i].GetComponent<BoardSlot>().completed && firstWord)
                firstWord = false;
        }

        if (newLetterIds.Count == 0)
            return;

        //Check if first word intersects center tile
       
        if (firstWord)
        {
            bool correct = false;
            foreach (int id in newLetterIds)
            {
                if (id == 112)
                    correct = true;
            }

            if (!correct)
            {
                errorCode = 2;
                return;
            }
        }

        //Checking if new tiles are at one line
        int prevX = 0;
        int prevY = 0;
        bool horizontal = false;
        bool vertical = false;

        foreach (int id in newLetterIds)
        {
            int x = id / 15 + 1;
            int y = (id + 1) % 15 == 0 ? 15 : (id + 1) % 15;

            if (prevX == 0 && prevY == 0)
            {
                prevX = x;
                prevY = y;
            }
            else
            {
                if (x == prevX && !vertical)
                {
                    horizontal = true;
                }
                else if (y == prevY && !horizontal)
                {
                    vertical = true;
                }
                else
                {
                    errorCode = 3;
                    return;
                }
            }
        }

        //Checking if a free space between letters
        int firstNewId = newLetterIds[0];
        if (horizontal)
        {
            for (int i = firstNewId; i < newLetterIds[newLetterIds.Count - 1]; i++)
            {
                if (BoardSlots[i].GetComponent<BoardSlot>().free)
                {
                    errorCode = 4;
                    return;
                }
            }
        }

        //Check if new tile contact old tile
        bool haveConnect = false;

        foreach (int id in newLetterIds)
        {
            if (CheckIfNewTileConnectsOld(id))
                haveConnect = true;
        }

        if (!haveConnect && !firstWord)
        {
            errorCode = 5;
            return;
        }



        //Buildig words and scores
        currentScore = 0;
        newWords = new List<string>();
        foreach (int id in newLetterIds)
        {
            int i;
            for (i = id; i > 0; i -= 15)
            {
                GameObject topSlot = GetАdjacentSlot(i, "top");
                if (!topSlot || topSlot.GetComponent<BoardSlot>().free)
                    break;
            }
            if (VerticalWord(i).Length > 1 && !newWords.Contains(VerticalWord(i)))
            {
                newWords.Add(VerticalWord(i));
                currentScore += GetVerticalScore(i);
            }
           

            int y;
            for (y = id; y > 0; y--)
            {
                GameObject leftSlot = GetАdjacentSlot(y, "left");
                if (!leftSlot || leftSlot.GetComponent<BoardSlot>().free)
                    break;
            }
            if (HorizontalWord(y).Length > 1 && !newWords.Contains(HorizontalWord(y)))
            {
                newWords.Add(HorizontalWord(y));
                currentScore += GetHorizontalScore(y);
            }
               
        }
        string newWordsList = "";
        foreach(string word in newWords)
        {
            newWordsList += word + ", ";
        }
        newWordsList = newWordsList.Remove(newWordsList.Length - 2);
        preApplyInfo = "APPLY '" + newWordsList + "' for " + currentScore + " scores?";

        newScoreBlock.SetActive(true);
        newScoreBlock.transform.position = boardTilesMatters[boardTilesMatters.Count - 1].transform.position + new Vector3(0.5f,-0.5f,-0.4f);
        newScoreBlock.GetComponentInChildren<TextMesh>().text = currentScore.ToString();
        newScoreBlock.GetComponent<Transformer>().ScaleImpulse(new Vector3(1f, 1f, 1), 0.15f, 1);
        //Debug.Log(currentScore);
    }

    public void ApplyTurn() {
        string info = "";

        if (newWords.Count == 0 && errorCode == 0)
            errorCode = 7;
       
        newWords = newWords.Distinct().ToList();

        //CHECKING NEW WORD WITH DICTIONARY
        if (dictChecking)
        {
            info += "\r\n";
            foreach (string word in newWords)
            {

                if (wordDictionary.data != null && !wordDictionary.data.hasWord(word))
                {
                    info += word + " ";
                    errorCode = 8;
                }

                if (addedWords.Contains(word))
                {
                    info = word;
                    errorCode = 9;
                }
            }
        }

        if (errorCode != 0)
        {
            ShowErrorAlert(errorCode, info);
            return;
        } else
        {
            foreach (string word in newWords)
                addedWords.Add(word);
        }

        //APPLYING WORD
        foreach (int id in newLetterIds)
        {
            BoardSlots[id].GetComponent<BoardSlot>().completed = true;
            BoardSlots[id].GetComponentInChildren<BoardTile>().completed = true;
        }

        foreach(GameObject bTile in boardTilesMatters)
        {
            bTile.GetComponent<Transformer>().ScaleImpulse(new Vector3(1.2f, 1.2f, 1), 0.125f, 1);
        }

        foreach (GameObject tile in UITiles)
        {
            if (!tile.activeInHierarchy)
            {
                tile.SetActive(true);
                tile.GetComponent<UITile>().ReCreateTile();
            }
        }

        int tilesLeft = 0;
        foreach (GameObject uiTile in UITiles)
        {
            if (uiTile.activeInHierarchy)
                tilesLeft++;
        }

        if (tilesLeft == 0)
            players[currentPlayer - 1].complete = true;

        players[currentPlayer-1].score += currentScore;
        skipCount = 0;
        UpdateTxts();

        Invoke("SwitchPlayer", 0.35f);
        SoundController.data.playApply();
    }

    public void ShowApplyConfirmDialog()
    {
        if (errorCode == 0 && preApplyInfo.Length > 0)
            ShowConfirmationDialog("ApplyTurn");
        else
            ApplyTurn();
    }

    bool CheckIfNewTileConnected(int tileId) {
        GameObject topTile = GetАdjacentSlot(tileId, "top");
        if (topTile != null && !topTile.GetComponent<BoardSlot>().free)
            return true;
        GameObject rightTile = GetАdjacentSlot(tileId, "right");
        if (rightTile != null && !rightTile.GetComponent<BoardSlot>().free)
            return true;
        GameObject bottomTile = GetАdjacentSlot(tileId, "bottom");
        if (bottomTile != null && !bottomTile.GetComponent<BoardSlot>().free)
            return true;
        GameObject leftTile = GetАdjacentSlot(tileId, "left");
        if (leftTile != null && !leftTile.GetComponent<BoardSlot>().free)
            return true;

        return false;
    }

    bool CheckIfNewTileConnectsOld(int tileId)
    {
        GameObject topTile = GetАdjacentSlot(tileId, "top");
        if (topTile != null && topTile.GetComponent<BoardSlot>().completed)
            return true;
        GameObject rightTile = GetАdjacentSlot(tileId, "right");
        if (rightTile != null && rightTile.GetComponent<BoardSlot>().completed)
            return true;
        GameObject bottomTile = GetАdjacentSlot(tileId, "bottom");
        if (bottomTile != null && bottomTile.GetComponent<BoardSlot>().completed)
            return true;
        GameObject leftTile = GetАdjacentSlot(tileId, "left");
        if (leftTile != null && leftTile.GetComponent<BoardSlot>().completed)
            return true;

        return false;
    }

    public GameObject GetАdjacentSlot(int tileId, string pos)
    {
        switch (pos)
        {
            case "top":
                int topTileID = tileId - 15;
                if (topTileID + 1 > 0)
                {
                    return BoardSlots[topTileID];
                }
                break;
            case "right":
                int rightTileId = tileId + 1;
                if ((rightTileId) % 15 != 0)
                {
                    return BoardSlots[rightTileId];
                }
                break;
            case "bottom":
                int bottomTileId = tileId + 15;
                if (bottomTileId + 1 < 226)
                {
                    return BoardSlots[bottomTileId];
                }
                break;
            case "left":
                int leftTileId = tileId - 1;
                if ((leftTileId+1) % 15 != 0)
                {
                    return BoardSlots[leftTileId];
                }
                break;
        }
        return null;
    }

    public string VerticalWord(int firstId)
    {
        string word = "";
        for(int i = firstId; i < 225; i += 15)
        {
            if (BoardSlots[i] && !BoardSlots[i].GetComponent<BoardSlot>().free)
            {
                word += BoardSlots[i].GetComponentInChildren<BoardTile>().letter;
                if (!boardTilesMatters.Contains(BoardSlots[i].GetComponentInChildren<BoardTile>().gameObject))
                    boardTilesMatters.Add(BoardSlots[i].GetComponentInChildren<BoardTile>().gameObject);
                else
                    boardTilesMatters.Remove(BoardSlots[i].GetComponentInChildren<BoardTile>().gameObject); boardTilesMatters.Add(BoardSlots[i].GetComponentInChildren<BoardTile>().gameObject);
                if (i + 15 > 224)
                    return word;
            }
            else
            {
                return word;
            }
        }
        return "";
    }

    public string HorizontalWord(int firstId)
    {
        string word = "";
        for (int i = firstId; i < 225; i++)
        {
            if (BoardSlots[i] && !BoardSlots[i].GetComponent<BoardSlot>().free)
            {
                word += BoardSlots[i].GetComponentInChildren<BoardTile>().letter;
                if (!boardTilesMatters.Contains(BoardSlots[i].GetComponentInChildren<BoardTile>().gameObject))
                    boardTilesMatters.Add(BoardSlots[i].GetComponentInChildren<BoardTile>().gameObject);
                else
                    boardTilesMatters.Remove(BoardSlots[i].GetComponentInChildren<BoardTile>().gameObject); boardTilesMatters.Add(BoardSlots[i].GetComponentInChildren<BoardTile>().gameObject);
                if ((i + 1) % 15 == 0)
                    return word;
            }
            else
            {
                return word;
            }
        }
        return word;
    }

    public int GetVerticalScore(int firstId)
    {
        int score = 0;
        int wordFactor = 1;
        for (int i = firstId; i < 225; i += 15)
        {
            BoardSlot boardSlot = BoardSlots[i].GetComponent<BoardSlot>();

            if (BoardSlots[i] && !boardSlot.free)
            {
                if (!boardSlot.completed)
                {
                    //score += Alphabet.data.GetLetterScore(BoardSlots[i].GetComponentInChildren<BoardTile>().letter) * (int)boardSlot.letterFactor;
                    score += BoardSlots[i].GetComponentInChildren<BoardTile>().score * (int)boardSlot.letterFactor;
                    if ((int)boardSlot.wordFactor > 1)
                        wordFactor *= (int)boardSlot.wordFactor;
                } else
                {
                    //score += Alphabet.data.GetLetterScore(BoardSlots[i].GetComponentInChildren<BoardTile>().letter);
                    score += BoardSlots[i].GetComponentInChildren<BoardTile>().score;
                }

                if (i + 15 > 224)
                    break;
            }
            else
            {
                break;
            }
        }
        return score * wordFactor;
    }

    public int GetHorizontalScore(int firstId)
    {
        int score = 0;
        int wordFactor = 1;
        for (int i = firstId; i < 225; i++)
        {
            BoardSlot boardSlot = BoardSlots[i].GetComponent<BoardSlot>();

            if (!BoardSlots[i].GetComponent<BoardSlot>().free)
            {

                if (!boardSlot.completed)
                {
                    //score += Alphabet.data.GetLetterScore(BoardSlots[i].GetComponentInChildren<BoardTile>().letter) * (int)boardSlot.letterFactor;
                    score += BoardSlots[i].GetComponentInChildren<BoardTile>().score * (int)boardSlot.letterFactor;
                if ((int)BoardSlots[i].GetComponent<BoardSlot>().wordFactor > 1)
                    wordFactor *= (int)BoardSlots[i].GetComponent<BoardSlot>().wordFactor;
                }
                else
                {
                    score += BoardSlots[i].GetComponentInChildren<BoardTile>().score;
                }
                if ((i + 1) % 15 == 0)
                    break;
            }
            else
            {
                break;
            }
        }
        return score * wordFactor;
    }

    public void ShowErrorAlert(int code, string info) {
        paused = true;
        string errorText = "";
        switch (code)
        {
            case 1:
                errorText = "TILES SHOULD BE CONNECTED!";
                break;
            case 2:
                errorText = "FIRST WORD SHOULD INTERSECT CENTER TILE!";
                break;
            case 3:
                errorText = "TILES SHOULD BE IN 1 LINE!";
                break;
            case 4:
                errorText = "TILES SHOULD NOT HAVE SPACES!";
                break;
            case 5:
                errorText = "NO CONNECTION WITH OLD TILES!";
                break;
            case 6:
                errorText = "NOT ENOUGH FREE TILES";
                break;
            case 7:
                errorText = "YOU NEED TO PLACE TILES FIRST!";
                break;
            case 8:
                errorText = "INCORRECT WORDS: " + info;
                break;
            case 9:
                errorText = "ALREADY USED WORD: " + info;
                break;
        }
        ErrorAlert.SetActive(true);
        ErrorAlert.GetComponentInChildren<Text>().text = errorText;
    }

    public void CloseErrorAlert()
    {
        ErrorAlert.SetActive(false);
        paused = false;
    }

    public void Restart()
    {
        SceneManager.LoadScene(0);
    }

    public void EnableSwapMode()
    {
        paused = swapMode =  true;
        CancelLetters();
        SwapBlock.SetActive(true);
    }

    public void ApplySwap()
    {
        List<GameObject> tilesToSwap = new List<GameObject>();
        List<string> oldLetters = new List<string>();

        foreach (GameObject uiTile in UITiles)
        {
            if (uiTile.GetComponent<UITile>().needSwap)
            {
                tilesToSwap.Add(uiTile);
                oldLetters.Add(uiTile.GetComponent<UITile>().letterString.text);
            }
        }

        if (tilesToSwap.Count == 0)
        {
            DisableSwapMode();
            return;
        }

        if (tilesToSwap.Count > Alphabet.data.LettersFeed.Count)
        {
            DisableSwapMode();
            ShowErrorAlert(6, "");
            return;
        }


            foreach (GameObject uiTile in tilesToSwap)
        {
            uiTile.GetComponent<UITile>().GetNewLetter();
        }

        foreach (string letter in oldLetters)
        {
            Alphabet.data.LettersFeed.Add(letter);
        }

        Invoke("DisableSwapMode", 0.15f);
        Invoke("SwitchPlayer", 0.5f);
        skipCount = 0;
    }

    public void DisableSwapMode()
    {
        paused = swapMode = false;
        SwapBlock.SetActive(false);

        foreach (GameObject uiTile in UITiles)
        {
            if (uiTile.GetComponent<UITile>().needSwap)
            {
                uiTile.GetComponent<UITile>().SetSwapState(false);
            }
        }
    }

    void UpdateTxts()
    {
        players[currentPlayer - 1].scoreTxt.text = players[currentPlayer - 1].score.ToString();
        letterLeftTxt.text = Alphabet.data.LettersFeed.Count.ToString() + " LETTERS LEFT";
    }

    public void SwitchPlayer()
    {
        _avatar = GetComponent<Alteruna.Avatar>();

        if (!_avatar.IsMe)
            return;

            if (CheckForGameOver())
        {
            GameOver();
            return;
        }
           

        currentPlayer = currentPlayer + 1 <= 2   ? currentPlayer + 1 : 1;
        if (!players[currentPlayer - 1].active || players[currentPlayer - 1].complete)
        {
            SwitchPlayer();
            return;
        }

        for (int i = 1; i <= players.Count; i++)
        {
            if (i == currentPlayer)
            {
                players[i - 1].UILettersPanel.SetActive(true);
                players[i - 1].scoreTxt.text += "←";
            }
            else
            {
                players[i - 1].UILettersPanel.SetActive(false);
                players[i - 1].scoreTxt.text.Trim((new System.Char[] { '←' }));
            }
        }


        UITileSlots = players[currentPlayer - 1].UITileSlots;
        UITiles = players[currentPlayer - 1].UITiles;

        currentPlayerTxt.text = "Player " + currentPlayer;
        PreApply();
        //Showing Title
        newPlayerTitle.GetComponentInChildren<Text>().text = "PLAYER'S " + currentPlayer + " TURN";
        newPlayerTitle.GetComponent<Transformer>().MoveUIImpulse(Vector2.zero, 1, 1);

        UpdateTxts();
    }

    public void SkipTurn()
    {
        CancelLetters();
        Invoke("SwitchPlayer", 0.35f);
    }

    public void GiveUp()
    {
        Debug.Log("Player "+currentPlayer +" gived up!");

        foreach (GameObject uiTile in UITiles)
            Alphabet.data.LettersFeed.Add(uiTile.GetComponent<UITile>().letterString.text);

        SkipTurn();
        playersCount -= 1;
        players[currentPlayer - 1].active = false;
    }

    public void ShowConfirmationDialog(string confirmationID)
    {
        this.confirmationID = confirmationID;
        switch (confirmationID)
        {
            case "ApplyTurn":
                confirmationDialogTxt.text = preApplyInfo;
                break;
            case "SkipTurn":
                confirmationDialogTxt.text = "Are you sure to skip turn?";
                break;
            case "GiveUp":
                confirmationDialogTxt.text = "Give up?";
                break;
        }
        confirmationDialog.SetActive(true);
        switchMenu(false);
    }

    public void ConfirmDialog()
    {
        switch (confirmationID)
        {
            case "ApplyTurn":
                ApplyTurn();
                break;
            case "SkipTurn":
                skipCount++;
                SkipTurn();
                break;
            case "GiveUp":
                Invoke("GiveUp", 0.35f);
                break;
        }
        confirmationDialog.SetActive(false);
    }

    public void switchMenu(bool state)
    {
        if(state)
            menuPanel.GetComponent<Transformer>().MoveUI(new Vector2(-494.5f, 0), 0.5f);
        else
            menuPanel.GetComponent<Transformer>().MoveUI(new Vector2(-1019.5f, 0f), 0.5f);
    }

    public void switchSound()
    {
        PlayerPrefs.SetInt("sound", PlayerPrefs.GetInt("sound", 1) == 1 ? 0 : 1);
        soundToggle.isOn = PlayerPrefs.GetInt("sound", 1) == 1 ? true : false;
        AudioListener.volume = PlayerPrefs.GetInt("sound", 1);
        //Debug.Log("volume set to "+ AudioListener.volume);
    }

    public void ChangePlayerCount(int delta)
    {
        if (playersCount + delta > 4)
            playersCount = 2;
        else if (playersCount + delta < 2)
            playersCount = 4;
        else
            playersCount += delta;
        inputPlayersCount.text = playersCount.ToString();
    }

    public void ResetData()
    {
        foreach (GameObject go in BoardTiles)
            Destroy(go);
        BoardTiles = new List<GameObject>();

        foreach (GameObject go in BoardSlots)
        {
            go.GetComponentInChildren<BoardSlot>().free = true;
            go.GetComponentInChildren<BoardSlot>().completed = false;
        }

        for (int i = 0; i <= 1; i++)
        {
            players[i].score = 0;
            players[i].active = false;
            players[i].complete = false;
            players[i].scoreTxt.text = "-";
            players[i].UITiles = new List<GameObject>();
        }

        currentPlayer = 0;
        addedWords = new List<string>();
        Alphabet.data.ResetFeed();
    }

    public void GoToMainMenu()
    {
        GameOverUI.SetActive(false);
    }

    public void GameOver()
    {
        SoundController.data.playFinish();
        if (playersCount == 1)
        {
            GameOverUI.SetActive(true);
            gameOverText.text = "PLAYER " + currentPlayer + " WINS!";
            return;
        } else
        {
            int winnerPlayer = 0;
            int maxScore = 0;
            for (int i = 0; i <= 3; i++)
            {
                if(players[i].active && players[i].score > maxScore)
                {
                    maxScore = players[i].score;
                    winnerPlayer = i + 1;
                }

            }
            GameOverUI.SetActive(true);
            gameOverText.text = "AAA " + winnerPlayer + " AA!";
        }

    }

    public bool CheckForGameOver()
    {
        foreach (PlayerData pd in players)
        {
            if (pd.complete)
                return true;
        }
        if (playersCount == 1 || skipCount == playersCount*2)
            return true;
        else
            return false;
    }
}

And i’m trying to figure it out where the player is gonna do the host or client. for example. Player 1 is the host, and when player 1 is done, player 2 is the client.

Also, I’m trying to connect this online game to my lobby that works, (yeah, lobby is based on code monkey’s tutorial)

using System;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Generic;
using Unity.Services.Core;
using Unity.Services.Authentication;
using Unity.Services.Lobbies;
using Unity.Services.Lobbies.Models;
using UnityEngine;
using UnityEngine.UI;
using QFSW.QC;
using System.Collections;
using UnityEngine.SceneManagement;
using UnityEngine.Networking;
using Unity.Netcode;

public class LobbyMain : MonoBehaviour
{
    //some inputs
    public static LobbyMain Instance { get; private set; }


    public const string KEY_PLAYER_NAME = "PlayerName";
    public const string KEY_GAME_TIMER = "GameTimer";
    public const string KEY_READY = "Ready";

    //events when ui acted
    public event EventHandler OnPlayerLeftLobby;

    public event EventHandler<LobbyEventArgs> OnJoiningLobby;
    public event EventHandler<LobbyEventArgs> OnJoiningLobbyRefresh;
    public event EventHandler<LobbyEventArgs> OnKickedFromLobby;
    public event EventHandler<LobbyEventArgs> OnLobbyTimerChanged;
    public event EventHandler<LobbyEventArgs> OnReady;
    public class LobbyEventArgs : EventArgs
    {
        public Lobby lobby;
    }

    public event EventHandler<OnOutsideLobbyListRefreshedEventArgs> OnOutsideLobbyListRefreshed;
    public class OnOutsideLobbyListRefreshedEventArgs : EventArgs
    {
        public List<Lobby> lobbyList;
    }

    //timer set enumeration
    public enum GameTimer
    {
        Min5 = 5 * 60,   // 5 minutes
        Min6 = 6 * 60,   // 6 minutes
        Min7 = 7 * 60,   // 7 minutes
        Min8 = 8 * 60,   // 8 minutes
        Min9 = 9 * 60,   // 9 minutes
        Min10 = 10 * 60, // 10 minutes
    }

    public enum Ready
    {
        Ready,
        NotReady
    }

    private float lobbyTimer;
    private float lobbyMenuUpdate;
    private float refreshOutsideLobbyListTimer = 5f;
    private Lobby joiningLobby;
    private string playerName;
    [SerializeField] private LobbyUI lobbyIsPrivateText;
    public int gameSceneIndex = 2;

    // Flag to ensure scene is loaded only once
    private bool sceneLoaded = false;
    public NetworkManager NetworkManager;


    /// <summary>
    /// Start Code
    /// </summary>
    ///
    private void Awake()
    {
        Instance = this;
    }

    //signed in account anonymously
    private async void Start()
    {
        await UnityServices.InitializeAsync();

        // Check if the player is already signed in before attempting to sign in again
        if (!AuthenticationService.Instance.IsSignedIn)
        {
            AuthenticationService.Instance.SignedIn += () =>
            {
                Debug.Log("Player signed in " + AuthenticationService.Instance.PlayerId);
            };

            try
            {
                await AuthenticationService.Instance.SignInAnonymouslyAsync();
            }
            catch (AuthenticationException e)
            {
                Debug.LogError("Authentication failed: " + e.Message);
            }
        }
        else
        {
            Debug.Log("Player is already signed in: " + AuthenticationService.Instance.PlayerId);
        }

        //randomplayernumbername
        playerName = PlayerPrefs.GetString("user_name"); // + UnityEngine.Random.Range(1, 99);
        Debug.Log(playerName);
    }

    private void Update()
    {
        //for host
        //LobbyRefresh();  // Disabled Auto Refresh for testing with multiple builds
        HandleLobbyRefresh();
        //for joining the lobby
        UpdateLobbyMenuAnytime();
    }









    /// <summary>
    /// Lobby List Refreshed
    /// </summary>
    private void LobbyRefresh()
    {
        if (UnityServices.State == ServicesInitializationState.Initialized && AuthenticationService.Instance.IsSignedIn)
        {
            refreshOutsideLobbyListTimer -= Time.deltaTime;
            if (refreshOutsideLobbyListTimer < 0f)
            {
                float refreshOutsideLobbyListTimerMax = 5f;
                refreshOutsideLobbyListTimer = refreshOutsideLobbyListTimerMax;

                ListNames();
            }
        }
    }

    //lobby time stays active after 30 seconds by default
    private async void HandleLobbyRefresh()
    {
        //if (hostLobby != null)
        if (IsLobbyHostCreatedByPlayer())
        {
            {
                lobbyTimer -= Time.deltaTime;
                if (lobbyTimer < 0f)
                {
                    float lobbyTimerMax = 15f; //<- every x seconds
                    lobbyTimer = lobbyTimerMax;

                    await LobbyService.Instance.SendHeartbeatPingAsync(joiningLobby.Id);
                }
            }
        }
    }
    //updates the lobby itself

    public async void UpdateLobbyMenuAnytime()
    {
        if (joiningLobby != null)
        {
            lobbyMenuUpdate -= Time.deltaTime;
            if (lobbyMenuUpdate < 0f)
            {
                float lobbyPollTimerMax = 1.1f; //<- joining takes 1 second to update (only 1 second. nothing more nothing less).
                lobbyMenuUpdate = lobbyPollTimerMax;

                joiningLobby = await LobbyService.Instance.GetLobbyAsync(joiningLobby.Id);

                OnJoiningLobbyRefresh?.Invoke(this, new LobbyEventArgs { lobby = joiningLobby });

                if (!IsPlayerAtHostLobby())
                {
                    // Player was kicked out of this lobby
                    Debug.Log("Kicked from Lobby!");

                    OnKickedFromLobby?.Invoke(this, new LobbyEventArgs { lobby = joiningLobby });
                    OnReady?.Invoke(this, new LobbyEventArgs { lobby = joiningLobby });

                    joiningLobby = null;
                }
            }
        }
    }




    /// <summary>
    /// Create lobby
    /// </summary>
    ///


    //explains later once figured it out
    public Lobby GetJoiningLobby()
    {
        return joiningLobby;
    }

    //bool for host lobby
    public bool IsLobbyHostCreatedByPlayer()
    {
        return joiningLobby != null && joiningLobby.HostId == AuthenticationService.Instance.PlayerId;
    }


    //bool if player is at host lobby
    private bool IsPlayerAtHostLobby()
    {
        if (joiningLobby != null && joiningLobby.Players != null)
        {
            foreach (Player player in joiningLobby.Players)
            {
                if (player.Id == AuthenticationService.Instance.PlayerId)
                {
                    // This player is in this lobby
                    return true;
                }
            }
        }
        return false;
    }



    //changing game timer based on enumeration at line 39.
    public void ChangeGameTimer()
    {
        if (IsPlayerAtHostLobby())
        {
            GameTimer gameTimer =
                Enum.Parse<GameTimer>(joiningLobby.Data[KEY_GAME_TIMER].Value);

            switch (gameTimer)
            {
                default:
                case GameTimer.Min5:
                    gameTimer = GameTimer.Min6;
                    break;
                case GameTimer.Min6:
                    gameTimer = GameTimer.Min7;
                    break;
                case GameTimer.Min7:
                    gameTimer = GameTimer.Min8;
                    break;
                case GameTimer.Min8:
                    gameTimer = GameTimer.Min9;
                    break;
                case GameTimer.Min9:
                    gameTimer = GameTimer.Min10;
                    break;
                case GameTimer.Min10:
                    gameTimer = GameTimer.Min5;
                    break;
            }
            UpdateGameTimer(gameTimer);
        }
    }

    //this'll trigger the printplayers on line 20
    [Command]
    private void PrintPlayers()
    {
        PrintPlayers(joiningLobby);
    }

    //this'll identify how many players in that lobby, in that case, this game has 2 players only. so It only shows one player waiting for the
    //opponent to get in the lobby.
    private void PrintPlayers(Lobby lobby)
    {
        Debug.Log(lobby.Name + " created! Please join to battle! " + "Timer set in: " + lobby.Data["GameTimer"].Value);
        foreach (Player player in lobby.Players)
        {
            Debug.Log("Opponent: " + player.Id + " " + player.Data["PlayerName"].Value);
        }
    }



    //now onto the real thing
    public async void CreateLobby(bool isPrivate, GameTimer gameTimer)
    {
        try
        {
            int lobbyNumber = 1; // You can initialize this to whatever starting value you want
            string lobbyName = "Lobby No. " + lobbyNumber.ToString("D3");
            int maxPlayers = 2;
            Player player = GetPlayer();

            //used to determine whether the player is creating the lobby privately or publicly
            CreateLobbyOptions createLobbyOptions = new CreateLobbyOptions
            {
                //true = private / false = public
                Player = player,
                IsPrivate = isPrivate,
                Data = new Dictionary<string, DataObject>
                //timer settings
                {
                { KEY_GAME_TIMER, new DataObject(DataObject.VisibilityOptions.Public, gameTimer.ToString()) },
                }
            };
            Lobby lobby = await LobbyService.Instance.CreateLobbyAsync(lobbyName, maxPlayers, createLobbyOptions);

            //this'll create a host lobby when u create the lobby
            //hostLobby = lobby;
            joiningLobby = lobby;


            //joining a host lobby
            //joiningLobby = hostLobby;
            OnJoiningLobby?.Invoke(this, new LobbyEventArgs { lobby = lobby });

            Debug.Log("You have created " + lobby.Name + " UserID: " + lobby.Id + " Code: " + lobby.LobbyCode + " " + isPrivate);

            //PrintPlayers(hostLobby);
            PrintPlayers(lobby);
            lobbyIsPrivateText.UpdateIsPrivateText(isPrivate);
        }
        catch (LobbyServiceException e)
        {
            Debug.Log(e);
        }
    }



    //opponents found when creating lobby
    [Command]
    public async void ListNames()
    {
        try
        {
            //this line handles the list of players that joins the game online
            QueryLobbiesOptions queryLobbiesOptions = new QueryLobbiesOptions
            {
                Count = 100, //how much players need to list the lobby
                             // Filter for open lobbies only
                Filters = new List<QueryFilter>
                    {
                    new QueryFilter(
                        //Available Slots, can be change in different lobbies separated such as one for chess / one for scrabble
                        field: QueryFilter.FieldOptions.AvailableSlots,
                        //for data
                        op: QueryFilter.OpOptions.GT,
                        value: "0")

                    },
                Order = new List<QueryOrder>
                    {
                        //true if ascending list, or false if descending list
                        new QueryOrder(
                        asc: false,
                        field: QueryOrder.FieldOptions.Created)
                    }
            };

            //list of lobbies
            QueryResponse queryResponse = await Lobbies.Instance.QueryLobbiesAsync(queryLobbiesOptions);
            OnOutsideLobbyListRefreshed?.Invoke(this, new OnOutsideLobbyListRefreshedEventArgs { lobbyList = queryResponse.Results });

            //debug logging
            Debug.Log("List of Lobbies: " + queryResponse.Results.Count);
            foreach (Lobby lobby in queryResponse.Results)//if join the very first one, continuation from line 126
            {
                Debug.Log(lobby.Name);
            }
        }
        catch (LobbyServiceException e)
        {
            Debug.Log(e);
        }
    }



    //seperate names when joining
    private Player GetPlayer()
    {
        return new Player(AuthenticationService.Instance.PlayerId, null, new Dictionary<string, PlayerDataObject> {
            { KEY_PLAYER_NAME, new PlayerDataObject(PlayerDataObject.VisibilityOptions.Public, playerName) },
            //{ "PlayerName", new PlayerDataObject(PlayerDataObject.VisibilityOptions.Member, playerName) }
            });
    }

    // update game timer when changing
    public async void UpdateGameTimer(GameTimer gameTimer)
    {
        try
        {
            Debug.Log("UpdateGameTimer " + gameTimer);
            Lobby lobby = await Lobbies.Instance.UpdateLobbyAsync(joiningLobby.Id, new UpdateLobbyOptions
            {
                Data = new Dictionary<string, DataObject> {
                    { KEY_GAME_TIMER, new DataObject(DataObject.VisibilityOptions.Public, gameTimer.ToString()) }
                }
            });

            joiningLobby = lobby;
            //joiningLobby = hostLobby;
            //PrintPlayers(lobby);
            //PrintPlayers(hostLobby);
            OnLobbyTimerChanged?.Invoke(this, new LobbyEventArgs { lobby = joiningLobby });
        }
        catch (LobbyServiceException e)
        {
            Debug.Log(e);
        }
    }




    //Joining lobby




    //this one joins code privately with code
    public async void JoinLobbyByCode(string lobbyCode)
    { //try {
        Player player = GetPlayer();

        //main join lobby line
        Lobby lobby = await LobbyService.Instance.JoinLobbyByCodeAsync(lobbyCode, new JoinLobbyByCodeOptions
        {
            Player = player
        });
        joiningLobby = lobby;
        OnJoiningLobby?.Invoke(this, new LobbyEventArgs { lobby = lobby });

        //debug logging
        Debug.Log("Joined lobby!   Code: " + lobbyCode);
        PrintPlayers(lobby);
    }
    //can also use with try catch
    //catch (LobbyServiceException e)
    //{
    //Debug.Log(e);
    // }


    //When player joins quick lobby by list
    public async void JoinLobby(Lobby lobby)
    {
        Player player = GetPlayer();

        joiningLobby = await LobbyService.Instance.JoinLobbyByIdAsync(lobby.Id, new JoinLobbyByIdOptions
        {
            Player = player
        });
        OnJoiningLobby?.Invoke(this, new LobbyEventArgs { lobby = lobby });
    }

    // this one joins lobby publicly and quickly
    public async void QuickJoinLobby()
    {
        try
        {
            QuickJoinLobbyOptions options = new QuickJoinLobbyOptions();

            Lobby lobby = await LobbyService.Instance.QuickJoinLobbyAsync(options = null);
            joiningLobby = lobby;

            OnJoiningLobby?.Invoke(this, new LobbyEventArgs { lobby = lobby });
        }
        catch (LobbyServiceException e)
        {
            Debug.Log(e);
        }
    }





    //other lobby code

    //this'll update player names in the options tab
    public async void UpdatePlayerName(string changeName)
    {
        this.playerName = changeName;

        if (joiningLobby != null)
        {
            try
            {
                UpdatePlayerOptions updatePlayerOptions = new UpdatePlayerOptions();

                updatePlayerOptions.Data = new Dictionary<string, PlayerDataObject>() {
                    {
                        KEY_PLAYER_NAME, new PlayerDataObject(
                            visibility: PlayerDataObject.VisibilityOptions.Public,//.Member,
                            value: changeName)
                    }
                };

                string playerId = AuthenticationService.Instance.PlayerId;

                Lobby lobby = await LobbyService.Instance.UpdatePlayerAsync(joiningLobby.Id, playerId, updatePlayerOptions);
                joiningLobby = lobby;

                OnJoiningLobbyRefresh?.Invoke(this, new LobbyEventArgs { lobby = joiningLobby });
            }
            catch (LobbyServiceException e)
            {
                Debug.Log(e);
            }
        }
    }


    //when player is leaving the lobby cuz he wants to quit the game or the lobby itself
    public async void LeaveLobby()
    {
        if (joiningLobby != null)
        {
            try
            {
                await LobbyService.Instance.RemovePlayerAsync(joiningLobby.Id, AuthenticationService.Instance.PlayerId);

                joiningLobby = null;

                OnPlayerLeftLobby?.Invoke(this, EventArgs.Empty);
            }
            catch (LobbyServiceException e)
            {
                Debug.Log(e);
            }
        }
    }


    //when you have to remove or kick your opponent in the lobby
    public async void KickPlayer(string playerId)
    {
        if (IsLobbyHostCreatedByPlayer())
        {
            try
            {
                await LobbyService.Instance.RemovePlayerAsync(joiningLobby.Id, playerId);
            }
            catch (LobbyServiceException e)
            {
                Debug.Log(e);
            }
        }
    }

    public void StartGame()
    {
        if (!sceneLoaded)
        {
            sceneLoaded = true;
            LoadGameScene();
        }
    }

    // Method to load the game scene
    void LoadGameScene()
    {
        SceneManager.LoadSceneAsync(2);
    }

    public void OnStartHost()
    {
        NetworkManager.OnServerStarted += OnServerStarted;
        NetworkManager.StartHost();
    }

    private void OnServerStarted()
    {
        NetworkManager.OnServerStarted -= OnServerStarted;
        // This only needs to be set on the host or server side and is sent to clients during synchronization.
        // This setting tells clients to re-use scenes that are already loaded.
        NetworkManager.SceneManager.SetClientSynchronizationMode(LoadSceneMode.Additive);
    }


    //when host leaves the lobby and then rejoined again, the player cannot modify any of the options such as timer, etc.
    public async void MigratePlayer()
    {
        try
        {
            Lobby lobby = await Lobbies.Instance.UpdateLobbyAsync(joiningLobby.Id, new UpdateLobbyOptions
            {
                HostId = joiningLobby.Players[1].Id
            });

            joiningLobby = lobby;
            OnLobbyTimerChanged?.Invoke(this, new LobbyEventArgs { lobby = joiningLobby });
        }
        catch (LobbyServiceException e)
        {
            Debug.Log(e);
        }
    }

    }

When I tried to press the “start game” button based on “StartGame” it doesn’t sync from one game to another.

the build version stuck on the lobby screen while the other one in the editor proceeds to play the scrabble scene.

I’m terribly sorry if this thread is too long. I really need your help as soon as possible. cuz i’m not good at C#. Any help is appreciated. Thank you!

Also, what I trying to say is that (UILettersPanel_p1) is the host, while (UILettersPanel_p2) is the client