I’m very new to network programming in general and working for a week now on a UNET project.
Is it safe to use only network messages for communcation between scripts? I tried the other HLAPI stuff like Command, ClientRPC and syncvar and came to the conclusion that the didn’t work quite well with our existing architecture.
Network messages give us the freedom we need to get networking running without having ugly hierachys or a dozen of network behaviours at a root object. Is there any downside of this approach?
Not directly replying to your question - but I’ve built an open-source alternative to UNET for multiplayer (and chat and more) called Nakama. It would be good to get your thoughts on it and see if there is anything missing that you’d like to see in there.
Probably somewhere, but unless you’re making every little thing it’s own message class I doubt you’ll have a problem. I’m mainly saying that from knowing that you can only have so many RPC’s and Command’s, so I would assume there’s some sort of hard limit but I don’t know if there actually is one. And even if there is, it might be something that you’ll never reach if it’s high enough.
As mentioned already, if you use RPCs as they are designed then they can be a very effective tool. However, you question the downside:
An RPC is most certainly a high-level concept in networking. It makes your solution easier to use and manage because an RPC (remote procedure call) is the process of calling a function/method in your code, from a remote location. What could be easier to understand than that?
As with anything in technology, concepts that are designed around being easy to use or convenient often suffer when it comes to performance. There are three performance issues that I am aware of:
An RPC has to send information about which object the RPC is targeting, and what is to be called on the object. This is a fairly minor issue as it will only ever be a few extra bytes but it’s still an overhead that can be avoided.
Within the internals of any networking solution, in order for an RPC to come in and be processed correctly, the solution needs to keep track of all “networked” objects (objects that can have RPCs called on them). On top of that, the incoming RPC needs to find the object, then use reflection to ensure that the method that you want to call can be called. This can add significant RAM and CPU overhead if you rely heavily on RPCs.
Finally, a “buffered” RPC is an RPC that is queued up to be sent to newly connecting peers. So for example, if you use an RPC to open a door, you would need to “buffer” that RPC so that newly connecting users get the RPC to open the door. Otherwise, the door would be closed when they connect. Buffered RPCs can easily queue up to very high numbers, and if you’re not careful can cause a lot of problems.
Personally, I always avoid RPCs. As soon as I came across the issues with buffered RPCs I came to the conclusion that the convenience of RPCs is just not worth the added issues. Some examples of how to get around using RPCs:
in the “open door” example. Synchronize a boolean over the network that states whether the door is open or not, and handle the changing of this boolean on the client side
when a player connects, send them all of the data that they need about objects on the server (such as all of the objects that exist, along with their current properties)
when new objects are spawned/destroyed, or their properties change, notify the peers using custom messages
There are a few minor drawbacks to the above, in that when a player connects and you send all of the object data in one go, depending on your solution this could be a lot of data, and also if an object gets destroyed during the sending of this data you can end up with a “stranded” object, where it exists on the client side, but has been destroyed on the server. The amount of data sent though will always be less than if you were to use high-level concepts (such as syncvars and RPCs), and the issue with stranded objects can be solved by storing an array of ID values of objects that have been destroyed, and not spawning an object if it’s ID is in the array.