Search Unity

  1. If you have experience with import & exporting custom (.unitypackage) packages, please help complete a survey (open until May 15, 2024).
    Dismiss Notice
  2. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice

NAT Traversal plugin + Unity's Network Lobby system?

Discussion in 'Multiplayer' started by CloudyVR, Jun 8, 2017.

  1. CloudyVR

    CloudyVR

    Joined:
    Mar 26, 2017
    Posts:
    715
    I am using both the NAT Traversal plugin and Unity's new Network Lobby system in my project. Individually I am able to use the examples and I understand how to make each plugin work by themselves. But my problem is that I can not figure out how to make them work together.

    I am new to networking in Unity and I have been reading a lot about the subject (trying examples too). However this seems to be becoming a very large task (4 days worth of attempts) and currently feels to be beyond my abilities.

    I was wondering and really hoping someone knowledgeable could help me understand how to make Unity's network lobby inherit from NATTraversal.NetworkManager instead of UnityEngine.NetworkManager and use NAT Punchthrough?

    After importing the Network Lobby plugin into a new project I noticed in the provided example LobbyManager prefab it uses a script called LobbyManager.cs which inherits from UnityEngine.NetworkLobbyManager and ultimately inherits from UnityEngine.NetworkManager, so the UnityEngine.NetworkLobbyManager is an assembly that I can not modify to change it's inheritance to the desired NATTraversal.NetworkManager.

    My attempt (mostly just shooting in the dark) was to change the inheritance in LobbyManager.cs from UnityEngine.NetworkLobbyManager to NATTraversal.NATLobbyManager which ultimately inherits from NATTraversal.NetworkManager. I am not sure if this is even remotely correct but I just wanted to see how things went.

    Upon doing the above I noticed some errors associated with missing references, I tried to fill in the blanks the best I could and correct any errors. The code now compiles and does "seem" to launch LAN matches, but whenever I try to create an internet match, the server's lobby window hangs forever on the "Connecting..." dialog. But I do see the server listed on the client after clicking List Server. So some things work. However when I attempt to join I get a NullReferenceException error for OnMatchJoined()

    Script attached to LobbyManager prefab (controls networking and the visibility of menu panels):
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4. using UnityEngine.SceneManagement;
    5. using UnityEngine.Networking;
    6. using UnityEngine.Networking.Types;
    7. using UnityEngine.Networking.Match;
    8. using System.Collections;
    9.  
    10. namespace Prototype.NetworkLobby
    11. {
    12.     public class LobbyManager : NATTraversal.NATLobbyManager
    13.     {
    14.         static short MsgKicked = MsgType.Highest + 1;
    15.  
    16.         static public LobbyManager s_Singleton;
    17.  
    18.  
    19.         [Header("Unity UI Lobby")]
    20.         [Tooltip("Time in second between all players ready & match start")]
    21.         public float prematchCountdown = 5.0f;
    22.  
    23.         [Space]
    24.         [Header("UI Reference")]
    25.         public LobbyTopPanel topPanel;
    26.  
    27.         public RectTransform mainMenuPanel;
    28.         public RectTransform lobbyPanel;
    29.  
    30.         public LobbyInfoPanel infoPanel;
    31.         public LobbyCountdownPanel countdownPanel;
    32.         public GameObject addPlayerButton;
    33.  
    34.         protected RectTransform currentPanel;
    35.  
    36.         public Button backButton;
    37.  
    38.         public Text statusInfo;
    39.         public Text hostInfo;
    40.  
    41.         //Client numPlayers from NetworkManager is always 0, so we count (throught connect/destroy in LobbyPlayer) the number
    42.         //of players, so that even client know how many player there is.
    43.         [HideInInspector]
    44.         public int _playerNumber = 0;
    45.  
    46.         //used to disconnect a client properly when exiting the matchmaker
    47.         [HideInInspector]
    48.         public bool _isMatchmaking = false;
    49.  
    50.         protected bool _disconnectServer = false;
    51.      
    52.         protected ulong _currentMatchID;
    53.  
    54.         protected LobbyHook _lobbyHooks;
    55.  
    56.         void Start()
    57.         {
    58.             base.Start ();
    59.             s_Singleton = this;
    60.             _lobbyHooks = GetComponent<Prototype.NetworkLobby.LobbyHook>();
    61.             currentPanel = mainMenuPanel;
    62.  
    63.             backButton.gameObject.SetActive(false);
    64.             GetComponent<Canvas>().enabled = true;
    65.  
    66.             DontDestroyOnLoad(gameObject);
    67.  
    68.             SetServerInfo("Offline", "None");
    69.         }
    70.  
    71.         public override void OnLobbyClientSceneChanged(NetworkConnection conn)
    72.         {
    73.             if (SceneManager.GetSceneAt(0).name == lobbyScene)
    74.             {
    75.                 if (topPanel.isInGame)
    76.                 {
    77.                     ChangeTo(lobbyPanel);
    78.                     if (_isMatchmaking)
    79.                     {
    80.                         if (conn.playerControllers[0].unetView.isServer)
    81.                         {
    82.                             backDelegate = StopHostClbk;
    83.                         }
    84.                         else
    85.                         {
    86.                             backDelegate = StopClientClbk;
    87.                         }
    88.                     }
    89.                     else
    90.                     {
    91.                         if (conn.playerControllers[0].unetView.isClient)
    92.                         {
    93.                             backDelegate = StopHostClbk;
    94.                         }
    95.                         else
    96.                         {
    97.                             backDelegate = StopClientClbk;
    98.                         }
    99.                     }
    100.                 }
    101.                 else
    102.                 {
    103.                     ChangeTo(mainMenuPanel);
    104.                 }
    105.  
    106.                 topPanel.ToggleVisibility(true);
    107.                 topPanel.isInGame = false;
    108.             }
    109.             else
    110.             {
    111.                 ChangeTo(null);
    112.  
    113.                 Destroy(GameObject.Find("MainMenuUI(Clone)"));
    114.  
    115.                 //backDelegate = StopGameClbk;
    116.                 topPanel.isInGame = true;
    117.                 topPanel.ToggleVisibility(false);
    118.             }
    119.         }
    120.  
    121.         public void ChangeTo(RectTransform newPanel)
    122.         {
    123.             if (currentPanel != null)
    124.             {
    125.                 currentPanel.gameObject.SetActive(false);
    126.             }
    127.  
    128.             if (newPanel != null)
    129.             {
    130.                 newPanel.gameObject.SetActive(true);
    131.             }
    132.  
    133.             currentPanel = newPanel;
    134.  
    135.             if (currentPanel != mainMenuPanel)
    136.             {
    137.                 backButton.gameObject.SetActive(true);
    138.             }
    139.             else
    140.             {
    141.                 backButton.gameObject.SetActive(false);
    142.                 SetServerInfo("Offline", "None");
    143.                 _isMatchmaking = false;
    144.             }
    145.         }
    146.  
    147.         public void DisplayIsConnecting()
    148.         {
    149.             var _this = this;
    150.             infoPanel.Display("Connecting...", "Cancel", () => {
    151.                 _this.backDelegate();
    152.          
    153.             });
    154.         }
    155.  
    156.         public void SetServerInfo(string status, string host)
    157.         {
    158.             statusInfo.text = status;
    159.             hostInfo.text = host;
    160.         }
    161.  
    162.  
    163.         public delegate void BackButtonDelegate();
    164.         public BackButtonDelegate backDelegate;
    165.         public void GoBackButton()
    166.         {
    167.             backDelegate();
    168.             topPanel.isInGame = false;
    169.         }
    170.  
    171.         // ----------------- Server management
    172.  
    173.         public void AddLocalPlayer()
    174.         {
    175.             TryToAddPlayer();
    176.         }
    177.  
    178.         public void RemovePlayer(LobbyPlayer player)
    179.         {
    180.             player.RemovePlayer();
    181.         }
    182.  
    183.         public void SimpleBackClbk()
    184.         {
    185.             ChangeTo(mainMenuPanel);
    186.         }
    187.                
    188.         public void StopHostClbk()
    189.         {
    190.             if (_isMatchmaking)
    191.             {
    192.                 matchMaker.DestroyMatch((NetworkID)_currentMatchID, 0, OnDestroyMatch);
    193.                 _disconnectServer = true;
    194.             }
    195.             else
    196.             {
    197.                 Debug.LogError  ("STOPPING NOW");
    198.                 NetworkManager manager = this.GetComponent<NetworkManager>();
    199.                 manager.StopHost();
    200.             }
    201.  
    202.          
    203.             ChangeTo(mainMenuPanel);
    204.         }
    205.  
    206.         public void StopClientClbk()
    207.         {
    208.             NetworkManager manager = this.GetComponent<NetworkManager>();
    209.             manager.StopClient();
    210.  
    211.             if (_isMatchmaking)
    212.             {
    213.                 StopMatchMaker();
    214.             }
    215.  
    216.             ChangeTo(mainMenuPanel);
    217.         }
    218.  
    219.         public void StopServerClbk()
    220.         {
    221.             NetworkManager manager = this.GetComponent<NetworkManager>();
    222.             manager.StopServer();
    223.             ChangeTo(mainMenuPanel);
    224.         }
    225.  
    226.         class KickMsg : MessageBase { }
    227.         public void KickPlayer(NetworkConnection conn)
    228.         {
    229.             conn.Send(MsgKicked, new KickMsg());
    230.         }
    231.  
    232.  
    233.  
    234.  
    235.         public void KickedMessageHandler(NetworkMessage netMsg)
    236.         {
    237.             infoPanel.Display("Kicked by Server", "Close", null);
    238.             netMsg.conn.Disconnect();
    239.         }
    240.  
    241.         //===================
    242.  
    243.         public override void OnStartHost()
    244.         {
    245.             base.OnStartHost();
    246.  
    247.             ChangeTo(lobbyPanel);
    248.             backDelegate = StopHostClbk;
    249.             SetServerInfo("Hosting", networkAddress);
    250.         }
    251.  
    252.         public override void OnMatchJoined(bool success, string extendedInfo, MatchInfo matchInfo)
    253.         {
    254.             //base.OnMatchJoined (success, extendedInfo, matchInfo);
    255.      
    256.         }
    257.  
    258.         public override void OnMatchCreate(bool success, string extendedInfo, MatchInfo matchInfo)
    259.         {
    260.             _currentMatchID = (System.UInt64)matchInfo.networkId;
    261.             base.OnMatchCreate(success, extendedInfo, matchInfo);
    262.  
    263.         }
    264.  
    265.         public override void OnDestroyMatch(bool success, string extendedInfo)
    266.         {
    267.             base.OnDestroyMatch(success, extendedInfo);
    268.             if (_disconnectServer)
    269.             {
    270.                 StopMatchMaker();
    271.                 StopHost();
    272.             }
    273.         }
    274.  
    275.         //allow to handle the (+) button to add/remove player
    276.         public void OnPlayersNumberModified(int count)
    277.         {
    278.             _playerNumber += count;
    279.  
    280.             int localPlayerCount = 0;
    281.             foreach (PlayerController p in ClientScene.localPlayers)
    282.                 localPlayerCount += (p == null || p.playerControllerId == -1) ? 0 : 1;
    283.  
    284.             addPlayerButton.SetActive(localPlayerCount < maxPlayersPerConnection && _playerNumber < maxPlayers);
    285.         }
    286.  
    287.         // ----------------- Server callbacks ------------------
    288.  
    289.         //we want to disable the button JOIN if we don't have enough player
    290.         //But OnLobbyClientConnect isn't called on hosting player. So we override the lobbyPlayer creation
    291.         public override GameObject OnLobbyServerCreateLobbyPlayer(NetworkConnection conn, short playerControllerId)
    292.         {
    293.             Debug.LogError  ("CREATE LOBBY PLAYER");
    294.             GameObject obj = Instantiate(lobbyPlayerPrefab.gameObject) as GameObject;
    295.  
    296.             LobbyPlayer newPlayer = obj.GetComponent<LobbyPlayer>();
    297.             newPlayer.ToggleJoinButton(numPlayers + 1 >= minPlayers);
    298.  
    299.  
    300.             for (int i = 0; i < lobbySlots.Length; ++i)
    301.             {
    302.                 LobbyPlayer p = lobbySlots[i] as LobbyPlayer;
    303.  
    304.                 if (p != null)
    305.                 {
    306.                     p.RpcUpdateRemoveButton();
    307.                     p.ToggleJoinButton(numPlayers + 1 >= minPlayers);
    308.                 }
    309.             }
    310.  
    311.             return obj;
    312.         }
    313.  
    314.         public override void OnLobbyServerPlayerRemoved(NetworkConnection conn, short playerControllerId)
    315.         {
    316.             for (int i = 0; i < lobbySlots.Length; ++i)
    317.             {
    318.                 LobbyPlayer p = lobbySlots[i] as LobbyPlayer;
    319.  
    320.                 if (p != null)
    321.                 {
    322.                     p.RpcUpdateRemoveButton();
    323.                     p.ToggleJoinButton(numPlayers + 1 >= minPlayers);
    324.                 }
    325.             }
    326.         }
    327.  
    328.         public override void OnLobbyServerDisconnect(NetworkConnection conn)
    329.         {
    330.             for (int i = 0; i < lobbySlots.Length; ++i)
    331.             {
    332.                 LobbyPlayer p = lobbySlots[i] as LobbyPlayer;
    333.  
    334.                 if (p != null)
    335.                 {
    336.                     p.RpcUpdateRemoveButton();
    337.                     p.ToggleJoinButton(numPlayers >= minPlayers);
    338.                 }
    339.             }
    340.  
    341.         }
    342.  
    343.         public override bool OnLobbyServerSceneLoadedForPlayer(GameObject lobbyPlayer, GameObject gamePlayer)
    344.         {
    345.             //This hook allows you to apply state data from the lobby-player to the game-player
    346.             //just subclass "LobbyHook" and add it to the lobby object.
    347.  
    348.             if (_lobbyHooks)
    349.                 _lobbyHooks.OnLobbyServerSceneLoadedForPlayer(this, lobbyPlayer, gamePlayer);
    350.  
    351.             return true;
    352.         }
    353.  
    354.         // --- Countdown management
    355.  
    356.         public override void OnLobbyServerPlayersReady()
    357.         {
    358.             bool allready = true;
    359.             for(int i = 0; i < lobbySlots.Length; ++i)
    360.             {
    361.                 if(lobbySlots[i] != null)
    362.                     allready &= lobbySlots[i].readyToBegin;
    363.             }
    364.  
    365.             if(allready)
    366.                 StartCoroutine(ServerCountdownCoroutine());
    367.         }
    368.  
    369.         public IEnumerator ServerCountdownCoroutine()
    370.         {
    371.             float remainingTime = prematchCountdown;
    372.             int floorTime = Mathf.FloorToInt(remainingTime);
    373.  
    374.             while (remainingTime > 0)
    375.             {
    376.                 yield return null;
    377.  
    378.                 remainingTime -= Time.deltaTime;
    379.                 int newFloorTime = Mathf.FloorToInt(remainingTime);
    380.  
    381.                 if (newFloorTime != floorTime)
    382.                 {//to avoid flooding the network of message, we only send a notice to client when the number of plain seconds change.
    383.                     floorTime = newFloorTime;
    384.  
    385.                     for (int i = 0; i < lobbySlots.Length; ++i)
    386.                     {
    387.                         if (lobbySlots[i] != null)
    388.                         {//there is maxPlayer slots, so some could be == null, need to test it before accessing!
    389.                             (lobbySlots[i] as LobbyPlayer).RpcUpdateCountdown(floorTime);
    390.                         }
    391.                     }
    392.                 }
    393.             }
    394.  
    395.             for (int i = 0; i < lobbySlots.Length; ++i)
    396.             {
    397.                 if (lobbySlots[i] != null)
    398.                 {
    399.                     (lobbySlots[i] as LobbyPlayer).RpcUpdateCountdown(0);
    400.                 }
    401.             }
    402.  
    403.             ServerChangeScene(playScene);
    404.         }
    405.  
    406.         // ----------------- Client callbacks ------------------
    407.  
    408.  
    409.  
    410.         public override void OnClientConnect(NetworkConnection conn)
    411.         {
    412.             Debug.LogError  ("OnClientConnect");
    413.             base.OnClientConnect(conn);
    414.  
    415.             infoPanel.gameObject.SetActive(false);
    416.  
    417.             conn.RegisterHandler(MsgKicked, KickedMessageHandler);
    418.  
    419.             if (!NetworkServer.active)
    420.             {//only to do on pure client (not self hosting client)
    421.                 ChangeTo(lobbyPanel);
    422.                 backDelegate = StopClientClbk;
    423.                 SetServerInfo("Client", networkAddress);
    424.             }
    425.         }
    426.  
    427.  
    428.         public override void OnClientDisconnect(NetworkConnection conn)
    429.         {
    430.             base.OnClientDisconnect(conn);
    431.             ChangeTo(mainMenuPanel);
    432.         }
    433.  
    434.         public override void OnClientError(NetworkConnection conn, int errorCode)
    435.         {
    436.             ChangeTo(mainMenuPanel);
    437.             infoPanel.Display("Cient error : " + (errorCode == 6 ? "timeout" : errorCode.ToString()), "Close", null);
    438.         }
    439.     }
    440. }
    441.  
    442.  

    Script component attached to PlayerInfo prefab (inheriting from NATTraversal.NATLobbyPlayer):
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4. using UnityEngine.Networking;
    5. using System.Collections;
    6. using System.Collections.Generic;
    7. using System.Linq;
    8.  
    9. namespace Prototype.NetworkLobby
    10. {
    11.     //Player entry in the lobby. Handle selecting color/setting name & getting ready for the game
    12.     //Any LobbyHook can then grab it and pass those value to the game player prefab (see the Pong Example in the Samples Scenes)
    13.     public class LobbyPlayer : NATTraversal.NATLobbyPlayer
    14.     {
    15.         static Color[] Colors = new Color[] { Color.magenta, Color.red, Color.cyan, Color.blue, Color.green, Color.yellow };
    16.         //used on server to avoid assigning the same color to two player
    17.         static List<int> _colorInUse = new List<int>();
    18.  
    19.         public Button colorButton;
    20.         public InputField nameInput;
    21.         public Button readyButton;
    22.         public Button waitingPlayerButton;
    23.         public Button removePlayerButton;
    24.  
    25.         public GameObject localIcone;
    26.         public GameObject remoteIcone;
    27.  
    28.         //OnMyName function will be invoked on clients when server change the value of playerName
    29.         [SyncVar(hook = "OnMyName")]
    30.         public string playerName = "";
    31.         [SyncVar(hook = "OnMyColor")]
    32.         public Color playerColor = Color.white;
    33.  
    34.         public Color OddRowColor = new Color(250.0f / 255.0f, 250.0f / 255.0f, 250.0f / 255.0f, 1.0f);
    35.         public Color EvenRowColor = new Color(180.0f / 255.0f, 180.0f / 255.0f, 180.0f / 255.0f, 1.0f);
    36.  
    37.         static Color JoinColor = new Color(255.0f/255.0f, 0.0f, 101.0f/255.0f,1.0f);
    38.         static Color NotReadyColor = new Color(34.0f / 255.0f, 44 / 255.0f, 55.0f / 255.0f, 1.0f);
    39.         static Color ReadyColor = new Color(0.0f, 204.0f / 255.0f, 204.0f / 255.0f, 1.0f);
    40.         static Color TransparentColor = new Color(0, 0, 0, 0);
    41.  
    42.         //static Color OddRowColor = new Color(250.0f / 255.0f, 250.0f / 255.0f, 250.0f / 255.0f, 1.0f);
    43.         //static Color EvenRowColor = new Color(180.0f / 255.0f, 180.0f / 255.0f, 180.0f / 255.0f, 1.0f);
    44.  
    45.  
    46.         public override void OnClientEnterLobby()
    47.         {
    48.             base.OnClientEnterLobby();
    49.  
    50.             if (LobbyManager.s_Singleton != null) LobbyManager.s_Singleton.OnPlayersNumberModified(1);
    51.  
    52.             LobbyPlayerList._instance.AddPlayer(this);
    53.             LobbyPlayerList._instance.DisplayDirectServerWarning(isServer && LobbyManager.s_Singleton.matchMaker == null);
    54.  
    55.             if (isLocalPlayer)
    56.             {
    57.                 SetupLocalPlayer();
    58.             }
    59.             else
    60.             {
    61.                 SetupOtherPlayer();
    62.             }
    63.  
    64.             //setup the player data on UI. The value are SyncVar so the player
    65.             //will be created with the right value currently on server
    66.             OnMyName(playerName);
    67.             OnMyColor(playerColor);
    68.         }
    69.  
    70.         public override void OnStartAuthority()
    71.         {
    72.             base.OnStartAuthority();
    73.  
    74.             //if we return from a game, color of text can still be the one for "Ready"
    75.             readyButton.transform.GetChild(0).GetComponent<Text>().color = Color.white;
    76.  
    77.            SetupLocalPlayer();
    78.         }
    79.  
    80.         void ChangeReadyButtonColor(Color c)
    81.         {
    82.             ColorBlock b = readyButton.colors;
    83.             b.normalColor = c;
    84.             b.pressedColor = c;
    85.             b.highlightedColor = c;
    86.             b.disabledColor = c;
    87.             readyButton.colors = b;
    88.         }
    89.  
    90.         void SetupOtherPlayer()
    91.         {
    92.             nameInput.interactable = false;
    93.             removePlayerButton.interactable = NetworkServer.active;
    94.  
    95.             ChangeReadyButtonColor(NotReadyColor);
    96.  
    97.             readyButton.transform.GetChild(0).GetComponent<Text>().text = "...";
    98.             readyButton.interactable = false;
    99.  
    100.             OnClientReady(false);
    101.         }
    102.  
    103.         void SetupLocalPlayer()
    104.         {
    105.             nameInput.interactable = true;
    106.             remoteIcone.gameObject.SetActive(false);
    107.             localIcone.gameObject.SetActive(true);
    108.  
    109.             CheckRemoveButton();
    110.  
    111.             if (playerColor == Color.white)
    112.                 CmdColorChange();
    113.  
    114.             ChangeReadyButtonColor(JoinColor);
    115.  
    116.             readyButton.transform.GetChild(0).GetComponent<Text>().text = "JOIN";
    117.             readyButton.interactable = true;
    118.  
    119.             //have to use child count of player prefab already setup as "this.slot" is not set yet
    120.             if (playerName == "")
    121.                 CmdNameChanged("Player" + (LobbyPlayerList._instance.playerListContentTransform.childCount-1));
    122.  
    123.             //we switch from simple name display to name input
    124.             colorButton.interactable = true;
    125.             nameInput.interactable = true;
    126.  
    127.             nameInput.onEndEdit.RemoveAllListeners();
    128.             nameInput.onEndEdit.AddListener(OnNameChanged);
    129.  
    130.             colorButton.onClick.RemoveAllListeners();
    131.             colorButton.onClick.AddListener(OnColorClicked);
    132.  
    133.             readyButton.onClick.RemoveAllListeners();
    134.             readyButton.onClick.AddListener(OnReadyClicked);
    135.  
    136.             //when OnClientEnterLobby is called, the loval PlayerController is not yet created, so we need to redo that here to disable
    137.             //the add button if we reach maxLocalPlayer. We pass 0, as it was already counted on OnClientEnterLobby
    138.             if (LobbyManager.s_Singleton != null) LobbyManager.s_Singleton.OnPlayersNumberModified(0);
    139.         }
    140.  
    141.         //This enable/disable the remove button depending on if that is the only local player or not
    142.         public void CheckRemoveButton()
    143.         {
    144.             if (!isLocalPlayer)
    145.                 return;
    146.  
    147.             int localPlayerCount = 0;
    148.             foreach (PlayerController p in ClientScene.localPlayers)
    149.                 localPlayerCount += (p == null || p.playerControllerId == -1) ? 0 : 1;
    150.  
    151.             removePlayerButton.interactable = localPlayerCount > 1;
    152.         }
    153.  
    154.         public override void OnClientReady(bool readyState)
    155.         {
    156.             if (readyState)
    157.             {
    158.                 ChangeReadyButtonColor(TransparentColor);
    159.  
    160.                 Text textComponent = readyButton.transform.GetChild(0).GetComponent<Text>();
    161.                 textComponent.text = "READY";
    162.                 textComponent.color = ReadyColor;
    163.                 readyButton.interactable = false;
    164.                 colorButton.interactable = false;
    165.                 nameInput.interactable = false;
    166.             }
    167.             else
    168.             {
    169.                 ChangeReadyButtonColor(isLocalPlayer ? JoinColor : NotReadyColor);
    170.  
    171.                 Text textComponent = readyButton.transform.GetChild(0).GetComponent<Text>();
    172.                 textComponent.text = isLocalPlayer ? "JOIN" : "...";
    173.                 textComponent.color = Color.white;
    174.                 readyButton.interactable = isLocalPlayer;
    175.                 colorButton.interactable = isLocalPlayer;
    176.                 nameInput.interactable = isLocalPlayer;
    177.             }
    178.         }
    179.  
    180.         public void OnPlayerListChanged(int idx)
    181.         {
    182.             GetComponent<Image>().color = (idx % 2 == 0) ? EvenRowColor : OddRowColor;
    183.         }
    184.  
    185.         ///===== callback from sync var
    186.  
    187.         public void OnMyName(string newName)
    188.         {
    189.             playerName = newName;
    190.             nameInput.text = playerName;
    191.         }
    192.  
    193.         public void OnMyColor(Color newColor)
    194.         {
    195.             playerColor = newColor;
    196.             colorButton.GetComponent<Image>().color = newColor;
    197.         }
    198.  
    199.         //===== UI Handler
    200.  
    201.         //Note that those handler use Command function, as we need to change the value on the server not locally
    202.         //so that all client get the new value throught syncvar
    203.         public void OnColorClicked()
    204.         {
    205.             CmdColorChange();
    206.         }
    207.  
    208.         public void OnReadyClicked()
    209.         {
    210.             SendReadyToBeginMessage();
    211.         }
    212.  
    213.         public void OnNameChanged(string str)
    214.         {
    215.             CmdNameChanged(str);
    216.         }
    217.  
    218.         public void OnRemovePlayerClick()
    219.         {
    220.             if (isLocalPlayer)
    221.             {
    222.                 RemovePlayer();
    223.             }
    224.             else if (isServer)
    225.                 LobbyManager.s_Singleton.KickPlayer(connectionToClient);
    226.  
    227.         }
    228.  
    229.         public void ToggleJoinButton(bool enabled)
    230.         {
    231.             readyButton.gameObject.SetActive(enabled);
    232.             waitingPlayerButton.gameObject.SetActive(!enabled);
    233.         }
    234.  
    235.         [ClientRpc]
    236.         public void RpcUpdateCountdown(int countdown)
    237.         {
    238.             LobbyManager.s_Singleton.countdownPanel.UIText.text = "Match Starting in " + countdown;
    239.             LobbyManager.s_Singleton.countdownPanel.gameObject.SetActive(countdown != 0);
    240.         }
    241.  
    242.         [ClientRpc]
    243.         public void RpcUpdateRemoveButton()
    244.         {
    245.             CheckRemoveButton();
    246.         }
    247.  
    248.         //====== Server Command
    249.  
    250.         [Command]
    251.         public void CmdColorChange()
    252.         {
    253.             int idx = System.Array.IndexOf(Colors, playerColor);
    254.  
    255.             int inUseIdx = _colorInUse.IndexOf(idx);
    256.  
    257.             if (idx < 0) idx = 0;
    258.  
    259.             idx = (idx + 1) % Colors.Length;
    260.  
    261.             bool alreadyInUse = false;
    262.  
    263.             do
    264.             {
    265.                 alreadyInUse = false;
    266.                 for (int i = 0; i < _colorInUse.Count; ++i)
    267.                 {
    268.                     if (_colorInUse[i] == idx)
    269.                     {//that color is already in use
    270.                         alreadyInUse = true;
    271.                         idx = (idx + 1) % Colors.Length;
    272.                     }
    273.                 }
    274.             }
    275.             while (alreadyInUse);
    276.  
    277.             if (inUseIdx >= 0)
    278.             {//if we already add an entry in the colorTabs, we change it
    279.                 _colorInUse[inUseIdx] = idx;
    280.             }
    281.             else
    282.             {//else we add it
    283.                 _colorInUse.Add(idx);
    284.             }
    285.  
    286.             playerColor = Colors[idx];
    287.         }
    288.  
    289.         [Command]
    290.         public void CmdNameChanged(string name)
    291.         {
    292.             playerName = name;
    293.         }
    294.  
    295.         //Cleanup thing when get destroy (which happen when client kick or disconnect)
    296.         public void OnDestroy()
    297.         {
    298.             LobbyPlayerList._instance.RemovePlayer(this);
    299.             if (LobbyManager.s_Singleton != null) LobbyManager.s_Singleton.OnPlayersNumberModified(-1);
    300.  
    301.             int idx = System.Array.IndexOf(Colors, playerColor);
    302.  
    303.             if (idx < 0)
    304.                 return;
    305.  
    306.             for (int i = 0; i < _colorInUse.Count; ++i)
    307.             {
    308.                 if (_colorInUse[i] == idx)
    309.                 {//that color is already in use
    310.                     _colorInUse.RemoveAt(i);
    311.                     break;
    312.                 }
    313.             }
    314.         }
    315.     }
    316. }
    317.  
    318.  

    The base class of the above LobbyPlayer:
    Code (csharp):
    1.  
    2. using System;
    3. using UnityEngine;
    4. using UnityEngine.Networking;
    5. using UnityEngine.Networking.NetworkSystem;
    6. using UnityEngine.SceneManagement;
    7.  
    8. namespace NATTraversal
    9. {
    10.     [DisallowMultipleComponent]
    11.     public class NATLobbyPlayer : NetworkBehaviour
    12.     {
    13.         [SerializeField]
    14.         public bool ShowLobbyGUI = true;
    15.  
    16.         byte m_Slot;
    17.         bool m_ReadyToBegin;
    18.  
    19.         public byte slot { get { return m_Slot; } set { m_Slot = value; } }
    20.         public bool readyToBegin { get { return m_ReadyToBegin; } set { m_ReadyToBegin = value; } }
    21.  
    22.         void Start()
    23.         {
    24.             DontDestroyOnLoad(gameObject);
    25.         }
    26.  
    27.         void OnEnable()
    28.         {
    29.             SceneManager.sceneLoaded += OnSceneLoaded;
    30.         }
    31.  
    32.         void OnDisable()
    33.         {
    34.             SceneManager.sceneLoaded -= OnSceneLoaded;
    35.         }
    36.  
    37.         public override void OnStartClient()
    38.         {
    39.             var lobby = NetworkManager.singleton as NATLobbyManager;
    40.             if (lobby)
    41.             {
    42.                 lobby.lobbySlots[m_Slot] = this;
    43.                 m_ReadyToBegin = false;
    44.                 OnClientEnterLobby();
    45.             }
    46.             else
    47.             {
    48.                 Debug.LogError("LobbyPlayer could not find a NATLobbyManager. The LobbyPlayer requires a NATLobbyManager object to function. Make sure that there is one in the scene.");
    49.             }
    50.         }
    51.  
    52.         public void SendReadyToBeginMessage()
    53.         {
    54.             if (LogFilter.logDebug) { Debug.Log("NATLobbyPlayer SendReadyToBeginMessage"); }
    55.  
    56.             var lobby = NetworkManager.singleton as NATLobbyManager;
    57.             if (lobby)
    58.             {
    59.                 var msg = new NATLobbyManager.LobbyReadyToBeginMessage();
    60.                 msg.slotId = (byte)playerControllerId;
    61.                 msg.readyState = true;
    62.                 lobby.client.Send(MsgType.LobbyReadyToBegin, msg);
    63.             }
    64.         }
    65.  
    66.         public void SendNotReadyToBeginMessage()
    67.         {
    68.             if (LogFilter.logDebug) { Debug.Log("NATLobbyPlayer SendReadyToBeginMessage"); }
    69.  
    70.             var lobby = NetworkManager.singleton as NATLobbyManager;
    71.             if (lobby)
    72.             {
    73.                 var msg = new NATLobbyManager.LobbyReadyToBeginMessage();
    74.                 msg.slotId = (byte)playerControllerId;
    75.                 msg.readyState = false;
    76.                 lobby.client.Send(MsgType.LobbyReadyToBegin, msg);
    77.             }
    78.         }
    79.  
    80.         public void SendSceneLoadedMessage()
    81.         {
    82.             if (LogFilter.logDebug) { Debug.Log("NATLobbyPlayer SendSceneLoadedMessage"); }
    83.  
    84.             var lobby = NetworkManager.singleton as NATLobbyManager;
    85.             if (lobby)
    86.             {
    87.                 var msg = new IntegerMessage(playerControllerId);
    88.                 lobby.client.Send(MsgType.LobbySceneLoaded, msg);
    89.             }
    90.         }
    91.  
    92.         void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    93.         {
    94.             var lobby = NetworkManager.singleton as NATLobbyManager;
    95.             if (lobby)
    96.             {
    97.                 // dont even try this in the startup scene
    98.                 // Should we check if the LoadSceneMode is Single or Additive??
    99.                 // Can the lobby scene be loaded Additively??
    100.                 string loadedSceneName = scene.name;
    101.                 if (loadedSceneName == lobby.lobbyScene)
    102.                 {
    103.                     return;
    104.                 }
    105.             }
    106.  
    107.             if (isLocalPlayer)
    108.             {
    109.                 SendSceneLoadedMessage();
    110.             }
    111.         }
    112.  
    113.         public void RemovePlayer()
    114.         {
    115.             if (isLocalPlayer && !m_ReadyToBegin)
    116.             {
    117.                 if (LogFilter.logDebug) { Debug.Log("NATLobbyPlayer RemovePlayer"); }
    118.  
    119.                 ClientScene.RemovePlayer(GetComponent<NetworkIdentity>().playerControllerId);
    120.             }
    121.         }
    122.  
    123.         // ------------------------ callbacks ------------------------
    124.  
    125.         public virtual void OnClientEnterLobby()
    126.         {
    127.         }
    128.  
    129.         public virtual void OnClientExitLobby()
    130.         {
    131.         }
    132.  
    133.         public virtual void OnClientReady(bool readyState)
    134.         {
    135.         }
    136.  
    137.         // ------------------------ Custom Serialization ------------------------
    138.  
    139.         public override bool OnSerialize(NetworkWriter writer, bool initialState)
    140.         {
    141.             // dirty flag
    142.             writer.WritePackedUInt32(1);
    143.  
    144.             writer.Write(m_Slot);
    145.             writer.Write(m_ReadyToBegin);
    146.             return true;
    147.         }
    148.  
    149.         public override void OnDeserialize(NetworkReader reader, bool initialState)
    150.         {
    151.             var dirty = reader.ReadPackedUInt32();
    152.             if (dirty == 0)
    153.                 return;
    154.  
    155.             m_Slot = reader.ReadByte();
    156.             m_ReadyToBegin = reader.ReadBoolean();
    157.         }
    158.  
    159.         // ------------------------ optional UI ------------------------
    160.  
    161.         void OnGUI()
    162.         {
    163.             if (!ShowLobbyGUI)
    164.                 return;
    165.  
    166.             var lobby = NetworkManager.singleton as NATLobbyManager;
    167.             if (lobby)
    168.             {
    169.                 if (!lobby.showLobbyGUI)
    170.                     return;
    171.  
    172.                 string loadedSceneName = SceneManager.GetSceneAt(0).name;
    173.                 if (loadedSceneName != lobby.lobbyScene)
    174.                     return;
    175.             }
    176.  
    177.             Rect rec = new Rect(100 + m_Slot * 100, 200, 90, 20);
    178.  
    179.             if (isLocalPlayer)
    180.             {
    181.                 string youStr;
    182.                 if (m_ReadyToBegin)
    183.                 {
    184.                     youStr = "(Ready)";
    185.                 }
    186.                 else
    187.                 {
    188.                     youStr = "(Not Ready)";
    189.                 }
    190.                 GUI.Label(rec, youStr);
    191.  
    192.                 if (m_ReadyToBegin)
    193.                 {
    194.                     rec.y += 25;
    195.                     if (GUI.Button(rec, "STOP"))
    196.                     {
    197.                         SendNotReadyToBeginMessage();
    198.                     }
    199.                 }
    200.                 else
    201.                 {
    202.                     rec.y += 25;
    203.                     if (GUI.Button(rec, "START"))
    204.                     {
    205.                         SendReadyToBeginMessage();
    206.                     }
    207.  
    208.                     rec.y += 25;
    209.                     if (GUI.Button(rec, "Remove"))
    210.                     {
    211.                         ClientScene.RemovePlayer(GetComponent<NetworkIdentity>().playerControllerId);
    212.                     }
    213.                 }
    214.             }
    215.             else
    216.             {
    217.                 GUI.Label(rec, "Player [" + netId + "]");
    218.                 rec.y += 25;
    219.                 GUI.Label(rec, "Ready [" + m_ReadyToBegin + "]");
    220.             }
    221.         }
    222.     }
    223. }
    224.  

    The call to StartHostAll() is contained in this script and is fired when the join button is clicked:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4. using System.Collections;
    5.  
    6. namespace Prototype.NetworkLobby
    7. {
    8.     //Main menu, mainly only a bunch of callback called by the UI (setup throught the Inspector)
    9.     public class LobbyMainMenu : MonoBehaviour
    10.     {
    11.         public LobbyManager lobbyManager;
    12.  
    13.         public RectTransform lobbyServerList;
    14.         public RectTransform lobbyPanel;
    15.  
    16.         public InputField ipInput;
    17.         public InputField matchNameInput;
    18.  
    19.         public void OnEnable()
    20.         {
    21.             lobbyManager.topPanel.ToggleVisibility(true);
    22.  
    23.             ipInput.onEndEdit.RemoveAllListeners();
    24.             ipInput.onEndEdit.AddListener(onEndEditIP);
    25.  
    26.             matchNameInput.onEndEdit.RemoveAllListeners();
    27.             matchNameInput.onEndEdit.AddListener(onEndEditGameName);
    28.         }
    29.  
    30.         public void OnClickHost()
    31.         {
    32.             lobbyManager.StartHost();
    33.         }
    34.  
    35.         public void OnClickJoin()
    36.         {
    37.             lobbyManager.ChangeTo(lobbyPanel);
    38.  
    39.             lobbyManager.networkAddress = ipInput.text;
    40.             lobbyManager.StartClient();
    41.  
    42.             lobbyManager.backDelegate = lobbyManager.StopClientClbk;
    43.             lobbyManager.DisplayIsConnecting();
    44.  
    45.             lobbyManager.SetServerInfo("Connecting...", lobbyManager.networkAddress);
    46.         }
    47.  
    48.         public void OnClickDedicated()
    49.         {
    50.             lobbyManager.ChangeTo(null);
    51.             lobbyManager.StartServer();
    52.  
    53.             lobbyManager.backDelegate = lobbyManager.StopServerClbk;
    54.  
    55.             lobbyManager.SetServerInfo("Dedicated Server", lobbyManager.networkAddress);
    56.         }
    57.  
    58.         public void OnClickCreateMatchmakingGame()
    59.         {
    60.             lobbyManager.StartMatchMaker();
    61.             /*lobbyManager.matchMaker.CreateMatch(
    62.                 matchNameInput.text,
    63.                 (uint)lobbyManager.maxPlayers,
    64.                 true,
    65.                 "", "", "", 0, 0,
    66.                 lobbyManager.OnMatchCreate);
    67.             */
    68.  
    69.             lobbyManager.StartHostAll(
    70.                 matchNameInput.text,
    71.                 (uint)lobbyManager.maxPlayers,
    72.                 true,
    73.                 "", 0, 0,
    74.                 lobbyManager.OnMatchCreate);
    75.  
    76.  
    77.  
    78.             lobbyManager.backDelegate = lobbyManager.StopHost;
    79.             lobbyManager._isMatchmaking = true;
    80.             lobbyManager.DisplayIsConnecting();
    81.  
    82.             lobbyManager.SetServerInfo("Matchmaker Host", lobbyManager.matchHost);
    83.         }
    84.  
    85.         public void OnClickOpenServerList()
    86.         {
    87.             lobbyManager.StartMatchMaker();
    88.             lobbyManager.backDelegate = lobbyManager.SimpleBackClbk;
    89.             lobbyManager.ChangeTo(lobbyServerList);
    90.         }
    91.  
    92.         void onEndEditIP(string text)
    93.         {
    94.             if (Input.GetKeyDown(KeyCode.Return))
    95.             {
    96.                 OnClickJoin();
    97.             }
    98.         }
    99.  
    100.         void onEndEditGameName(string text)
    101.         {
    102.             if (Input.GetKeyDown(KeyCode.Return))
    103.             {
    104.                 OnClickCreateMatchmakingGame();
    105.             }
    106.         }
    107.  
    108.     }
    109. }
    110.  



    [EDIT]
    I went ahead and added the override method to my LobbyManager.cs:
    Code (csharp):
    1. public override void OnMatchJoined(bool success, string extendedInfo, MatchInfo matchInfo) {
    2.     Debug.Log("JUST CALLED: OnMatchJoined");
    3.     //What should I do here?
    4. }
    Now the NullReferenceException error for OnMatchJoined() is no longer thrown and that function is called right after pressing "Join", however it only contains a Debug line and I do not know what else to put inside of it?

    [EDIT]
    I just tried adding a call to the base function but I keep getting a NullReferenceException:
    Code (csharp):
    1. public override void OnMatchJoined(bool success, string extendedInfo, MatchInfo matchInfo) {
    2.     Debug.Log("JUST CALLED: OnMatchJoined");
    3.     base.OnMatchJoined (success, extendedInfo, matchInfo);
    4. }
    5.  
    6. /*PRODUCES ERROR:
    7. NullReferenceException: Object reference not set to an instance of an object
    8. NATTraversal.NetworkManager.OnMatchJoined (Boolean success, System.String extendedInfo, UnityEngine.Networking.Match.MatchInfo info)
    9. Prototype.NetworkLobby.LobbyManager.OnMatchJoined (Boolean success, System.String extendedInfo, UnityEngine.Networking.Match.MatchInfo matchInfo) (at Assets/Lobby/Scripts/Lobby/LobbyManager.cs:255)
    10. UnityEngine.Networking.Match.NetworkMatch.OnMatchJoined (UnityEngine.Networking.Match.JoinMatchResponse response, UnityEngine.Networking.Match.DataResponseDelegate`1 userCallback) (at C:/buildslave/unity/build/Runtime/Networking/Managed/MatchMakingClient.cs:226)
    11. UnityEngine.Networking.Match.NetworkMatch+<ProcessMatchResponse>c__Iterator0`2[UnityEngine.Networking.Match.JoinMatchResponse,UnityEngine.Networking.Match.NetworkMatch+DataResponseDelegate`1[UnityEngine.Networking.Match.MatchInfo]].MoveNext () (at C:/buildslave/unity/build/Runtime/Networking/Managed/MatchMakingClient.cs:438)
    12. UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) (at C:/buildslave/unity/build/Runtime/Export/Coroutines.cs:17)
    13. */
     
    Last edited: Jun 8, 2017
  2. KyleStank

    KyleStank

    Joined:
    Feb 9, 2014
    Posts:
    204
    Did you ever figure out how to do this?
     
  3. CloudyVR

    CloudyVR

    Joined:
    Mar 26, 2017
    Posts:
    715
    Yes, I had to reverse engineer both NATTraversal's network lobby manager and Unity's network lobby manager to rewrite a single hybrid network lobby manage consisting of elements from both managers. It works very well as a lobby system for NATTraversal but I have not completed it yet. I was planning on releasing an example project on GitHub when I can catch up on my game a little. But it is totally possible to combine them with a bit of work!
     
  4. GIRsMySpritAnimal

    GIRsMySpritAnimal

    Joined:
    Jan 13, 2017
    Posts:
    45
    have you had a chance to document how you did it exactly for those of us who may not be as savvy networking wise?
     
  5. CloudyVR

    CloudyVR

    Joined:
    Mar 26, 2017
    Posts:
    715
    It is one of those things I really need to get around to finishing. If I get something up on Git soon I will definitely pm you and post a link in this thread.
     
    GIRsMySpritAnimal likes this.
  6. chadfranklin47

    chadfranklin47

    Joined:
    Aug 11, 2015
    Posts:
    229
    Any Solution? Please update.