[Solved] Different prefab client / server , (fragmented) network channel not working and other stuff

I am currently implementing the multiplayer part of our game and had a few problems along the way that i could not immediately find solutions to.

So here is a little thread where i will post problems and their solutions that i come across while using unet so i don’t forget about them … and maybe it is also of some help to others :wink:

1. Different prefab for the client and server

(I am assuming you are using the NetworkManager)

You have to create a custom spawn handler on the client to do this.

On the server, spawn your prefab and use AddPlayerForConnection like you normally do:

//Add this to OnServerAddPlayer() in your NetworkManager
//Load prefab. It MUST have a NetworkIdentity
GameObject _prefab = Resources.Load("Server/Player") as GameObject;
//Create an instance
GameObject _object = Instantiate(_prefab);
//Send spawn message to client and make him the owner
NetworkServer.AddPlayerForConnection(conn, _object, playerControllerId);

Now on the client you do this:

//Add this to OnStartClient() in your NetworkManager
//Get SERVER Prefab
GameObject _serverPrefab = Resources.Load("Server/Player") as GameObject;
//Create a custom handler for the server prefab, so we can spawn a custom client prefab
ClientScene.RegisterSpawnHandler( _serverPrefab.GetComponent<NetworkIdentity>().assetId, SpawnPlayer, UnspawnPlayer );

And finally add the handlers on the client:

GameObject SpawnPlayer(Vector3 position, NetworkHash128 assetID)
{
    //Finally: Load the client prefab. It must NOT have a NetworkIdentity! This is important
    GameObject _clientPrefab = Resources.Load("Client/Player");
    //Create an instance
    GameObject _object = Instantiate(_clientPrefab);
    //Add a NetworkIdentity
    _object.AddComponent<NetworkIdentity>();

    return _object;
}

void UnspawnPlayer(GameObject inObject)
{
    Destroy(inObject);
}

2. (Fragmented) network channels are not working

You are most likely using the NetworkManager and trying to change connectionConfig by code and manually adding channels with connectionConfig.AddChannel(…).

To make this work, don’t forget to set

customConfig = true;

before making any changes to connectionConfig.

If you don’t set customConfig, the channels will be added and you can use them, but they won’t be of the QoS type you defined.

Alternatively, just create a new NetworkConfig and use that when calling StartServer(…);

3. I don’t want to use the default channel to send stuff around

For some reason, you can still use [RPC] with unet and the example i followed used them. But you should not. Instead, use [ClientRpc]. You can then use channel to set the … well … channel

[ClientRpc(channel=2)]

This also works for [Command]

If you wan’t to send large data around (database / long json string), QosType.ReliableFragmented is your friend.

4. I want to give control/ownership (NOT authority) of two gameobjects to a client, but with AddPlayerForConnection i can only use one.

This is actually very easy but is not that clear in the documentation (at least for me):
When calling AddPlayerForConnection(conn, player, playerControllerID) you just increase playerControllerID for every new object you want the player to control.

//Spawn 100 units and give control to the client.
//Honestly, you should not do that but create a single manager for this .. but as this is an example ... ;)
GameObject _prefab = Resources.Load("AwesomeUnit") as GameObject;
for(int i=0; i<100; ++i)
{
    GameObject _object = Instantiate(_prefab);
    NetworkServer.AddPlayerForConnection(conn, _object, i);
}

5. Send custom data over the network

Use NetworkServer.RegisterHandler and Send(…)/SendByChannel(…)/etc. for this.

//Define a custom message type
public class MsgTypeCustom{
    //Add a new type and give it a unique id
    public const short OnReceiveDatabase = 1001;
    //You can add more Types here, just increase the id
}

//Register a handler for the custom message
NetworkServer.RegisterHandler(MsgTypeCustom.OnReceiveDatabase, OnReceiveDatabase);

//Custom Message class which will hold our data
public class MsgDatabase : MessageBase
{
    public string someText;
    public int stuff;
}


//Send custom message. We are sending from the server here, but you can also send from client, of course
[Command]
void Cmd_SendDatabase()
{
    if (!isServer)
        return;

    //Create new message
    MsgDatabase _msg = new MsgDatabase();
    //fill message with data
    ...
    //Send message
    connectionToClient.Send(MsgTypeCustom.OnReceiveDatabase, _msg);
}

//Receive the message
void OnReceiveDatabase(NetworkMessage inMessage)
{
    //"parse" message so we can access the data
    var database = inMessage.ReadMessage<MsgDatabase>();
    Debug.Log(database.someText);
}

to be continued :wink:

3 Likes

Awesome I’m stucked on this too. I’m coding a driving simulator that have 3 x 40" monitors for tge front and 3 x 27" monitors for the rear view, also 2 touch screen monitors used for beacon and emergency lights and a last monitor for the dashboard. The current projet (for an airport) is the first one that I use uNet and yeah… kinda bored. Each station connect to the instructor station that is the server and allow the instructor to manage the IA traffic, follow the simulator vehicule and so on. So each station has and identifier stored locally to determine if the server needs to spawn a drivable car, activate a specific cam, or activate the instructor interface. Now, I’m not sure if I should make the server spawn each appropriate gameobject at client connect or if I should just place the objects where they belong in the scene and assign network ids to the appropriate stations based on their identifiers. Because its an educationnal simulator, I know that exactly whats needed every sessions… Any thoughts on that anyone ?