Network views on scene objects

I’m building a networked 2-player game where I have the 2 players already set-up on the scene.
When I network-load the level, the host has networkView.isMine = true on both players, while they’re both false on the client.

How would I make one peer in control of one player, and vice-versa (“listening” to the other, remote player)?
I want the server to control P1 and the client to control P2.

Right now I broke down my scene, took out my players to Network.Instantiate them on load, but it seems a dumb thing to do, there’s probably a better way, right? I mean, why instantiate objects that were already there. All I need is to tell them that one is mine and the other isn’t… but how??

Any object with a networkView is going to be considered ‘owned’ by the server if it’s just sitting in the level at the time of loading. Having each player instantiate their avatar on level load is, as far as I know, the best solution to making sure that the correct network players ‘own’ the objects that they control directly.

Another solution would be to have the server ‘own’ all of the player avatars, and the clients merely send requests to the server to control them via RPC’s. This would cause an undesirable delay between client input and the reaction of their character, but many games correct this with complex systems of prediction/interpolation that are evaluated locally, but then corrected by the server’s input. Getting this method to look good will take a lot more legwork than simply having each player own their own characters.

I think its far simpler to just let the client instantiate his own objects on connecting. Either with Network.Instantiate or manually.

That being said, you can make the client take control of an scene object which is normally owned by the server. When the client connects he can allocate a view ID for the scene object and assign it to him. He also needs to do this on the server. This is easily accomplished with an RPC function executed by the client which takes the allocated view ID as a parameter. In the function the object is looked up and the new view ID assigned, as this is done by RPC on everybody (including the server) everyone will agree on the new view ID.

Take a look at Unity - Scripting API: Network.AllocateViewID

The example code there shows how to manually do network instantiation. You could do something similar except that you don’t instantiate but lookup the already present object.

Yes, I agree with both of you, it is simpler to Network.Instantiate… for a purely networked game. My game can be played both split-screen (sharing a keyboard) and online, so I need my scene to manage both situations with as little redundancy as I can.

I’ve had my eye on Network.AllocateViewID for a while but I’ll admit I don’t understand much of what the doc says so I haven’t tried it yet.
Basically, if a client manually sets its view ID, does that mean the view will become his (isMine = true) ?

Yes, he is then in control of the network view and will send updates for the component it is watching.

You just need to make sure that everyone else in the networked game receives this information (the new view ID) AND any new clients which connect afterwards. If you don’t then they (the others) won’t accept the updates. That is done in the example code I pointed to, in the RPC function.

OK… so I think I figured out how to use it, but I still have a few problems.

Here’s what I do:

// THIS RUNS ON THE CLIENT
void Awake()
{
	if (Network.isClient)
	{
		// generate a new ID
		NetworkViewID viewID = Network.AllocateViewID();

		// set it locally
		player2.networkView.viewID = viewID;

		// synchronize the server
		networkView.RPC("allocateP2ViewID", RPCMode.Server, viewID);
	}
}

// THIS RUNS ON THE SERVER
[RPC]
void allocateP2ViewID(NetworkViewID viewID)
{
	player2.networkView.viewID = viewID;
}

That seems to work ok, the client now has P2.isMine, while the server already had P1.isMine.
Plus the game globally behaves ok.

But now I get these messages, not very often, can’t figure out when or why:

View ID AllocatedID: 51 not found during lookup. Strange behaviour may occur (Filename: /Users/unity-build/Desktop/automatic-build-2/unity/Projects/../Runtime/Network/NetworkManager.cpp Line: 1263)

Received state update for view id' AllocatedID: 51' but the NetworkView doesn't exist (Filename: /Users/unity-build/Desktop/automatic-build-2/unity/Projects/../Runtime/Network/NetworkManager.cpp Line: 1990)

It says it has a problem with view 51, while my allocated ID for P2 is always 50. But once I had the same message with view ID 8…

