Creating a networking solution
Table of content
- Introduction
- Peer-to-peer vs Client-server architecture
- Client-server networking
- Best practices using the client server model
- When to use UDP instead of TCP
- Client-server in my project
- Conclusion
- Future
- Sources
Introduction
Have you ever seen a multiplayer game run so smoothly while having a huge open map with a lot of game objects in space? The base of such a game is a good network solution.
In this project I’m going to try and create a networking solution which is easily expandable and uses best networking practices, so that a smooth-running game can be built upon it. The question I wanted to answer is: how can you create a networking solution that is scalable and minimalizes the number of packets sent?
Peer-to-peer vs Client-server architecture
The standard in today’s games is a client-server architecture. But why not a peer-to-peer architecture. First of all: how does peer-to-peer work?
Peer-to-peer or refers to a network of computers (and/or devices) that share and exchange workloads. These “peers”, send messages to each other directly. The messages are what controls the game state. This is different from client-server architecture where one central machine controls the whole game. (Franchetti, 2021). With an architecture such as peer-to-peer, there are a few advantages and disadvantages.

According to a blogpost about the Limitations of p2p multiplayer games vs client-server, Akaltar answered, The peer-to-peer architecture is faster, because all the clients are also the server (2013). Every peer can calculate his own movement and you don’t have to wait for an answer of a server. Because you don’t need a server this way is cheaper and more viable for low-budget games. Akaltar also mentions that P2P scales well up to the point when the average peer cannot handle the bandwidth. For the cons of p2p, all of the peers have to send data to every other peer and therefor the bandwidth usage is higher. This is not a big problem these days, because internet connections can send incredible amounts of data. Another disadvantage is security explains Mishal Roomi on a website post. It is very hard to prevent cheating because the client can control his own game state and influence the game by changing the game objects that he controls. (2020) This can be avoided by making one of the peers authoritative, but doing so will make it less scalable. Lastly it is harder to implement a good working p2p architecture than a server-client.

With the Client-server architecture there is a centralized storage. this is beneficial, because this helps prevent cheating. Leeman Cheng writes: “A centralized server means there is control over quality of connection, quick patching and on-the-spot maintenance.”(2017) He also states that people with a bad connection don’t ruin the gameplay for other players because all of the data comes form the server.
All the big companies use the client-server architecture and it does not seem to be changing in the near future. Also using a Server/client architecture you don’t rely on everyone having a good connection. Combined with the more difficult implementation is why I chose to create my prototype with the client-server architecture.
Client-Server networking
The idea behind the client-server networking architecture is quite simple. There is one server that controls the game state and the clients show the visual representation of that game state. Client can also interact by sending requests to the server who then calculates a desired outcome of that request and sends a response in the form of a new game state. The big difference between p2p and server client is that clients aren’t aware of each other. (Silveira, 2015)

There are 2 different types of way to use a server in a networked game, an authoritative server and a non-authoritative server. An authoritative server as the word already describes is authoritative. What I mean by that is that the server handles the game logic, inputs, collision etc… With a non-authoritative server, the clients handle their own inputs. Most games today combine these two methods. Rotation is client sided, but moving is server sided. (Silveira, 2015)
Best practices using the client server model
Separation of concerns: Write discrete components that do one “thing” and they do it well. this is to prevent huge bulks of code. Doing this can make it easier to design, code, test, reason, and maintain your game. (Silveira, 2015)
Reduce packets sent: “Send the minimal amount of information necessary as infrequently as possible.” (What would be best practice of client communication in a multiplayer game, 2013) Think of ways to reduce the packets that need sending.
Packet size: “It’s generally advisable to keep your packets as small as possible because small packets have a faster transmission time.” (Game Sparks Technologies, z.d.) Even though small packets are faster it can be wise to use larger packets if the packet frequency is too high, because it reduces overhead.
Never trust your clients: Don’t trust players because they cheat.
When to use UDP instead of TCP
TCP is a reliable protocol. When you send something through TCP, you are assured that it arrives as long as the connection is not stopped. The downside to using TCP is that packets are larger and it is therefore slower to send. “In general, your game is not fixed to a single protocol: you can use UDP when you don’t require the reliability guarantees of TCP and you can switch to TCP when you do. Under good network conditions (low latency and packet loss), this won’t matter much. But if things get shaky, then you’ll avoid some misadventures associated with lost messages.” (Heroic Labs Team & Contributors, z.d.) Important messages should be sent trough TCP and messages with less important data can be sent through UDP.
Client-server in my project
First, we need a server that can send and receive data. We need a client class where all things related to a client are stored. I have created two UML diagrams that show how the server and client work.

