Creating multiplayer game logic for My Giants turned out to be a very challenging task. There are many concepts and different layers of complexity that need to be addressed in order to get good results. For us, the major decision was which kind of server logic we want to build: non-authoritative, authoritative or semi-authoritative.
A non-authoritative server exists only as a kind of proxy between all the connected clients. The server relays messages sent by clients and doesn’t know anything or very little about the game logic. All of the game logic is implemented on a client. Such setup is prone to hacking and cheating, since it is possible to change the original game logic on the client. Hacking software even exists that can automatically search and modify important game variables like lives, score, etc… On the other hand, since all the logic is on the client side, the server requires much less cpu and memory resources than an authoritative server.
Authoritative servers are the most secure in terms of cheating because all game logic runs on the server. The server contains proper game state at any moment and it can detect and override possible client’s attempts to cheat. For example, on the client side character speed is hacked in order to gain advantage over the other players. However, this does not make any difference for other players, because the server calculates the movement of the hacked player and only the server result is relevant.
Semi-authoritative server is a blend between the two aforementioned approaches, giving some authority to the client over certain aspects of game logic. For example, in semi-authoritative setup, client reports to the server when an opponent is hit and should receive damage, and the server keeps track of a player’s health status and decreases it accordingly.
In the course of a few months our networking game logic went through many iterations of improvement and experimenting, trying three networking technologies:
I’ll try to give an overview of our experience with the three technologies, some of the difficulties we had and a few core differences. Our networking logic is still in development and there will probably be other observations as we progress, but this is as good time as any, to share some of the things we picked up along the way.
With almost no prior experience with multiplayer games, we started using Unity built-in networking as it seemed the easiest approach. Unity Networking is nicely integrated into Unity and works very well for rapid development. Network Views are components that make game object network-aware. Through Network Views, using State Synchronization and Remote Procedure Calls, any kind of multiplayer game logic can be implemented.
Our intention was to create a working prototype in a fairly short amount of time. There are a lot of examples and nice tutorials to learn about Unity networking (i.e. M2H Tutorials), which helped us a lot in the beginning. We had basic movement and animation synchronization done in a matter of days. At that point, we only used peer to peer connection and we didn’t even think about the idea of having a headless server running on a cloud or VPS. P2P connection gave us a lot of headache during tests, because the servers were behind the router and the NAT punch-through functionality didn’t work as expected. These initial problems paired with a general opinion in the Unity community that built-in networking is not suitable for real-world games, made us think about a different solution. Although My Giants is envisioned as a game with 10 to 20 players per session, which Unity Networking could probably handle, we didn’t want to lock ourselves to a solution that, supposedly, has limitations with a fairly low number of players. True or not, we started considering other solutions and decided to try out Photon Network Engine.
When we started working on MyGiants networking, we soon found out that the semi-authoritative setup was the only way to go at that point. We didn’t want to have a non-authoritative server because we wanted to keep a level of control over cheating, and if we wanted to have fully authoritative server, we would have to use Unity built-in networking, which has it’s own problems in regards to scalability and performance.
There are a lot of 3rd party server solutions available and after spending some time on evaluation we chose Photon from ExitGames, a very powerful network engine with optimized socket communication, reliable and non-reliable protocol built on top of UDP, encryption and many other nice features. The only thing we had to give up was the idea of a fully authoritative server. Photon server application is not aware of Unity geometry, physics and the collision system, which means we would need to recreate all those parts on the server side. This is a difficult task and what’s more important, prevents us from re-using Unity’s built-in features. That is why we used the semi-authoritative approach.
We were able to implement networking logic relatively fast. That includes: player movement and animation states, throwing items, damage and health, NPC movement.
Synchronizing NPCs gave us a bit of a headache. When synchronizing players, it is clear who is the owner. The player is the owner and the player is responsible for updating its own position. With NPCs, the server should be the owner, but Photon server can’t be the owner because it doesn’t know how to move NPCs. Photon server is not aware of Unity geometry and collisions. Thus, we need to move NPC ownership to one of the players. The server assigns ownership to one of the players who becomes a master client. This also requires logic for shifting ownership to another player in case the master client loses connection, which brings all kinds of synchronization problems.
When comparing Photon and Unity Networking, Unity Networking makes it simple to write networking code that is more object oriented because each Game Object has Network View which is added to the object as Unity Component. For example, code for moving a character can be put into a script “PlayerController.cs”. The script is added to Unity Game Object – Character. This object also has Network View, and the PlayerController script uses that Network View both on the client and the server. The code is encapsulated and easy to maintain.
With Photon, it is a bit more difficult. More work and knowledge is required to achieve this kind of abstraction, both on the client and server side. We were beginners with Photon and after some time we ended up with a very centralized networking code with 1000+ lines of networking code in one script, a lot of switch and if-else cases used just to relay messages between the server to the client and then to proper game objects. The code soon became unmanagable and needed refactoring. Aside from that, working with Photon is a pleasant experience. The framework is very good, there are basic applications which can be extended with own custom server logic and there is plenty of documentation and examples. Server logic is written in C# and is very convenient for us, because we mostly use C# for Unity scripts also.
During evaluation of different server solutions for multiplayer games, we noticed uLink as one option that was not that famous and was not talked about that much on the forums. It’s probably because the price for the product is very high and it seems like it’s aimed at already established, large studios. Because of the price we soon forgot about it but came back to it at the time we started refactoring our Photon code. uLink was recently made available to indies at a very popular price and we decided to try it out.
uLink is more similar to Unity Networking than to Photon. It is also fully integrated into Unity and has Network View components, State Synchronization and Remote Procedure Calls. In fact, it is possible to automatically replace Unity Networking with uLink using uLink converter – that’s how similar the two technologies are. So, what’s the difference?
- uLink is supposed to be much more scalable and optimized than Unity Networking.
- certain built in concepts make multiplayer development easier, for example:
in uLink settings there is a checkbox “Authoritative server”. If the checkbox is clicked, uLink refuses to execute RPCs or sync states between the clients. Clients can only call RPCs on the server and only the server can sync object states to the clients.
Game Objects can be instantiated on all clients and on the server with a single call, but different prefabs can be used depending on where the object is instantiated. For example: when NPC is instantiated, the server is creator and it instantiates an object with prefab “NPCServer” which contains the whole AI logic for moving, attacking etc… while on the clients another prefab “NPCClient” is used, which is a lightweight version of “NPCServer”. “NPCClient” contains animations and simple logic to update character position, according to updates recevied by “NPCServer”. This is a very powerful concept and when paired with C# subclassing, produces clean and maintainable code.
- powerful test tool uTsung which can be used to generate many connections on the server and test how the game behaves under different conditions.
- uCollab, a Unity plugin that enables the developer to open two Unity editors, load a master project and a client project which is synced with the master project. That way, a developer can have a client scene loaded in one editor, server scene in another and debug both scenes, during multiplayer game testing. This boosts productivity significantly.
In the end, we were able to port most of our Photon server and client side code in a week and a half, improving the overall code architecture, making it easier to add multiplayer features in the future and what’s most important, we moved from semi-authoritative to fully authoritative server.
The main difference between Photon and uLink is that when using uLink, game server logic is developed in Unity. The server knows about the scene geometry, physics and collisions which makes development easier. In case of our own game My Giants, every match game is one game server instance, running on a cloud server. Game server instances are automatically started or stopped, depending on the number of concurrent players.
Photon server application is not aware of Unity geometry, so in our opinion it is best suited for the type of games that don’t need this kind of information. For some games it is easy to write custom collision system, some games require the scene to be divided into movement areas (rectangle, hexagonal)… In this case, Photon is just as attractive a solution as uLink, so it comes down to choosing the best tool for the job. Inadvertently, we got to know three different networking technologies, and although we spent some extra time on it, it’s always good to have an extra weapon in a developers’ arsenal.
uCollab Client + Server (VIDEO):