Skip to content

Unity Networking

Jayshua edited this page Apr 13, 2018 · 13 revisions

These are some observations about the Unity Networking API.

Calling functions somewhere else

  • Call a function on the server from a client: [Command]
  • Call a function on all clients from the server: [ClientRPC]
  • Call a function on a specific client from the server: [TargetRPC]

Adding a command as an event handler (someObject.OnEvent += CmdHandler;) does not work. The function is run on the client instead of the server without any type of message or error.

[Command] methods can only be called on objects that are localPlayer or have local authority.

Unsurprisingly, [ClientRPC]/[TargetRPC] methods can only be called on the server.

Synchronizing values across all computers [SyncVar]

[SyncVar] will only sync changes if the variable is changed on the server. This has no regard for localPlayer or localAuthority. Even if it is on an attribute of a class on a gameObject with a network identity component marked as having local authority, it will still not sync changes that are made by the client.

Attempting to set a [SyncVar] on the client fails silently. (Which is dumb - it should log an error. (Actually, it would be really nice if there was a compile error. But how much can you really ask for?))

Because of this, [SyncVar] will sometimes be pared with a [Command] method so that its value can be changed if the client really should have control over the variable. Note, however, that the client should typically not have authority over the value. The server should be running the code that changes the value itself. (Don't forget that servers can and should run collision triggers too.)

You can add a function to be called when the SyncVar updates like this [SyncVar (hook = "functionToCall")]. This function will only be called on all clients. If you do this you must set the variable's new value in functionToCall. Adding a hook to a SyncVar causes unity to no longer update the value automatically. Additionally, the hook is not called when the value is initially set when the networked GameObject is instantiated.

Remember, the SyncVar hook will run on all clients since it syncs to all clients.

Local Player and Authority

From what I can gather, Unity originally only supported a single GameObject having local authority on the client. That is, only one GameObject in the entire game could call [Command] methods and otherwise manage itself. This was fixed when they introduced the concept of local authority. Now objects that are marked as having local authority can do all those fun localPlayer things like calling [Command] methods. Unfortunately, they did not merge the localPlayer concept into the new local authority, so now we have two nearly identical but subtly different ways to do essentially the same thing. Some observations:

  • When a network connection is lost (the player closes the game, etc.) all objects marked as the localPlayer or as having local authority that were associated with that connection are automagically destroyed.
  • Only a single object can be localPlayer, but any number of objects can be local authority.
  • Spawn an object under the server's control with NetworkServer.Spawn()
  • Spawn an object under a player's control with NetworkServer.SpawnWithClientAuthority()
  • Spawn an object as the localPlayer object with NetworkServer.AddPlayerForConnection()
  • this.connectionToClientis used on the server to get the connection to the client. This only works for objects that are the local player.
  • Meanwhile, networkIdentity.clientAuthorityOwner is used on the server to get the connection to the client. This property only works for objects that have local authority. Additionally, unlike literally every. other. property. this property is defined on NetworkIdentity rather than NetworkBehaviour, so you'll need to use code like this this.GetComponent<NetworkIdentity>().clientAuthorityOwner.
  • The local player has a startup method that can be overridden: OnStartLocalPlayer
  • Objects with local authority also have a startup method that can be overridden: OnStartAuthority. This method is called on the localPlayer object as well. As far as I can tell this is literally the only crossover between the localPlayer and local authority concepts.
  • Local authority objects also have a method called OnStopAuthority that will be called if local authority is removed from them with NetworkIdentity.RemoveClientAuthority. Since it isn't possible to mark an object as no longer localPlayer, this function is never called for localPlayer objects.
  • Local authority can be assigned with NetworkIdentity.AssignClientAuthority, and removed with NetworkIdentity.RemoveClientAuthority. These functions probably only work on the server. I don't know what would happen if you tried them on the client, but given Unity's track record they would probably fail silently.

Method Dichotomy

Most functions have a specific place they are intended to be called. (I.e., either the server or the client.) While Unity could have designed the API in a way that made calling client functions on the server a compile time error, instead the made it a run-time error. Worse, they made it an opt-in run-time error. Because of this, you should mark all of your methods with [Server] and [Client]. I even find it helpful to mark lifetime methods with them such as OnStartClient and OnStartAuthority, just to get a message earlier in the debug process if something isn't working as expected.

Methods marked with [Server] that are called on the client will not run, and will trigger a warning in the debug console. Methods marked with [Client], similarly, will not run if called on the server and will trigger a warning in the debug console.

Additionally, I like to define an [Everywhere] property that does nothing, but serves to mark the remaining functions which are intended to be run [Everywhere]. I also think it would be good to create an [Authority] tag which would prevent the function from running on the server or regular clients, but I've never actually done that.

Network Management

Objects inheriting the NetworkManager class can not use [Server], [Client], [Command], [ClientRPC], or [TargetRPC]. (i.e., any of the super useful attributes that would be really nice to use.)

Another Note

Also, UnityEngine.Random.Range is weird. If you give it float arguments the second argument is inclusive. That is, Random.Range(0.0f, 1.0f) may produce a 0.0f, a 1.0f, or any value inbetween. If you give it int arguments the second argument is exclusive. That is, Random.Range(0, 10) may produce a 0, a 9, or any integer in between, but never 10.

Clone this wiki locally