Thursday, December 18, 2014

Choosing Transfer Propotocol



Once our app architecture is designed, we have to define a communication interface between clients and server. This interface shows how client and server can interact  in a non ambiguos way. This dialog have to be determinist in order to make an affordable implementation.
You can see the detailed flow or commands in the following diagram. 




 If we want to create a new type of client we just need to focus on implementing this interface  contract. It doesn’t matter the tecnology below our implementation. At the lowest level, The interface contract just tells us which stream of bytes need to be sent in order to achieve some funcionality.
Going one step down, it has to rely on a network protocol. Since we doesn’t want to reinvent the wheel, we want to interchange data through internet and in a fast way (minimizing latency)  there are just two options realistic: TCP or UDP protocols.
For non familiars, these two propotocols are capable of sending and receiving packets of data between to points. TCP is a step upper udp (ISO tower) since it assures you that packets are going to be received at the other side and at the same order that they have been sent. Of course, this capabilities are not for free. In a non visible way, this controls is done internally by TCP so it’s never an easy choice which protocol to choose.

Let’s suppose that we choose TCP because of these out of the box funcionalities. Later we are going to review this decission. By choosing TCP, we know that network will be such  a data pipeline. Data are going to arrive to our peer and in the order we wanted.
In order to optimize TCP protocol with the objective to deal with high latencies you have to take some decissions:
  • Deactivate  Nagles algorithm. By doing this, packets are going to be sent once ordered. It would seems quite obvious but it’s not. Nagles algorithm tries to fill every packet sent  in order to minimize global congestion. This works fine when you want exchange big amounts of data but this solution hits you severely if you want to reduce your latency. Through our tests we have saved  an average of 200-300 ms by deactivating this algorithm. Easy to do but difficult to know. At the end, one of the best decissions we made.
  • Related to the last point , we have to assure to flush buffers. This is not a TCP protocol issue or not just a protocol issue. At a prior level, but also a problem, java streams try to gather all information together in order to send once a packet is full. Once again, we are struggling against the latency. We can’t afford this behaviour so, let’s flush everything. Always send and flush.
  • Personal serialization when needed. Java serialization are so easy to do but it’s a real big black box. You know that you are  sending your classes  across the net but you don’t know at all how this really works. You just implement serializable  interface and it works! But… again, we want to minimize our transfer data. This is not an issue related to the latency but the size of the information that we deal with. Java Serialization puts in the wire recursively all the hierarchy of a  class. You can calculate  the amount of data that  we are  going to send. A sniffer will show you this in detail. How to improve? Implement Externalizable interface and override writeExternal and readExternal methods in order to just send and read what you really need to rebuild your class at the other point of the net. It’s not difficult to do and you can reduce an incredible amount of data transfered.

Wednesday, December 17, 2014

Architecture and first decissions


After the first step, we focus on how the client and players will interact.
If we want to get  a real online multiplayer and smooth game we have to respond smoothly to users demands. This means that if the client moves in some direction it should be reflected on the screen asap. You might think that this last premise conflicts with a client/server architecture. Not Actually. We are going to use player predictions to solve this issue. Let me explain myself. We are going to follow this guidelines:
  •     Every remote player will move his own snake by his own. The objective is that the player can perceive the movements in real time on the screen.
  •     Every remote player will send his movement at the end of his tick to the server. So, once every remote player have sent their moves, the server will send back to the clients the consolidated  movements.
  •     Once the remote player gets the consolidated movements, he will move the other snakes.
The consequences of this guideline is that players will see a smooth game where the snake itself moves as if we were playing in a standalone game. The rest of the snakes will move with a certain gap after the own snake has moved. 
OK, it seems that it works but there’s always some issues that affects predictions...

What happens if, for any network reasons, a remote movement from player never reaches the server or it arrives once the server tick is ended? Yeah, this is a big problem since the server is going to send a different movement from one of the players that has already moved in a local way. To avoid this issue we must implement a local correction engine. This means that remote clients have to detect if the movement, already made, conflicts with the one sent back by the server.
At this point, we are going to take another critical decision. We decide that our server will be a mandatory server. What the server has in mind is what the world real is. So predictions will be the way it sounds: just predictions. If the remote client detects a conflict between last local movement and consolidated server movements, the client has to move back to the last local movement  and move again based on the server orders. As a consequence, every remote player will see the same game, that is what it is supposed to be. How this affect this game? Of course, it has consequences: the remote client will see, eventually,  that his snake will change his position once moved.


We will have to minimize this last effect from our game. Let’s Write down who will be our worst Enemy: Latency. In the following entries we will learn how to struggle this formidable opponent with some tricky strategies.

Introduction


Real time game. Three words that summarize the mission that we want to achieve. Even Better: High Frequency Real Time Game.

Create a multiplayer game in real time. This will be our mission throughout this blog. Even Better: we want to  create a multiplayer game in real time in which movements will often succeed at milliseconds rate.

A major challenge we will face in the next chapters.

What applications will benefit from the engine that will build? Certainly a variety of areas, but we have to focus on a specific use to show how to achieve this goal.

Looking back, we will take as a reference of a classic game Snake. If you put an eye on it, you will realize that green large animal moves pretty fast. We will implement a version of Internet snake through the determination of the game is perceived in the same way to the standalone version. Like when a child is and there are no limits, we have added the following to the wish list:
  •     We don’t want to limit the number of players.

  •     The game has to be fast. Maybe no as fast as the original but quite fast.

  •      We want to play with my smartphone, performing a first implementation in Android.

  •     We want to play through mobile networks. We don’t want to be forced to  use wifi if we want to play in a fast way.

  •    We do not want to see us affected when people leave the game.

It’s not an easy list to achieve. Specially the points related to the agility and mobile networks. Anyway, we were young and we still have no limits, so we are going to make it.

Where do we start?
In relation to architecture, which approach we will take?. Client to Client approach or a client-server architecture?. Well, let’s think about it. We do want to play several players at the same time. It seems clear that someone has to be the referee. Someone has to guarantee the real state of the game since remote players can have networks issues or, directly, leave the game. This referee can’t be one of the remote players ( as we have just said he can leave the game ). It has to be someone else. Who? The game server.

We also should think in which language we are going to use for implementing the game. At client side we will have to use Java or Objective-C (later) if we plant to be at Android /iOs Markets. At server side, for sure, C is the fast way we can do it… but, why not Java?. Yes, its more slower than C but this won’t be our bottleneck: it will be the net. Besides that, we are not planning to draw 3d pictures (at least, at the beginning). Java inheritance and polimorphism are such powerful tools that we want to be on our side. So, Java is our language. Besides, Android is Java based so at Android version  we would have client and server layers using the same language.

In the following entries we will go deep inside this approach detailing some specifics that will make us win the battle.