Difference between Hosting on Built version vs Editor

Hello

I have a strange issue. I used the Multiplayer Lobby to start a game. If I host my game from the Editor, I will not get any error on the Editor version but on the built version.
But if I host the game from the built version I will get this error on both editor (running) and built version (running) when entering the main scene:

NullReferenceException: Object reference not set to an instance of an object
PlayerGUIManager.Awake () (at Assets/scripts/PlayerGUIManager.cs:21)
UnityEngine.Networking.NetworkIdentity:UNetStaticUpdate()

The code at that line would be in the Awake() function of the NetworkBehavior PlayerGUIManager

GameObject gameMgr = GameObject.Find("GameManager");
if(gameMgr == null) Debug.LogError("gameMgr in PlayerGUIManager#Awake() is null");
mihMgr = gameMgr.GetComponent<MeepsInHandManager>();

The GameManager Awake() function is definately called before this code so I can’t understand how the GameManager (NetworkBehavior) can not be found.

The GameManager is in scene 2 and thus not present from the very beginning (lobby scene). He has a NetworkIdentity where both boxes are unticked. It’s the same behavior if server-only is ticked (btw it should be ticked, right?).
The NetworkManager is in scene 2, the LobbyManager in the lobby scene. NetworkManager won’t exist anymore if scene 2 is loaded. The GameManager should exist only once on the server.

Maybe I got something totally wrong about the Networking API. Is it wrong that my GameManager is a NetworkBehavior, server-only? If not, how do I accomplish it that every client finds this GameManager via NetworkManager.Find()?

Can someone explain this different behavior and help make this clear? Thanks in advance

When you run it, is the GameManager disabled in the hierarchy? I kept having that problem, I would fix it, turn my computer off. Turn it on the next day, then boom, GameManager starts disabled when it loads the scene even though it was active in the editor and no scripts disabling it.

You can’t use GameObject.Find on an object that is disabled, so that’s why your problem arises.

1 Like

WOW

Indeed, the GameManager is disabled when I host the game on the built version (not when hosting from editor).
What is that???

Are you trying to find the GameManager object from the client or server?

If the NetworkIdentity is ticked as ServerOnly and the object is part of the scene it will be only active on server side. So if you try to find it on client side you won’t find it.

If the object is Local Authority, it will appear on both sides but for some reason I cant send a Command to the server from the client.

My 2 cents here is that you should think why you need that object that way and see if you can’t find an other way to achieve a similar result. It happened to me, to think in one way without fully understanding the Unet-way. Most of the times you just need to rethink why you want to do it that way and analyze other options.

If I start developing a game, I will have a class called “GameManager”. This class will handle the main state of the game (whose turn it is etc).
Now when I switch to Multiplayer, this GameManager somehow needs to be present on both machines but only the server should send the state updates to the clients.
Both boxes are not ticked as from my understanding even the GameManager, which exists only once even on a local MP game, no player controls it yet it holds references to objects in the scene.

To your question: I am trying to find it from both clients. Both clients need to know how to reach the GameManager.

Having read another thread people seem to complain that even easy things like this seem extremely complicated with unet. Maybe I just think in a wrong way but this turn-based stuff should be one the very first things that are explained in the documentation. Yet, there is not much useful information about it :confused:

Make sure you are trying to reference the game manager AFTER your client is logged in, preferably after the scene is loaded.

As @angusmf said. Stop using awake unless you know that the object exist in the scene from the benginning. Use OnServerServer and things like that are meant to be used with networking.

I’m not in my pc right now but you can solve that creating a Singleton class, I did that in my game.

So the moment I implement Networking into my game I have to rewrite all the code since I cannot use Awake() anymore??

Thanks for the help, will try it out the next days :slight_smile:

Awake is still useful, it just can’t reference anything that hasn’t been enabled yet. “Normal” objects are enabled when the scene loads like normal, but objects that have a networking component load after the server starts (if you are a server) or after you connect to the server (if you are a client.)

Basically, just chase down the null ref errors as they occur and find a way to interact with that object later. Sometimes even Start is too soon, particularly if you are trying to communicate over the network. In that case, you have to watch for the scene loaded hook to fire.

1 Like

OK having put a million debug statements everywhere after your hints I ran into the following when loading the new scene. I output the number of NetworkIdentities and the netIds.

Now, all the NetworkIds that are 0 in MarketResourceCanvas::Awake() are disabled in my scene, which they shouldn’t. What is this? Even stranger is the fact that the number starts out with 6, then gets magically reduced to 1 ô.O
Moreover, I don’t get any further log output from my class MarketResourceCanvas, which has Start(), OnStartServer() and OnStartClient() besides Awake().
There are no additional error messages whatsoever. Just all the objects are disabled in the scene and I don’t know why. May it have to do with registering something at the NetworkManager?

PlayerGUIManager::Awake() is called TWICE from netId 0. Why/how?

Moreover, no matter how often I call NetworkServer.SpawnObjects(), the connected client will always give me “OnObjSpawn netId: 100 has invalid asset Id” when I Spawn an object live via a [Command]

