Search Unity

Unity Networking Notes

Discussion in 'Multiplayer' started by jayshua, Dec 12, 2018.

  1. jayshua

    jayshua

    Joined:
    Oct 16, 2017
    Posts:
    3
    Last year for my senior project some friends and I created a multiplayer game supporting 40+ players. I was going through some leftover documentation and found this write-up that I created on the horrors of Unity's networking system. I don't know if it's changed at all since that time, but here's the info if it helps anyone out.

    --------

    Unity Networking
    These are some observations about the Unity Networking API.

    Calling functions somewhere else
    Using these attributes you can cause functions to execute on a computer different from the one on which they were called. This could be useful for starting a particle effect on all clients, or displaying a notification on a specific client.
    • 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 function marked with [Command], [ClientRPC], or [TargetRPC] as an event handler does not work. (E.g. someObject.OnEvent += CmdHandler;) The function will be executed on the computer it is called on instead of elsewhere without any type of message or error.

    [Command] methods can only be called on objects that are the localPlayer or that 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 local authority. Even if the attribute is on a NetworkBehaviour that has local authority, the changes on a client will still not be synced from client to server.

    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 on the client. Note, however, that the client should typically not have authority over variables. The server should be running the code that changes the value itself. (Don't forget that servers can and should run collision triggers too. Just check if it is the server in the trigger and update the [SyncVar] there.)

    You can add a function to be called when the SyncVar updates like this [SyncVar (hook = "functionToCall")]. This function will 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, but the correct value is set.

    Remember, the SyncVar hook will run on all clients since it syncs to all clients. You may wish to check localPlayer or hasAuthority in the handler.

    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 Unity 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 which does nothing but serving to mark the remaining functions which are intended to be run everywhere. This helps with telling which functions can be called where. I also think it would be good to create an [Authority] tag which would prevent the function from running everywhere except on a client with local authority. I've never done that however. It would probably require modifications to the UNetWeaver.

    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.)

    Other Notes
    The Unity networking API works through a process called Weaving. This is a technique where your compiled C# code is run through a program which makes modifications to it. That program is called the UNetWeaver. When you receive a UNetWeaver error it means the program could not make the modifications it was trying to. That's why the UNetWeaver errors don't disappear from the Unity error console - since they aren't really compilation errors but post-processing errors.

    ----------

    Suffice it to say Unity networking was a horrible experience. I created a super basic game supporting 40+ players in C and enet backed networking that was better experience. Anyway, that's enough complaining. Hope you have better luck than I did.
     
    nixalott and Cranom like this.