mercoledì 30 novembre 2016

Day 193: Network interpolation completed

Today I managed to finally fix and complete the interpolation code. I still had many problems, and I was able to fix them all today.
First, I wasn't able to address the lag problem. So today I noticed that there was something wrong... My interpolation was happening between the last predicted position and the position received by the packet, but this doesn't account for the latency.

The code I had was doing the following:
- interpolation to smooth from the last predicted position to the received position
- extrapolation to predict the position in the next frames

And now I replaced it with this code instead:
- extrapolation to calculate the predicted position
- interpolation to smooth from the last predicted position to the predicted position
- extrapolation to predict the position in the next frames

Now everything works fine.
I ran testing with 150ms of lag, and the result is very satisfying: the path is of course imprecise, but smooth. Also, if the prediction is correct (i.e: going in a straight line, or rotate at constant speed) the prediction is perfect even with very high lag.
I also tested the game with every possible bad network condition active, and nothing presented much of a problem (except packet tampering, but that's not really something to worry about).

giovedì 24 novembre 2016

Day 192: Disconnection, Network time interpolation and testing

Today I did three major things:

- Handling disconnection
Now when a client goes away, his id goes back to a pool of disconnected ids. If a new client comes in, he's gonna take his place, thus avoiding leaking players behind

-Testing
I used Clumsy, a program to disturb network packets in many ways, and I tested many aspects of the game. The game reacts very well to packet loss. Then, noticing that it didn't react nicely with packets out of order and duplication, I add a check that removes packets with a time which is smaller than a time already received, and that solved both problems. Finally the game behaves not so well with lag, with a very intense...well..."laggy" effect.I'm not sure if this is entirely my fault or if this is what I should expect from the simple interpolation method I'm using.

- Network time interpolation
Testing allowed me to notice that with high lag the client ships were just following behind, which meant that I was not using the network shared time for interpolation, but just the normal local time. So I added a timestamp to packets, and the value is used for prediction. This solved the lag problems (meaning that now it syncs the players correctly, but the movement is still badly jittery)

mercoledì 23 novembre 2016

Day 191: Network Game

Today I worked a lot on the engine, and I managed to create a fully functional game.
First thing I added is a new control system for the players which allows them to rotate and move in the direction they are facing. I also downloaded a simple ship model and replaced the cubes with it.
Then I added a ball object to the screen. Each ship can try to acquire it, and if successful the player get a score, and the ball is reset to another position. This required the following:
- handling differently the reception of player and ball packets
- handing physics (in this case a simple collision resolution) in the server
- create a new type of message to send updates about the score
- handle the reception of these packets
- print the scores on screen
and many other minor or major adjustments.
I'm not excessively happy with how I divided things between the engine and the game though, and many times I had to force the engine to behave in not elegant ways to force a struct or a class to remain inside the game or engine project. This happened mostly because I had to speed up the development of the project due to a deadline, which I never did since the beginning of the Ryno's development. Hopefully I'll have time to improve the code later.
In the picture you can see a screenshot of the game with all the features implemented. Only thing missing is a end game message and maybe a countdown.


martedì 22 novembre 2016

Day 190: New message format

Today I started by fixing a few problems, like the impossibility to set a network object in a certain position without having to lerp. So I added a reset_position method which reset the cache and set the position immediately. I also fixed a delay at the beginning of the game that made the objects spawn only after the first time update.
Then I noticed that the messages of my games were defined inside the engine, so I tried to move them in the client, but after an hour I had to rollback, because the use of unions and inheritance are quite problematic. Also the client class now handles many important aspects of the game, and this refactoring would have implied a migration of many functionalities directly in the game code, which is not ideal.
So in the end I decided to abstract the types of messages directly in the engine, so that they can be used by any king of game. For instance I created a Object_Update message which contains position, scale and rotation about the object. To make this system adaptable, I added to this message a selector code: if the game has two different kind of objects, he can use the same message with a different selector which will  be defined in the game code, and will allow to handle the message differently. I'll use this feature soon because I'll probably add a Ball class to the game.
In the end I also had to adapt the prediciton class inside the NetworkObject to also account for rotation and scaling (picture below), and I also improved the way in which it handles data conversion.


lunedì 21 novembre 2016

Day 189: Network time completed

Today I noticed that I was only updating the network time when a packet is received, and I was not increasing it along with local delta time.
So I modified the class to increase time properly. In the picture you can see a good degree of synchronization between the clients.
I then noticed that while dragging windows around SDL freezes. I assume that only rendering and time calculations are affected, because when the game is resumed, it is clear that the game was still running underneath. The problem of this is that if the time is not incremented it is easy to end up with mathematical errors, and indeed the game was placing objects in a NaN position because of this. I added checks and now the game is robust against this problem.


giovedì 17 novembre 2016

Day 188: Network time syncronization

Today I had an incredibly productive morning, and I managed to do in a few hours the amount of work I expected to do in 4 days.
I started by implementing a NetworkTime class to handle all the information related to network time, and I also refactored the TimeManager class to make it a bit more accessible.
After that I refactored the messages, and created a ClientUpdate and a ServerUpdate message.
After that I spent most of the time working on the protocol. Right now a client cannot send messages (except for the ClientUpdate one) until he's connected. To connect a client must send an update, get an answer from the server and sync the time. The time is then averaged before the last received packages to avoid spikes due to floating latency.
To achieve this I had to handle this new types of packages both on client and server.
In the end I also added safety measures to remove every net objects and close the client if the connection with the server is lost.

In the picture there is a bunch of number in every shell. They are the difference between the local and network time. All of them are a couple of seconds bigger than zero (which is roughly the time it takes to establish the first connection), and then the difference remain roughly constant.
Still, I'm not storing the difference, but the actual network time, which is safer in case a client has a faster clock.


venerdì 11 novembre 2016

Day 187: Linear interpolation

Today I managed to implement linear extrapolation, and now the prediction is smoother and more accurate. However I tried to use also acceleration and jerk in the extrapolation, and I have serious problems making it work. I also tried using a cubic spline but I have the same problem. Most of it is because I'm not sure of which data I need to use, and so for now I'll just linear interpolation until I find a better way to do it.

I also added a way to disable manual camera control in the scene.

mercoledì 9 novembre 2016

Day 186: Prediction model

Right now the engine was setting the position of a network object only when receiving a packet. This imply snappy objects and/or sending a huge amount of packets. To solve this I'm now keeping track of previous data, and then interpolate to predict results.
To do so I created a struct for each network object which holds the last two positions, velocities, accelerations and even the last jerk (actually only the last value of each pair is needed, but two positions are required to calculate one velocity and so on).
So when a packet is received these values are updated accordingly, and then in the update method the predicted position is guessed.
Right now I only implemented linear interpolation (X = pos + time * vel), and it works, so I assume the same for the other models. I'll test them at the end because right now I don't have a scene that simulates acceleration and jerk and I don't want to lose time implementing it at this stage.
The main remaining problem is that when a new packet is received, the object snaps to the correct position, and in case the prediction was not accurate that's very noticeable. The solution is extrapolation, which is an averaged interpolations between the old predicted position and the new predicted position, which should grant a smooth transition. I'll try implementing it tomorrow.

domenica 6 novembre 2016

Day 185: Refactoring and disconnections

Today I started by trying to implement a system to exchange timestamps and sync the global time/ I then stopped because it's not clear to me how to cope this with the absence of connections in UDP. An example: if a client starts sending data before syncing, and then syncs with the server, every time value used until then must be somehow adapted, and everything can get messy, so before I have a clear idea of where to go I'll wait implementing this.

So i then proceeded to fix a bug I had during disconnections: a flood of "failed recv" messages.
I fixed it and I decided to take it one step further and write a system that delete every object who hasn't received data from the network for a long time. To do that I also had to refactor huge chunks of the code, including the abstraction of the NetworkScene in a Base component that allows the child scene to only handle game related code.

giovedì 3 novembre 2016

Day 184: Unions

Today I tried to understand how to make the system exchange different types of packets. My original idea was to have a code_id at the beginning of each message, so that the recv could read that, and then depending on the code, another recv would read a different type of structure.
My networking professor suggested to use unions instead. I still think my idea is more bandwidth efficient because you only send the necessary data, but I decided to go with unions anyway to avoid two calls to recv and for a cleaner implementation.
So I implemented that and now the engine make full use of those. Nothing changed apparently, but now I can very easily add other types of packages, like a RequestTime one which will enable me to sync the time of network and clients.