I don’t know your setup and script to answer all the questions that you are asking.

For seeing disabled objects on client side it could be that it has some kind of error in scripts or they have the network authority set to server (that way the only the server sees them).

I don’t know if you are spawning the object or are part of the scene; if they are part of the scene and you think that setting the checkboxes to local authority, the server will grab the authority first, and even if you see it checked as local on client side only ONE can own the authority (in this case the server because it runs first. You can check it printing “hasAuthority”).

If you want to spawn the object you have to declare the prefab in the NetworkManager first.

My suggestion is to create a simple project just to test the networking without thinking in your game. Nail it there and then go back to your game with what you learned there. I had to do that when I was migrating.

This guy explains it the networking pretty good and simple (has a list dedicated to networking find it in youtube if you want)

1 Like

Thanks for your help so far. I feel like getting further

One problem seems to be the already in scene 2 existent NetworkBehaviour gameobjects. They are disabled and are never spawned, since they already exist in the scene and are not prefabs. So what to do with them? Why are they disabled? And still, it depends on whether I start from the built version or from the Editor… sigh

This behaviour really drives me mad!
In the documentation, there is nothing said about NetworkIdentity GOs that already exist in the scene and are NOT prefabs… god, is it me or the documentation?

In my network test project this problem does not arise. I can start from Editor and built version and none of the NetworkIdentities is disabled from the beginning.

So what could cause objects to be disabled in general? And what 's the difference between Editor and built version in general?

I can’t help you any further without knowing your code. Sorry…

But probably is your code or how you are trying to solve the things

Having solved a whole lot of my problems. Had to do with the [ClientRpc] tags and the order in which objects have been searched.

But maybe someone can explain me if NetworkTransforms make any sense in a 2D game where people will have different aspect ratios. Imho this Component doesnt make sense for a simple 2D board game, does it? I almost got mad until I finally found that this component was trying to update the position so it matches the host’s but the host has a totally different resolution than the client.
Or did I miss important components? The objects are spawned

If you aren’t using physics for the objects, there’s no need for the network transform, thus no need to spawn using unet. You can control your spawning and position functions through MESSAGES sent from the server/host. Do not try to use RPCs because the messages will be sent indiscriminately to ALL clients. (Although if you aren’t concerned about cheating, it’s easier to use RPCs and just include a piece of information in the RPC that tells the clients which one of them the message is intended for.)

What exactly do you mean by MESSAGES? And why shouldn’t I use Rpc here?

Messages:
https://docs.unity3d.com/Manual/UNetMessages.html

The problem with all the client syncing stuff like syncvars, synclists and ClientRPC is that the data is synced to all clients. If the data is only pertinent to one client, you have to filter it yourself in the function or hook by checking that the message was addressed to that client. A hacked client could see data that was intended for other clients by ignoring the filter. With Unet MessageBase, the data can be addressed to a specific client id.

My pattern is this:

Player initiates some action which calls a Command or sends a MessageBase-derived message to the server, which is what a Command does under the covers. The network id or some unique ID that maps the network ID of that client is used to look up the user on the server in the Command or MessageBase method so that the server knows A) which client is asking for something which leads to B) the server knows who to send a response to, if needed.

Example in pseudo-code:

Client:
OnLogin() {
CmdLogMeIn()
}


Server:
CmdLogMeIn() {

string uid = GenerateUID(connectionToClient.connectionId) ; //where this creates an association between the conn id and a new GUID or something in a lookup dictionary
SendUID(connectionToClient.connectionId, uid);

}

CmdDoSomething() {

string uid = LookUpConnId(connectionToClient.connectionId); //where LookUpConnID gets the uid based on the conn id
string result = DoSomeStuff(uid); //a method that returns a result that pertains to this user
SendDoSomethingResponse(result, connectionToClient.connectionId); //where we call a function that returns the result to the user

}


MessageBase-derived Login class:

...
SendUID(int connection_id, string uid) {

NetworkServer.SendToClient(connection_id, CreateLoginMsg(uid), LoginMsgType);

}

LoginMsg CreateLoginMsg(string uid){

LoginMsg msg = new LoginMsg();
msg.uid = uid;
return msg;

}

...

MessageBase-derived DoSomething class:

...

SendDoSomethingResponse(string result, int connection_id){

NetworkServer.SendToClient(connection_id, CreateDoSomethingResponseMsg(result), DoSomethintResponseMsgType);

}

...

And so on. While looking up SendToClient, I noticed there is also a method named SendToClientOfPlayer which saves you the trouble of creating your own lookup dictionary as long as you have the correct player object to give it.

My apologies if the code is not helpful or leads anyone down the wrong path. This is just the general solution I came up with since there is no “private” ClientRPC.

Hmm I dont understand the difference between a message and an RPC :frowning: I don’t the need functionality to communicate with a certain player.

My clients cannot call the [Command] on the Manager because they dont have authority. But for other objects (Tiles) they can call [Command]s on the Manager.
I think my problem is I just want to send a message to the REAL server (who started the first RPC) but I will only target the local GameManagers. Thing is I need to wait for all clients to complete their RPCs before the server can continue.