The Server has a server class that starts listening on a port for any messages. To emphasize the best practice, Separation of concerns, the only thing that the Server class does is hold a list of clients and starting the server and knowing which packets do what.
We differentiate the packets by using enumerators that are the same on the client and the server. This way we can differentiate between packets using a number which is small and keeps the packet-size low.
The server holds a Dictionary with a client packet as an id and a method that can read out the information out of the packet. This works with a delegate. “A delegate is a type that represents references to methods with a particular parameter list and return type. When you instantiate a delegate, you can associate its instance with any method with a compatible signature and return type.” We can create an abstract method just like in an interface, but instead of a class it is a method. When in the TCP class a packet is read, we can read the first number and then use the dictionary to find the method that can read out the data and do something with it.


The ServerHandle class is in charge of handling client packets. The server has to handle every interaction that a player wants to do, so this can make this class large pretty fast. This is also what happened with the ServerSend class where a child of this class is created to keep the code in small amounts and also separate concerns. This is in line with the best practices and design goals set out for this project.
Because you don’t want every class just sending random packets, we have a centralized place where we sent packets. This is the ServerSend class.

Now for the client, it works in a very similar way as the server, but instead of calculating everything it asks if it is allowed to… For example, if the player wants to shoot, it first creates a packet with the number in the Client packet enumerator that is signed to this kind of packet. In this case 4. We send the rotation of the player because the rotation is calculated on the client for lag reasons. If you calculate rotation on the server, you need some sort of movement prediction which is definitely the next step in this project.

In one of the blogposts, I read researching this project, NoobsArePeople2 answered the question:” What would be best practice of client communication in a multiplayer game?” with this: “Send the minimal amount of information necessary as infrequently as possible. In my case I maintain the state of the keyboard by listening for the keyup and keydown events in the client.” (What would be best practice of client comunication in a multiplayer game, 2013) Instead of sending a request every tick, only sent one if the inputs change. This is a big improvement in packets sent and I implemented this


While testing this I played the game for 10 seconds and calculated that with this change there was a 73% decrease in the number of times a position packet was created when applying this change.
Conclusion
My main question I wanted to answer going into this project is: how can you create a networking solution that is scalable and minimalizes the number of packets sent? I think I achieved this goal and I might use this code in the next project if the plan is to create a networking game. My main goals were keeping the packet size low and reducing the packets sent as much a possible. I did not do anything to keep the packet size’s as small as possible, but I found a way to decrease the number of packets sent by only sending key input changes. Lastly the best practice separation of concerns I tried to achieve by creating a different for each different element in the networking solution.
Future
For the future I would like to experiment with client-side prediction and using animations to disguise any lag. Right now, the rotation of the player is client sided, but with client-side prediction I might be able to make rotating a server-side feature.
Sources
Ccna, M. M. (2001, 2 juli). Exploring the anatomy of a data packet. TechRepublic. Geraadpleegd op 7 november 2021, van https://www.techrepublic.com/article/exploring-the-anatomy-of-a-data-packet/
Franchetti, J. (2021, 10 mei). Building a peer-to-peer multiplayer game with pub/sub. Ably Blog: Data in Motion. Geraadpleegd op 7 november 2021, van https://ably.com/blog/peer-to-peer-game-with-pub-sub
Game Sparks Technologies. (z.d.). Real-Time Best Practices – GameSparks Learn. Game Sparks. Geraadpleegd op 7 november 2021, van https://docs.gamesparks.com/tutorials/real-time-services/real-time-best-practices.html
Heroic Labs Team & Contributors. (z.d.). Choosing TCP or UDP – heroic labs documentation. Heroic Labs Documentation. Geraadpleegd op 7 november 2021, van https://heroiclabs.com/docs/nakama/concepts/tcp-vs-udp/
Limitations of p2p multiplayer games vs client-server. (2013, 22 december). Game Development Stack Exchange. Geraadpleegd op 7 november 2021, van https://gamedev.stackexchange.com/questions/67738/limitations-of-p2p-multiplayer-games-vs-client-server
Roomi, M. (2020, 15 november). 7 Advantages and Disadvantages of Peer to Peer Network | Drawbacks & Benefits of Peer to Peer Network. HitechWhizz – The Ultimate Tech Experience. Geraadpleegd op 7 november 2021, van https://www.hitechwhizz.com/2020/11/7-advantages-and-disadvantages-drawbacks-benefits-of-p2p-network.html
Silveira, R. (2015). Multiplayer game development with HTML5. In Multiplayer Game Development with HTML5 (1ste ed., pp. 1–12). Van Haren Publishing. https://subscription.packtpub.com/book/game_development/9781785283109/1/ch01lvl1sec08/understanding-the-basics-of-networking
Visual difference between P2P and P2 Client/Server. (2016, 22 juni). [Gif]. https://imgur.com/gallery/emtuYmH What would be best practice of client comunication in a multiplayer game. (2013, 5 maart). Game Development Stack Exchange. Geraadpleegd op 7 november 2021, van https://gamedev.stackexchange.com/questions/50451/what-would-be-best-practice-of-client-comunication-in-a-multiplayer-game
Gitlab: https://gitlab.fdmci.hva.nl/valentj3/rdmultiplayerproject
Author: Joost Valentijn