Network code update
Network code update
Sorry I haven't been on the forum for a while, it's just that I didn't have anything good to say! However I have made some progress and there's some questions to answer and some questions to ask.
I wrote some classes to do all the network stuff. The important one is Network which is supposed to be used by the main program to do high level controlling of the network (hostgame, disconnect, connect to game, get game list, get incoming events for processing). The internal implementation of this uses a bunch of other classes including a Socket class. Socket is pretty much finished and totally works on ipv6/ipv4 without anyone even caring about it. Of course, you can restrict connections to one or the other.
The current plan that its going toward is there are four types of packets
chat (long strings with from whom and to whom info)
sync (unit number, timestamp, some floats)
order (unit number, timestamp, order, some floats)
event (miscellaneous messages like new players, team joinings etc)
like Cire said, the event notifications can be 'tokenized' so events are pretty small packets. sync and order might be relatively long if they
contain many floats, and chat is only long if the message is long.
My idea is that chat, event, and orders go over the tcp connection and syncs go over udp without reliability. If a unit doesnt sync due to lost packet, it will sync later. No problem. Orders are critical to the game and must be received exactly once. Instead of making a scheme to do this over udp, why not just use tcp? A single sync should be short enough to fit in one udp packet, ie, no fragmentation. Am i right?
Zuzuf asked about data compression. chat messages might be reduced a few bytes with a huffman code. But since chat messages wont happen very often I don't think its worth it. Events are likely to be a couple bytes big. The packets that contain floating point numbers are going to be numerous and often. The best way to reduce this traffic is to decide which floats dont require 32 bits of precision (rotation?), since the data we will be using in ta3d will have sufficiently random floating point representation its unlikely that we can consistently save a singnificant number of bytes (out of perhaps 9 floats * 4 bytes = 36 bytes) with some compression algorithm.
Which brings me to my next topic, what exactly are we sending? For a sync we need the units position, velocity and orientation (its behavior isn't critical as there will be orders packetes). Seems like position is just two floats, because its Z is either the same as the ground or at the standard aircraft flight level. Orientation for ground units is like one float right? Aircraft? Velocity for ground units is two floats since you can determine missing information from the ground's gradient. Aircraft?
For unit orders, it seems that we require floats at least sometimes. A move/build order takes two floats: the destination to move/build. Firing takes either two floats or an integer which is the unit to shoot at.
Which brings me to the next topic, shooting individual bullets seems to be slightly random and also could be very high frequency. We shouldn't send an order for each bullet fired, just the attack order. This helps with the very high frequency. We can avoid sending random bullet velocities by making sure that all players have syncronized random number generators. But I can image this failing when 1 more bullet is fired on one machine than another and everything goes wrong....
Which brings up the topic of catastrophic game split. Since no one is authoritative about the state of the game, we might run into a situation where a unit is dead in one game and still alive in another. (due to latency player1 fires and kills player2's unit before player2 knows it and player2 fires 'illegally' possibly killing a third unit who should not have died). Two solutions to this problem i can think of are: 1 periodically check for this kind of inconsistency and kill the 'ghost' unit on all games even if he 'should have lived' or 2 keep some kind of semi authoritative game state on the host computer and refer to him for the 'true' state of the unit's health, living/death status. Number 2 seems to be the more complicated.
Ideas?
And now a question. It seems reasonable to try and reduce network traffic by not sending sync or orders about units behind the fog of war. However the game might have fog of war off, so we sometimes must send all the information about everything to everyone. Perhaps we just shouldn't worry about such an optimization?
About AI, if all players even ai players are represented as network players (even in a single player game) then the same machinery that runs a network game will automatically work for a single player game with AI. Then the AI players are just written to have an idea of what the game looks like and send the orders (and syncs?) to the players game. This also allows us to have a dedicated AI server which uses extreme amounts of CPU to do strategy that would slow you down if it were running on the same machine as you. Any thoughts?
Another thing, does anyone want to look at my code? Should it be uploaded to the CVS? There is definitely some stuff to change regarding size of types, windows compatability, etc. Since I am working on it alone I feel like I might be doing something totally wrong.
I wrote some classes to do all the network stuff. The important one is Network which is supposed to be used by the main program to do high level controlling of the network (hostgame, disconnect, connect to game, get game list, get incoming events for processing). The internal implementation of this uses a bunch of other classes including a Socket class. Socket is pretty much finished and totally works on ipv6/ipv4 without anyone even caring about it. Of course, you can restrict connections to one or the other.
The current plan that its going toward is there are four types of packets
chat (long strings with from whom and to whom info)
sync (unit number, timestamp, some floats)
order (unit number, timestamp, order, some floats)
event (miscellaneous messages like new players, team joinings etc)
like Cire said, the event notifications can be 'tokenized' so events are pretty small packets. sync and order might be relatively long if they
contain many floats, and chat is only long if the message is long.
My idea is that chat, event, and orders go over the tcp connection and syncs go over udp without reliability. If a unit doesnt sync due to lost packet, it will sync later. No problem. Orders are critical to the game and must be received exactly once. Instead of making a scheme to do this over udp, why not just use tcp? A single sync should be short enough to fit in one udp packet, ie, no fragmentation. Am i right?
Zuzuf asked about data compression. chat messages might be reduced a few bytes with a huffman code. But since chat messages wont happen very often I don't think its worth it. Events are likely to be a couple bytes big. The packets that contain floating point numbers are going to be numerous and often. The best way to reduce this traffic is to decide which floats dont require 32 bits of precision (rotation?), since the data we will be using in ta3d will have sufficiently random floating point representation its unlikely that we can consistently save a singnificant number of bytes (out of perhaps 9 floats * 4 bytes = 36 bytes) with some compression algorithm.
Which brings me to my next topic, what exactly are we sending? For a sync we need the units position, velocity and orientation (its behavior isn't critical as there will be orders packetes). Seems like position is just two floats, because its Z is either the same as the ground or at the standard aircraft flight level. Orientation for ground units is like one float right? Aircraft? Velocity for ground units is two floats since you can determine missing information from the ground's gradient. Aircraft?
For unit orders, it seems that we require floats at least sometimes. A move/build order takes two floats: the destination to move/build. Firing takes either two floats or an integer which is the unit to shoot at.
Which brings me to the next topic, shooting individual bullets seems to be slightly random and also could be very high frequency. We shouldn't send an order for each bullet fired, just the attack order. This helps with the very high frequency. We can avoid sending random bullet velocities by making sure that all players have syncronized random number generators. But I can image this failing when 1 more bullet is fired on one machine than another and everything goes wrong....
Which brings up the topic of catastrophic game split. Since no one is authoritative about the state of the game, we might run into a situation where a unit is dead in one game and still alive in another. (due to latency player1 fires and kills player2's unit before player2 knows it and player2 fires 'illegally' possibly killing a third unit who should not have died). Two solutions to this problem i can think of are: 1 periodically check for this kind of inconsistency and kill the 'ghost' unit on all games even if he 'should have lived' or 2 keep some kind of semi authoritative game state on the host computer and refer to him for the 'true' state of the unit's health, living/death status. Number 2 seems to be the more complicated.
Ideas?
And now a question. It seems reasonable to try and reduce network traffic by not sending sync or orders about units behind the fog of war. However the game might have fog of war off, so we sometimes must send all the information about everything to everyone. Perhaps we just shouldn't worry about such an optimization?
About AI, if all players even ai players are represented as network players (even in a single player game) then the same machinery that runs a network game will automatically work for a single player game with AI. Then the AI players are just written to have an idea of what the game looks like and send the orders (and syncs?) to the players game. This also allows us to have a dedicated AI server which uses extreme amounts of CPU to do strategy that would slow you down if it were running on the same machine as you. Any thoughts?
Another thing, does anyone want to look at my code? Should it be uploaded to the CVS? There is definitely some stuff to change regarding size of types, windows compatability, etc. Since I am working on it alone I feel like I might be doing something totally wrong.
- zuzuf
- Administrateur - Site Admin
- Posts: 3281
- Joined: Mon Oct 30, 2006 8:49 pm
- Location: Toulouse, France
- Contact:
for position, I agree, 2 floats should be enough
for speed, 2 floats too (in fact speed is 2D for planes too because the Y coordinate is managed separately.
orientation is one float but an unsigned short int will be enough
the random number generator is heavily used by particles effects like explosions and above all huge explosions (atomic weapons) which produce a lots of particles. It's easy to synchronize the random number generator (just synchronize the position in the random array) but you will have to do it often because lots of things use it. Worse, we will need to synchronize weapons trajectories (what if a commander avoid a nuclear rocket on a computer and not on the others ??)
I prefer your first solution, if you can check all the units every 10 sec., that should be enough.
Your idea is interesting. We can avoid sending data which won't be displayed, but not everything hidden by the fog of war because, a unit can shoot something hidden!!
As far as AI is concerned, AI players are local players, and I would prefer to synchronize all the local players in one pass (not one by local player), I think this would prevent wasting some bandwidth and the engine can very well run in dedicated mode: you just have to omit the local human player.
I think you should put your code on CVS, this way everyone can test it, report bugs or comments.
for speed, 2 floats too (in fact speed is 2D for planes too because the Y coordinate is managed separately.
orientation is one float but an unsigned short int will be enough
the random number generator is heavily used by particles effects like explosions and above all huge explosions (atomic weapons) which produce a lots of particles. It's easy to synchronize the random number generator (just synchronize the position in the random array) but you will have to do it often because lots of things use it. Worse, we will need to synchronize weapons trajectories (what if a commander avoid a nuclear rocket on a computer and not on the others ??)
I prefer your first solution, if you can check all the units every 10 sec., that should be enough.
Your idea is interesting. We can avoid sending data which won't be displayed, but not everything hidden by the fog of war because, a unit can shoot something hidden!!
As far as AI is concerned, AI players are local players, and I would prefer to synchronize all the local players in one pass (not one by local player), I think this would prevent wasting some bandwidth and the engine can very well run in dedicated mode: you just have to omit the local human player.
I think you should put your code on CVS, this way everyone can test it, report bugs or comments.
=>;-D Penguin Powered
Ok I have never seriously used CVS before, I know it can get annoying to rename files. Perhaps we should agree on what the names of the files should be before i upload. After that, what do I type to make new modules?
proposed names for new modules
Socket.cpp
Socket.h
TA3DSock.cpp
TA3DSock.h
SuperQueue.cpp
SuperQueue.h
Thread.h
Network.cpp
Network.h
and you only need to include Network.h in the main game.
By the way, I am having some trouble on devC++ for windows. It seems that the winsock include files that come with it are not as full featured as described at MSDN. In particular the new sockets api isn't totally complete. After some fiddling with those headers I got an ipv6 prog to compile and run on windows, so I know the support is out there. Hopefully Cire, who presumably has MS visual studio, will be able to test my sockets code on windows.
proposed names for new modules
Socket.cpp
Socket.h
TA3DSock.cpp
TA3DSock.h
SuperQueue.cpp
SuperQueue.h
Thread.h
Network.cpp
Network.h
and you only need to include Network.h in the main game.
By the way, I am having some trouble on devC++ for windows. It seems that the winsock include files that come with it are not as full featured as described at MSDN. In particular the new sockets api isn't totally complete. After some fiddling with those headers I got an ipv6 prog to compile and run on windows, so I know the support is out there. Hopefully Cire, who presumably has MS visual studio, will be able to test my sockets code on windows.
- zuzuf
- Administrateur - Site Admin
- Posts: 3281
- Joined: Mon Oct 30, 2006 8:49 pm
- Location: Toulouse, France
- Contact:
These names are OK. Now to make new modules, you must edit the src/Makefile.am, there is the list of files to compile, just add one line per new module (.cpp & .h) and global headers at the end of the list (like matrix.h, vector.h,ta3dbase.h).
Then you need to go to TA3D's root dir and type:
aclocal
autoconf
automake -a -c
in order to refresh things, now when you type make the new modules will build.
to upload this to CVS,
(from a working copy of the CVS repository)
cd to src/ and type:
cvs add Socket.cpp
and do this for each new file
then cd to TA3D's root dir and type:
cvs commit
then type some comments (for example "network modules")
and wait until it's done
Then you need to go to TA3D's root dir and type:
aclocal
autoconf
automake -a -c
in order to refresh things, now when you type make the new modules will build.
to upload this to CVS,
(from a working copy of the CVS repository)
cd to src/ and type:
cvs add Socket.cpp
and do this for each new file
then cd to TA3D's root dir and type:
cvs commit
then type some comments (for example "network modules")
and wait until it's done
=>;-D Penguin Powered
Well only thing I need to say is that NOTHING criticle should go accross TCP, keep in mind TCP requires ACK packet replies, and if not received after a period of time will resend the packet again and again untill ACK is recived, This in many cases can be seconds! A few seconds in a RTS Game is very very very very bad especially if its sending it often, imagine each tick having to wait 2 or 3 or more seconds, when its normally over in 500 ms or something like that.
Hate to say it but keep TCP to chat and events, since they are not criticle to gameplay. Now to me each player will have a playerlist/ai player list ect in a list, eahc player will have a list of units and what not local to each player. If I change the orders of some of my units, flag it as dirty, which will go out over next tick to be updated to other players.
Get rid of most randomization that are 'criticle', and come up with algorthisims that are depandant so when they occur they will be the same on all platforms.
Now zuff brought up a key point, and that is that FPO's preformed on my cpu may not work out exactly the same on another cpu, so work here needs to be worked out.
I think if u reserved FPO's to some scale, like say 6 decimal places, you might be all right, I would also check out other big engines such as quake for ways in which they handle FPO's and what not from one to another cpu, ect.. You could also at connect time or for that matter 'at first install time or run time', run a routine that would say generate 50 floats from a set of fixed FPO calculations and when users connect to each other, attemtp to match these to each other, if thier are issues this is when your gona find out. It won't do you much good to see that thire are issues but at the very min i'tll help in debugging, and show you how much scale you can play with, for all you know cpus could be out at about 6 decimal places, or 12 its hard to say.
These are just a few suggestions, hope they help.
++Cire.
Hate to say it but keep TCP to chat and events, since they are not criticle to gameplay. Now to me each player will have a playerlist/ai player list ect in a list, eahc player will have a list of units and what not local to each player. If I change the orders of some of my units, flag it as dirty, which will go out over next tick to be updated to other players.
Get rid of most randomization that are 'criticle', and come up with algorthisims that are depandant so when they occur they will be the same on all platforms.
Now zuff brought up a key point, and that is that FPO's preformed on my cpu may not work out exactly the same on another cpu, so work here needs to be worked out.
I think if u reserved FPO's to some scale, like say 6 decimal places, you might be all right, I would also check out other big engines such as quake for ways in which they handle FPO's and what not from one to another cpu, ect.. You could also at connect time or for that matter 'at first install time or run time', run a routine that would say generate 50 floats from a set of fixed FPO calculations and when users connect to each other, attemtp to match these to each other, if thier are issues this is when your gona find out. It won't do you much good to see that thire are issues but at the very min i'tll help in debugging, and show you how much scale you can play with, for all you know cpus could be out at about 6 decimal places, or 12 its hard to say.
These are just a few suggestions, hope they help.
++Cire.
single precision floats have 23 bits of significand. That translates to under 7 significant figures in decimal representation. That is we can only count on numbers like 1.234567 and 123.4567. This is how computers should work on floats as long as they have ieee754 standard. As far as I know thats everyone with an x86 based machine. On what computers are you worried about non standard floating point?
So I am trying to compile my code as part of the main project that I downloaded latest CVS. The problem I am having is what to call my platform dependent #define directive and how to tell automake and friends that we need to define that. Currently I'm using the words #define TA3D_PLATFORM_LINUX to denote linux-only implementation. How do I get a -DTA3D_PLATFORM_LINUX into the build process? I can add this to the makefile manually, but I'd like to know the right way.
After that works I may want to go ahead and check in the new modules, but do I not need a special password and login instructions?
After that works I may want to go ahead and check in the new modules, but do I not need a special password and login instructions?
For now just add #define TA3D_PLATFORM_LINUX to the top of your code, in upcoming sorce release it should be defined in a file called TA3D_Platform.h
Ideally when a user is building a version for himself, they will edit this file and define or undefine whatever platform they want, so you do not need to make any adjustements
To test your builds for now as I said just define it at the top of your code, or better yet create a file named TA3D_Platform.h and place it in that, that way you will not require any changes.
++Cire.
Ideally when a user is building a version for himself, they will edit this file and define or undefine whatever platform they want, so you do not need to make any adjustements
To test your builds for now as I said just define it at the top of your code, or better yet create a file named TA3D_Platform.h and place it in that, that way you will not require any changes.
++Cire.
Ok I added the network modules. In the current state it probably won't compile on linux until the platform specific directives are fixed (they still have #ifdef TA3D_PLATFORM_XXX). After that is changed (to #ifdef ALLEGRO_XXX ?), it probably won't compile on windows due to some missing functionality in the sockets (the errors like E_WOULDBLOCK). Since I haven't built it on windows successfully I haven't tested the win32 thread stuff either. My problem is that the winsock2 header files that come with DevC++ don't have complete support for gai_strerror(). This function is in the documentation on MSDN so it should work on visual c++. The Socket class reports its own errors with printf, but that can easily be changed to use the rest of the projects error handling. Also the integer types in ta3dsock are the ones in sys/types.h on linux, that needs to use the types from the rest of the project.
Who is online
Users browsing this forum: No registered users and 45 guests