This entry is about networking though, and since this is not too visual, I will just add some intermittent images, although they have nothing to do with the entry. The images are somewhat older too, but then they are just designed to keep things interesting. I will soon write another entry with more recent visuals.
Today, I finished the infrastructure necessary to propagate keyboard and mouse events to the server and all the clients. Before, data was sent after every frame, but now I am using a sliding time window where updates are sent every four frames or later depending on the network traffic. This means that a client who controls an avatar accumulates the keyboard and mouse events over a period of time before they are sent to server. The server then passes on this input immediately to all the clients including the one from which it was received in the first place. At the same time, the server uses the input to control its own representation of the character over the given time period. Once a client has received the input it in turn uses the data to control its own version of the character. For example, once you hit 'w', first the character on the server begins to walk and then about 100ms later then ones on the clients begin to move. This is working quite well and if no packets get lots, all versions of the avatars are exactly at the same location about 200ms after the controlling client has released the 'w' key. Just in case I also send the final position of the avatar a few 100 ms after the 'w' key is released. All collisions with the static environment are resolved locally on the clients and server. Thus, even if you are walking into a wall, in no instance does the character penetrate the wall in any of the computers running the simulation.
There are some problems with this approach though. First off all, it takes a noticeable amount of time before your character begins to walk when you hit a key because the keyboard event first has to go to the server and then come back. This effect is even more noticeable when you are trying to turn using the mouse. In my opinion this delay is not acceptable and I have never noticed it during my BF1942 days. That game would have been unplayable with those kinds of delays. So, the solution is that the character on the controlling client begins to walk right away without waiting for the IO state from the server. This is really not a problem and will work well in a purely static environment where there are no other dynamic objects. Since a client does its own collision detection with the static environment, its avatar will at the end still come to rest in the same place as on the server and the other clients. The problem only begins once you introduce other dynamic objects. Right now, the clients don't resolve collisions among dynamic objects because that didn't work too well before.
So, imagine this: You are standing right in front of a box that is moveable and now you are hitting 'w'. If the client has to wait on keyboard input from the server it will at the same time get an update on the position and velocities of the box, because the collision will have occurred on the server. In this case, the box will move as you hitting it. But there are still some problems, because the server sends you updates on your characters position as well, since you are a dynamic object and you have collided with another dynamic object. The effect is that the client will have to update the characters position according to the keyboard and subsequent animation sequence and, at the same, incorporate the correction due to collision. This could, actually it will, cause the character to jitter. So, I believe the solution must be that the client also resolves collisions among dynamic objects. In this case, the character on the controlling clients side can be begin to walk right away without waiting for the IO state from the server and it will correctly collide with box and move it out of the way. So far so good, but the problem now is the time delay. Image this box is moving and the character on the client is colliding with it, but at the time the server moves the character, it has missed the box and the collision thus has never occurred on the server. Now things are out of sync, that's why I never wanted to allow dynamic objects on the client to collide in the first place. My original idea was to detect dynamic collision on the server only and then send a new position and velocities to the clients. The clients then use that information to plot the path of, in this case the box until a new collision occurs on the server. Remember, collisions with the static environment are always resolved on the client as well. So, there will be no new updates for the box until another collision with a dynamic object occurs.
So, if the client resolves collision and the server resolves collision things can get out of sync, especially if there are other avatars walking around. The solution, I think, lies in how to update the clients. The server will still have to send information about an objects position and velocities but this information has to be used differently. Now, I am simply replacing the position and velocities with those from the server. But this can make things even worse. Imagine the server sends an update for an object which causes it to deeply intersect with another dynamic object. The clients' physics engine will then compute correction forces that make the objects explode, even though this has never occurred on the server. An update for the other object that caused the explosion might still be on the network, but by the time it arrives it will too late. The solution here, I think, is not to simply replace the clients' information with that from the server but rather to apply linear and angular correction forces that will ensure that the local object arrives at the same position and orientation as dictated by the server in a reasonable amount of time. This way, at least objects won't jitter around and it's possible to start moving the local avatar, shuttle or vehicle right away. By using correction forces, it may also not be necessary for the server to send location and orientation as frequently, because the forces are designed to reach the final position. Things can still get out of sync but over time they will gradually converge on the same state. Again, the idea is that this will occur smoothly without jittering. Tomorrow I will begin working on this problem...
I have also realized that restrictions will have to be placed on the magnitude of the velocities that objects can achieve. Imagine a ball rolling around a corner on the server, but on the client it didn't quite make it. Now, the server will send the client correction information that will lead it right through the wall. But since the client does collision detection, the ball will never make it to the desired position. This is a big problem and I can think of several others along that line. By limiting the magnitude of the velocities according to the width of the thinnest wall this may be avoidable. Geist3D should probably also do temporal collision detection, where collisions aren't just resolved at the location where the objects are at a point in time, but along the swept volume between the last position and the current position. I am pretty sure this is standard in most of the games now and, it is probably not too difficult to implement; at least a rudimentary version. With this type of collision detection the above scenario can be detected and corrected.
In any case, the dynamic objects in a game are probably limited to avatars, shuttles, vehicles and maybe some fancy projectiles, so I am going a bit overboard with 40 balls being pushed around by an avatar in close quarters. I don't see much value in objects like balls and spheres unless they can add to the game experience.
Well, this was a pretty big entry. Only took a couple of beers to write. I hope it brings across some of the problems that networking introduces to the story.