Creating a networking solution

Studentnaam: Joost Valentijn
studentnummer: 500827123
Git: https://gitlab.fdmci.hva.nl/valentj3/rdmultiplayerproject


Tabel of contents

Intoduction
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
Client side movement prediction
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, minimalizes the number of packets sent and has a high reactionspeed?

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.

figure 4
Figure 1

 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.

Figure 2, (Visual difference between P2P and P2 Client/Server, 2016)

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)

Figure 3, (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.

Figure 4, (Ccna, 2001)
Figure 5, (Ccna, 2001)

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.

Figure 6, class diagram Server
Figure 7, TCP-class
Figure 8, Server-class

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.

Figure 9, class diagram Client

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.

Figure 10, Player shoot packet creation

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

Figure 11, only send when input changed
Figure 12, Test amount of packets sent

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.

Client side Movement prediction

When a client sends their inputs to the server it takes a while to get to the server and back. This is called Round Trip Time (RTT). When dealing with round trip times up to 300 ms it can be hard to play the game.

To solve this issue we have to rethink the way we see the client. As of now the client is nothing more than a visualisation of the data that is sent from the server. We call this a “dumb” or minimal client. To create a more instant feel of the game we have to pretend that the movement is instant by calculating the movement on the client. Make the client less dumb so to say. This does not mean that the client is fully in control of the movement. There is still an authoritative server that is running the calculations. “Having an authoritative server means that even if the client simulates different results than the server, the server’s results will eventually correct the client’s incorrect simulation. Because of the latency in the connection, the correction might not occur until a full round trip’s worth of time has passed.” (Bernier, 2001) If you would just immediately use the server’s results something like figure 13 might happen. On the client 2 movement commands are executed and calculated on the client before the server packets return. The position of the player would be as follows: (10, 10) (11, 10) (12, 10) (11, 10) (12,10). The client calculated the movement correctly, but because of the delay there is jittering. Jittering is shown in figure 14.

For prediction to work correctly we use the last acknowledged position by the server as a starting point. This will be a position that is, relative to the current game state, in the past. When sending packets to the server, the client also stores those packets and the time the packet was sent. This information will be useful for when a new position gets acknowledged by the server. “For instance, if the client is running at 50 frames per second (fps) and has 100 milliseconds of round-trip time, then the client will have stored up five user commands ahead of the last one acknowledged by the server.”  (Bernier, 2001) Starting with the acknowledged position and simulating the movement of the other user commands that have been stored up, we can predict the movement without jittering the entire screen around and still give the server the final position (figure 15).

Figure 13
Figure 14

Difference between movement prediction on and movement prediction off.

Figure 14 (with movement prediction)
Figure 15 (without movement prediction)

Conclusion

My main question I wanted to answer going into this project is: how can you create a networking solution that is scalable, minimalizes the number of packets sent and supports movement prediction? 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 as 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. The best practice, separation of concerns I tried to achieve by creating a different for each different element in the networking solution. Lastly, I added client-side prediction and with 500 ms it feel good to walk. Of course, this is just a demo and with more and more features the time it takes and the difficulty of making the prediction feel good will increase.

Future

For the future I would like to experiment with a server rollback system. With that I can make a game where time traveling is one of the powerups. Everyone gets teleported back to the position a second ago. IT would also be a valuable next step to integrating a more sophisticated prediction algoritm.

Sources

Bernier, Y. W. B. (2001). Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization – Valve Developer Community. Valve Developer Community. Retrieved 31 March 2022, from https://developer.valvesoftware.com/wiki/Latency_Compensating_Methods_in_Client/Server_In-game_Protocol_Design_and_Optimization

Franchetti, J. (2022, January 13). Building a peer-to-peer multiplayer game with pub/sub. Ably Blog: Data in Motion. Retrieved 7 November 2022, from https://ably.com/blog/peer-to-peer-game-with-pub-sub

Gambetta, G. G. (n.d.). Client-Side Prediction and Server Reconciliation – Gabriel Gambetta. Gabrielgambetta. Retrieved 31 March 2022, from https://www.gabrielgambetta.com/client-side-prediction-server-reconciliation.html

Heroic Labs Team & Contributors. (n.d.). Choosing TCP or UDP – heroic labs documentation. Heroic Labs Documentation. Retrieved 7 November 2021, from https://heroiclabs.com/docs/nakama/concepts/tcp-vs-udp/

Limitations of p2p multiplayer games vs client-server. (2013, December 22). Game Development Stack Exchange. Retrieved 7 November 2022, from https://gamedev.stackexchange.com/questions/67738/limitations-of-p2p-multiplayer-games-vs-client-server

Mullins, M. (2007, June 8). Exploring the anatomy of a data packet. TechRepublic. Retrieved 7 November 2021, from https://www.techrepublic.com/article/exploring-the-anatomy-of-a-data-packet/

Real-Time Best Practices – GameSparks Learn. (n.d.). Game Sparks Technologies. Retrieved 7 November 2022, from https://docs.gamesparks.com/tutorials/real-time-services/real-time-best-practices.html

Roomi, M., & Roomi, M. (2020, November 15). 7 Advantages and Disadvantages of Peer to Peer Network | Drawbacks & Benefits of Peer to Peer Network. HitechWhizz – The Ultimate Tech Experience. Retrieved 7 November 2022, from https://www.hitechwhizz.com/2020/11/7-advantages-and-disadvantages-drawbacks-benefits-of-p2p-network.html

Silveira, R. (2015). Multiplayer Game Development with HTML5 (1st ed.). Van Haren Publishing.

Visual difference between P2P and P2 Client/Server. (2016, June 22). [GIF]. Imgur. https://imgur.com/gallery/emtuYmH

What would be best practice of client comunication in a multiplayer game. (2013, March 5). Game Development Stack Exchange. Retrieved 7 November 2022, from https://gamedev.stackexchange.com/questions/50451/what-would-be-best-practice-of-client-comunication-in-a-multiplayer-game

Related Posts