Synchronize Objects' Position With NetworkManager

Hey!

I’m working on a simple proof-of-concept application with network functionalities.

The idea is to create a server, where multiple clients can connect. Every time a server (or host) is started, a 3D game object, e.g. a Sphere, is spawned, thus, there is always only one game object in a one-to-many (server to client) relation. For now, only the server should be able to move the object, and clients should only see the object transforming.

For the network functionalities I’m using the built-in Network Manager with a custom network manager class. In the spawn info of my Network Manager I have a Sphere prefab that has a Rigidbody, Network Identity, Network Transform, and a script component attached.

In my custom network manager class I have the following to handle that only the server can spawn a sphere object. Not sure if conn.hostId==-1 is the correct way, but couldn’t find another way to check if the current connecting player is creating a server or just joining as a client.

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

class CustomNetworkManager : NetworkManager
{
    public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
    {
        if (conn.hostId == -1) {
            GameObject player = (GameObject)Instantiate (playerPrefab, Vector3.zero, Quaternion.identity);
            NetworkServer.AddPlayerForConnection (conn, player, playerControllerId);
        }
    }
}

The code for the sphere is quite simple:

using UnityEngine;
using UnityEngine.Networking;

public class SphereController : NetworkBehaviour {

    // Update is called once per frame
    void Update () {
        var x = Input.GetAxis("Horizontal")*0.1f;
        var z = Input.GetAxis("Vertical")*0.1f;

        transform.Translate(x, 0, z);
    }
}

The sphere is spawned correctly if I start a LAN as host. I can also connect as a client just fine, and also see the sphere. Right now I can move the sphere in both applications (server and client), since I don’t have any conditionals in my SphereController class.

How do I pass the transformation info from my server to my client? I thought the Network Transform component would take care of that, but I guess I somehow need to pass the information in the SphereController?

I just found the error why it was not syncing. The ‘Transform Sync Mode’ in the ‘Network Transform’ was not set to ‘Sync Transform’.

So, now the only question remaining is, how to determine that the current client calling the ‘OnServerAddPlayer’ method was the server?

If you want it to exist in the scene already and theres only one, and the server should own it, you don’t really have to spawn it; you could just make a network scene object with local authority enabled and it will already be in the scene when clients/host load the scene and the server will own it and have authority.

http://docs.unity3d.com/Manual/UNetSceneObjects.html

Cool! Good to know. I’ll look into it.

Ok, so I just changed my object into a scene object and removed the prefab from the Network Manager’s spawn info. Also enabled local authority on the scene object. However, now the server has authority about the position, but the client can still change the position (but it is not updated in the server view). Can I prohibit the movement for the client altogether? Or better yet, the ultimate goal would be to have something like a token (syncvar variable?) that I can set/pass to the client that should have control at a given time?

I believe you can use http://docs.unity3d.com/ScriptReference/Networking.NetworkIdentity.AssignClientAuthority.html

and then have your code inside an if(hasAuthority) conditional that you only want the controlling client to execute.

Local Authority only needs to be set on player objects or objects that will be spawned on the client with Local Authority. To stop the clients moving the object, disable the movement scripts on the client using something like (if(!NetworkServer.active), etc.

That would be fine if he only wanted the server to control it. His followup question was how to pass control of a server owned or scene object to different clients. Pretty sure AssignClientAuthority is how you’d do it, judging from the docs and this thread. The function’s existence doesn’t make much sense otherwise.

To swap Authority like you want you’ll need to enable controls, etc. in NetworkBehaviour.OnStartAuthority() and disable controls,etc. in NetworkBehaviour.OnStopAuthority()
On the server, you’ll need to first called NetworkIdentity.RemoveClientAuthority(NetworkIdentity.clientAuthorityOwner) to remove the current owner and then call NetworkIdentity.AssignClientAuthority(NetworkConnection) with the new owner.

You could also use network messages to control the object, although you would still need a way to stop clients from moving it locally if they are not supposed to.