I started to manage user disconnections in my game. In two words I have menu scene and game scene. When client leaves game scene I just shutdown his network manager and load menu scene, but I bump into troubles when host leaves the game scene. First of all I go through connected users and if it’s a client I try to shutdown his network manager and load menu scene for him as I said. And after that I just disconnect the last user in the scene - host. But I can’t disconnect clients this way for some reasons.
I’ve tried to shutdown and load scene in different order, but it didn’t make weather.
public void OnPointerClick(PointerEventData eventData)
{
if (NetworkManager.Singleton.IsHost)
{
for (int i = 0; i < NetworkManager.Singleton.ConnectedClientsList.Count; i++)
{
ulong id = NetworkManager.Singleton.ConnectedClientsList[i].ClientId;
if (NetworkManager.Singleton.LocalClientId == id) continue;
NetworkManager.Singleton.ConnectedClients[id].PlayerObject.NetworkManager.Shutdown();
NetworkManager.Singleton.ConnectedClients[id].PlayerObject.NetworkManager.SceneManager
.LoadScene("RoomConnection", LoadSceneMode.Single);
}
NetworkManager.Singleton.Shutdown();
SceneManager.LoadScene("RoomConnection", LoadSceneMode.Single);
}
else
{
NetworkManager.Singleton.Shutdown();
SceneManager.LoadScene("RoomConnection", LoadSceneMode.Single);
}
Cursor.lockState = CursorLockMode.None;
}
public void OnPointerClick(PointerEventData eventData)
{
if (NetworkManager.Singleton.IsHost)
{
for (int i = 0; i < NetworkManager.Singleton.ConnectedClientsList.Count; i++)
{
ulong id = NetworkManager.Singleton.ConnectedClientsList[i].ClientId;
if (NetworkManager.Singleton.LocalClientId == id) continue;
NetworkManager.Singleton.ConnectedClients[id].PlayerObject.NetworkManager.SceneManager
.LoadScene("RoomConnection", LoadSceneMode.Single);
NetworkManager.Singleton.ConnectedClients[id].PlayerObject.NetworkManager.Shutdown();
}
NetworkManager.Singleton.Shutdown();
SceneManager.LoadScene("RoomConnection", LoadSceneMode.Single);
}
else
{
NetworkManager.Singleton.Shutdown();
SceneManager.LoadScene("RoomConnection", LoadSceneMode.Single);
}
Cursor.lockState = CursorLockMode.None;
}
Also I’ve tried just switch scene and in menu on start check singleton is active and shutdown it if it is. But I’m not sure if it’s the right way to check.
private void Start()
{
if (!NetworkManager.Singleton.IsClient && !NetworkManager.Singleton.IsHost) return;
NetworkManager.Singleton.Shutdown();
}
The last thing I’ve tried is subscribe to an event and check if local id and parameter id are the same. If it is I had to shutdown and switch scene just like for host.
You could try using the NetworkManager.Singleton.OnClientDisconnectCallback;
Something like:
NetworkManager.Singleton.OnClientDisconnectCallback += BackToMenu;
void BackToMenu() { // Logic }
Havent tried it myself with a host, but should still be the same result
Yeah, callback was the way to go. But now I have troubles with reconnection. When I reconnect to the game my player can’t move or rotate camera for some reasons. In the console I get exceptions (pinned photo). My google searches showed that first exception just tells when exception was thrown and I need to fix second MissingReferenceException. I’ve tried to check if it’s not equals to null, but this exception still shows up. So for now I need to fix this exception and understand why reconnection make moving and camera rotation unavailable (even if shutdown network manager and delete its gameobject).
Do you shut down your Client properly? For example static Lists, you need to remove your Object in the List pre Shutdown or else on reconnect the object is still in there, but got actually destroyed, a null reference. Also make sure to remove the object everywhere relevant, not only your client, all clients that are interacting with that object.
So basically, make sure to Shutdown your client safely. Thats my advice for now.
I checked my code and it looks like there is no such lists or something. Here is my current state of user disconnection code. Also I’ve spotted an intresting behaviour, but firstly I need to tell you how game works. If press the Esc key I activate menu that contains in canvas that contains in every player prefab. In this menu I have a Leave button that manage disconnections and its script I’ve pinned to this answer. Now lets get back to behaviour that I told you about. On first game session everything works good, then if I leave and reconnect I can’t move and rotate camera, also if click left mouse button (shoot) I get MissingReferenceException. If I again open the menu I will not able to close him. Then I press a leave button second time and reconnect for the third time. On the third attempt I get the same result as the second, but if open menu on third connection I get back control on movent, camera rotation and menu (can close him). So it looks like the first connection (where everything works fine), but I still receive MissingReferenceExceptions even I check if its null. Any ideas where did I do mistake?
public class LeaveRoomButton : NetworkBehaviour, IPointerClickHandler
{
public void OnPointerClick(PointerEventData eventData)
{
if (NetworkManager.Singleton.IsHost)
{
for (int i = 0; i < NetworkManager.Singleton.ConnectedClientsList.Count; i++)
{
ulong id = NetworkManager.Singleton.ConnectedClientsList[i].ClientId;
if (NetworkManager.Singleton.LocalClientId == id) continue;
DespawnPlayerServerRpc(id);
NetworkManager.Singleton.DisconnectClient(id);
}
DespawnPlayerServerRpc(NetworkManager.Singleton.LocalClientId);
NetworkManager.Singleton.Shutdown();
Destroy(NetworkManager.Singleton.gameObject);
SceneManager.LoadScene("RoomConnection", LoadSceneMode.Single);
}
else
{
DespawnPlayerServerRpc(NetworkManager.Singleton.LocalClientId);
Cursor.lockState = CursorLockMode.None;
NetworkManager.Singleton.Shutdown();
Destroy(NetworkManager.Singleton.gameObject);
SceneManager.LoadScene("RoomConnection", LoadSceneMode.Single);
}
}
[ServerRpc(RequireOwnership = false)]
private void DespawnPlayerServerRpc(ulong id)
{
DespawnPlayerClientRpc(id);
}
[ClientRpc]
private void DespawnPlayerClientRpc(ulong id)
{
var clientObject = NetworkManager.Singleton.ConnectedClients[id].PlayerObject;
if (clientObject != null) clientObject.Despawn();
}
}
There are a few things that catch my eye.
-
Your ClientRpc, it is important to know that this one will be executed on all Clients. Meaning that all Clients with this script attached will execute this code. So you got multiple calls on clientobject.Despawn with multiple Clients. If you wanan use the ClientRpc, use the IsOwner attribute. This will execute the code only on the owned object.
-
Just for comfort, you could search through NetworkManager.Singelton.ConnectedClientIds to find your ID.
-
The other thing is, that the Despawn call should always come from the Server itself. Just for security reason alone, so a Client cant disconnect another Client. Netcode despawns a NetworkObject for all Clients as well. So just send a ServerRpc request wirth your own ID to the server and let him despawn your Client.
[ServerRpc]
private void RequestClientDisconnect(ulong ID)
{
foreach (var clientID in NetworkManager.Singelton.ConnectedClientIds)
{
if (clientID == ID) NetworkManager.Singleton.SpawnManager.GetPlayerNetworkObject(ID).Despawn();
break;
}
}
Something like that should be enough to disconnect your Client. The disconnection on the Host should more like something you already did, by disconnecting all Clients prio your Host.
At least I use this kind of disconnection.
About the Camera issue. Do you attach your Camera on the Client by any chance? Cause it could be that the Camera is searching for its attached Client, that got disconnected/destroyed. Or it could be that the Camera is destroyed as well. Therefore i recommend using a Camera in the Scene and dont attach it directly on the object, rather reference the Object and check for null.
Do you start in the same Scene as you play? I recommend using a ClientMenu scene that handles the Connection and the Server should, on connect, transport you to the ServerScene. Like that your Scene gets resetted.
I also recommend some sort of CommunicatorClass for your Client, to keep stuff clean.
I personally use the ClientRpc only to sync certain aspects, but all the important decisions are done on the Server. Movement, Connection, etc.
*Also a sidenote for that example code, I just wrote the code out of my memory. Overally the logic is something like that, the actual names could be a little different.
Sorry for late answer! Tthank you for your help! So looks like I’ve fixed all problems. Unavailable movement and camera rotation was fixed by writing IsGamePaused = false in start (It was a flag to check if player has opened menu, so he couldn’t move or rotate camera, I still don’t know why should I write it even if bool is false by default). The MissingReferenceExceptions was fixed by checking component != null instead of component.gameObject != null. And the last one is your disconnection code works perfectly, I have not seen any error on reconnection. Thanks so much!
Since i belief you just open an MenuCanvas instead resetting the Scene, the reason why your isGamePaused did not change, is because you never change it back (most likely). A disconnect is not a Scene reset or some sort. Its just a disconnect. Thats why I suggest to use a MenuScene, this assures you that the Scene is back at its default state.
Pre connect, be in MenuScene
Access PlayScene via MenuScene
OnDisconnect go back to MenuScene
Reconnect from MenuScene to PlayScene
1 Like