It feels a bit weird to use AllocateViewID on the client, but if I do the other way around (allocate on the server then synch the client), I’m right back where I started: the view belongs to the server.
What does AllocateViewID do, query the server for an ID? Or is it a local allocation?

You use allocated view IDs everytime you instantiate something from a prefab. Then you need a new unique ID to attach to that prefab. This is done automatically for you when you use Network.Instantiate. Clients have a pool of unique view IDs which they get from the server at startup. The error you are seeing, with regard to allocated view ID 51, probably comes from the client (P2), since you saw one with the ID 50 from him. Something in you scene is probably generating that view ID. If you have ReliableDeltaCompression then something is only sent when the watched component has changed. So it might seem erratic. When you see this you can look in the inspector on the client and see if he has that view ID attached to some network view on some object, something must be sending stuff with that ID.

Perhaps a simpler solution would be to destroy the existing object and reinstantiate it using the networkInstantiate when/if the player connects to another via network. Then all of the network view stuff will be done automatically. Haven’t tried this, but just a thought.

Ok… erratic seems the key word here! There’s clearly something wrong with what I’m doing.

I think I’ll just forget my dream of reusing my objects and distributing them between client and server :roll:, and just bend to the common practice of network-instantiating what needs to be shared.

At least now I know what the limits are, so once again thank you Larus the networking master :smile: !

I’m also receiving these messages, and I think that it is because I’m not buffering the objects being instantiate, instead each time a user enters the room the server sends that players in the room, so that he instantiates them.

However, it seems that the player takes a while to process the “create new player messages” and receives a state update for a player that he hasn’t created yet, which generates these messages. If I add the player creation to the RPC Buffer, the problem goes away.

So my question is, does this create a problem? It’s just that the “strange behavior may occur” makes me wonder!

Also, if this happens, does this mean that all the “network objects” have to be in every client?

EDIT: It seems that if I propagate these messages on OnPlayerConnected the problem goes away. Anyway, the second question still remains, network objects have to be in every client?

Regards,
Afonso

Yes… unless you feel adventurous and let go of the convenience of RPCMode.All and RPCMode.Others (and the buffered stuff … and Network.Instantiate … and the state synchronization that comes for free in Unity …) and make sure that messages are only sent to those clients that have the relevant objects.

That means a lot of extra-management on your side (and probably a bit of less performance), but if you want to do something where only people in a certain context get the updates (e.g. in-game location based or a game server hosting multiple game sessions), you better do it that way right from the start because you’ll run against rather solid walls otherwise…

Basically, “all” you need to do is keep lists of NetworkPlayer (on the server, of course) and then, when you want to send out a message, you loop over the relevant NetworkPlayers and do RPC(blah, player, blahblah). That “all” ends up being quite a lot :wink:

You still need to make sure your NetworkViewIDs are synchronized correctly (you can’t use Network.Instantiate in that approach, either), but that’s not such a big deal when the right datastructures for look-ups are in place.

Sunny regards,
Jashan

I had the same error as here above…

View ID AllocatedID: 51 not found during lookup. Strange behaviour may occur (Filename: /Users/unity-build/Desktop/automatic-build-2/unity/Projects/…/Runtime/Network/NetworkManager.cpp Line: 1263)

What I did wrong was that I had 2 camera´s in the scene. One for player 1 and one for player 2 (both network view attached) . The camera of the player that isn´t yours I deactivated with networkview = mine (don’t remember the code exactly).

When they are in the scene player 1’s camera is sending information over the network view, but player 2 doesn’t have that camera anymore because it isn’t active anymore so that’s why its not found. I deactivated the camera on an another way and now player 2 still has that second network view of player 1 and can receive information.

It’s not an optimized way. but now i know what the problem is and its gonna be changed now.

Thought I let you guys know. It worked for me and hope for you all. Maybe it was very obvious, but I am just working for a few month in unity now and had big problems with that error.

Kind Regards,

Lemon,