Assigning network views using C# (Edited)

Repost for simpler reading:

If I have 3 objects on the (unconnected) client and three objects on the server, each with their unique network view id. how do i get the network views to match when the client connects to the server, so the server can send RPC calls to the right objects?

It’s like some horrible catch-22 scenario! Without matching ViewIDs I can’t send RPC calls and without RPC calls I can’t send the ViewIDs so they can match! :evil:

Help!

Unity doesn’t have a “server” per say, it is a client that acts like a server, so when you connect, the server then sends the network view ID’s along with network instansiate commands. You don’t have to deal with them at all. The master server, if that is what you are referring to, is nothing more than a bridge, it doesn’t even know what a view ID is. If the client that is running as the server reboots or the game terminates, all view ID’s are lost.

I think in the end you will need to Network.Instantiate at least once (to create a bridge).

Then, by this bridge, you make a RPC function called “DefineViewID”, with arguments

goName : String (name of the gameObject with a NetworkView)
newNetworkViewID : NetworkViewID (the new NetworkViewID).

Use Network.AllocateViewID to get an available NetworkViewID and put it in argument. The function do a GameObject.Find (goName) and give him the new NetworkViewID.

I agree, I really can’t get my head around Unity’s networking structure so I’m going to have a network_manager object that is the only object with a NetworkView.

Hopefully I can use that fact to assign viewID(0) to it and assume it’ll be right. From then on I’ll hopefully be able to to use a custom delegate based (RPC)event based model for RPC calls so that all calls get routed through that object, including new networkViews for synchronising view IDs etc…

I should be able to do something like this:

Register interest in an RPC message from within lobby script:

NetworkManager.RegisterRPCListener("New Lobby Message", AddNewMessage);

on a remote machine call something like this:

sndRPCEvent(RPCMode.AllBuffered, "New Lobby Message", messageString);

[RPC]
sndRPCEvent(RPCMode anRPCMode, string eventName, object Obj1, object Obj2, object Obj3)
{
   networkView.RPC("rcvRPCEvent",anRPCMode,viewID,eventName,Obj1,Obj2,Obj3); 
}

recieve it with something like this:

[RPC]
public void rcvRPCEvent(string eventName, NetworkMessageInfo RPCMsgInfo, object Obj1, object Obj2, object Obj3)
{
   RPCManager.newRPCEvent(eventName, RPCMsgInfo, Obj1, Obj2, Obj3)
}

Which should now call ‘AddNewMessage’ and pass in the parameters.

That’s where I’m at anyways, anyone tried this approach before?

?

Unless I am mistaken :

You have two gameObjects, A and B. They both have NetworkView, each gameObject is on a different computer.

In the case they both have the same NetworkViewID, it means they are linked, so you can use state synchronize between them.

You can then also ask your own instance of NetworkView (A) to do RPC messages stuff (networkView.RPC) and reach others linked NetworkView (B).
When you call that function, you specify who receive this messages (RPCMode.Server, etc).

When you connect to the server, you don’t know whether you have a reliable bridge.

So when the server side initialize the server, it do a Network.Instantiate (bridgeInitPrefab, transform.position, transform.rotation, 0), so you have that bridge. You call it only once, when new players connect to the server, they should also get a copy of the bridgeInitPrefab (the manual says Instantiate is a kind of RPCBuffered, so even if future new players were not present when executing this line, they should still anyway get it. Let’s believe it).

Then, with that bridge, you do a RPC function, for example :

var freeNetworkViewID : NetworkViewID;
freeNetworkViewID = Network.AllocateViewID ();

networkView.RPC ("DefineGameObjectNetworkViewID", RPCMode.All, "ObjectName1ToChange", freeNetworkViewID);

@RPC
function DefineGameObjectNetworkViewID (goName : String, newNVID : NetworkViewID)
{
	var goToModify : GameObject;
	goToModify = GameObject.Find (goName);
	goToModify.networkView.viewID = newNVID;
}

(if some network expert could confirm what I am saying, I would be delighted to know I have understood few things about Network…)

That would work if you want to use network instantiate (which is unity’s preffered solution) but I’m trying a different approach. I don’t want to be calling network.instantiate(thisOrThat) straight away, I think I’d rather have a system where I can route all RPC calls through a singleton manager. The manager also includes event management for things like OnConnected, etc.

In the model I’m proposing, at the start of the game (lobby stage) the server will send the clients the level data using the same RPC (to construct the game environment), it can then send initial player data the same way including an assigned networkViewID and the player’s character data. Once the client has got the player data for each player it can create a new local player object and assign the networkview component to the player with the correct viewID (as sent from the server along with initial player data) to syncronise it across the network. From then on the player will be syncronised using that network view while things like chat, etc are still handled by my custom RPC events.

It should lend itself to swapping-out of network views, servers and other data. ultimately giving me a bit more control over how the networking works (given all the unity networking bugs I’m a bit worried about relying on network.instantiate for everything).

Actually, thinking about it I could use network.instantiate to create an RPCEventSentinal that would handle all the RPC calls and fire off the RPC events as described above. I can still use networkviews to syncronise objects, they’ll just be defined manually via these RPC events rather than network.instantiate. though it is possible to call an rpc on a non-network.instantiate(ed) object using -

function RPC (name : string, target : NetworkPlayer, params args : object[]) : void

And how would it send the data ?

off the top of my head, I could have a level constructor object that does something like:

RPCSentinal.RegisterRPCListener("Define Level Data", Contructlevel);

public void Constructlevel(LevelData newlevelData) {
   // construct level (game environment) using leveldata here
}

then the server calls:

RPCSentinal.sndRPCEvent(RPCMode.AllBuffered, "Define Level Data", levelData);

which tells all the clients (and the server) to construct the level using ‘levelData’ sent to Constructlevel as defined above.

I imagine this could also work for players like this:

RPCSentinal.RegisterRPCListener("Define Player", DefinePlayer);

public void DefinePlayer(NetworkPlayer aNetworkPlayer, playerData thePlayersData) {
   // construct Player using thePlayersData here
}

RPCSentinal.RegisterRPCListener("Set Player NetworkViewID", setPlayerViewID);

public void setPlayerViewID(NetworkPlayer aNetworkPlayer, networkViewID aNetworkViewID ) {
   foreach (Player aPlayer in players) {
      if (aPlayer.networkPlayer == aNetworkPlayer) {
         // add a networkView
         // set it's networkViewID to 'aNetworkViewID '
      }
   }
}

From then on I assume the player will be syncronised using the network view it’s just been assigned.

No idea if this will work but it looks like it’ll be fun to try!