timesync.c

Go to the documentation of this file.
00001 
00025 #include "vector.h"
00026 #include "timesync.h"
00027 #include "iothread.h"
00028 #include "message.h"
00029 #include "vector.h"
00030 #include "net.h"
00031 #include <assert.h>
00032 #include <math.h>
00033 #ifndef NDEBUG
00034 #include <stdio.h>
00035 #endif
00036 #ifndef WIN32
00037 #include <sys/time.h>
00038 #include <sys/types.h>
00039 #include <unistd.h>
00040 #endif
00041 #include <SDL.h>
00042 #include <SDL_thread.h>
00043 
00044 Sint32 timeDelta;
00045 int timeSyncState = TIMESYNC_IDLE;
00046 TimeSample timeSyncResults;
00047 
00052 static SDL_Thread *thread = NULL;
00053 
00057 static int tsyncSock;
00058 
00065 #define REQ_SAMPLES  16
00066 
00072 #define INC_SAMPLES   4
00073 
00081 #define MAX_SAMPLES  32
00082 
00090 static void ComputeAverages(Vector *samples) {
00091     double delta = 0, latency = 0;
00092     TimeSample *sample = (TimeSample*)samples->array;
00093     unsigned int loop;
00094     if (samples->items) {
00095         // go through all the samples
00096         for (loop = 0; loop < samples->items; loop++, sample++) {
00097             // accumulate the times
00098             delta += sample->delta;
00099             latency += sample->latency;
00100         }
00101         // divide for an average
00102         timeSyncResults.delta = delta / (double)samples->items;
00103         timeSyncResults.latency = latency / (double)samples->items;
00104     }
00105     else {
00106         timeSyncResults.delta = timeSyncResults.latency = 0.0;
00107     }
00108 }
00109 
00118 static double ComputeStdDeviation(Vector *samples) {
00119     double delta = 0;
00120     TimeSample *sample = (TimeSample*)samples->array;
00121     unsigned int loop;
00122     // go through all the samples
00123     for (loop = 0; loop < samples->items; loop++, sample++) {
00124         // accumulate the squares
00125         delta = (timeSyncResults.delta - sample->delta) *
00126         (timeSyncResults.delta - sample->delta);
00127     }
00128     // finsh the result
00129     return sqrt(delta / (double)(samples->items - 1));
00130 }
00131 
00144 static Bool GetSamples(Vector *samples, SocketAddr *dest, int sock,
00145 int reqSamples) {
00146     fd_set fds;
00147     SocketAddr origin;
00148     TimeSample *lastSample;
00149     int loop;
00150     Uint32 sentTime, recvTime;
00151     Uint8  buff[9];
00152     origin.length = sizeof(struct sockaddr_in6);
00153     FD_ZERO(&fds);
00154     // compute the number of samples still needed
00155     loop = (reqSamples << 1) - samples->items;
00156     while ((loop > 0) && !IsCancelationRequested()) {
00157         int gotTime;
00158         // wait up to a half second for a response
00159         struct timeval tv = { 0, 500000 };
00160         // first, wait a little while between time sync datagrams
00161         SDL_Delay(50);
00162         // get the current time
00163         sentTime = SDL_GetTicks();
00164         // build the datagram
00165         buff[0] = localPlayer;
00166         MSG_WRITE_32C(buff, 1, sentTime);
00167         // send the timesync datagram
00168         if (sendto(sock, buff, 5, 0, &(dest->addr), dest->length) != 5) {
00169             // failed to send
00170             timeSyncState = TIMESYNC_ERROR;
00171             return FALSE;
00172         }
00173         GetSamples_select:
00174         FD_SET(sock, &fds);
00175         // wait for the response
00176         if (select(sock + 1, &fds, NULL, &fds, &tv) <= 0) {
00177             // got nothing; try again
00178             continue;
00179         }
00180         // get the response and check for error
00181         if ((recvfrom(sock, buff, 9, 0, &origin.addr,
00182         &origin.length) != 9) || !EqualAddresses(&origin, dest)) {
00183             // try again
00184             goto GetSamples_select;
00185         }
00186         // capture the current time
00187         recvTime = SDL_GetTicks();
00188         // see if the response has the same client time as the last one
00189         // sent to the server
00190         MSG_READ_32C(buff, 1, gotTime);
00191         if ((localPlayer != buff[0]) || (gotTime != sentTime)) {
00192             // try again
00193             goto GetSamples_select;
00194         }
00195         // add the sample
00196         MSG_READ_32C(buff, 5, gotTime);
00197         lastSample = VectorAdd(samples);
00198         lastSample->delta = (double)(gotTime - recvTime) +
00199         (lastSample->latency = (double)(recvTime - sentTime) / 2.0);
00200         // one sample done
00201         loop--;
00202     }
00203     // success
00204     return TRUE;
00205 }
00206 
00215 static void RemoveBadSamples(Vector *samples) {
00216     double stdDev, upper, lower;
00217     TimeSample *sample = (TimeSample*)samples->array;
00218     unsigned int loop = 0;
00219     // find the averages
00220     ComputeAverages(samples);
00221     // find the standard deviatiom
00222     stdDev = ComputeStdDeviation(samples);
00223     // set the lower bound
00224     lower = timeSyncResults.delta - stdDev - 1.0;
00225     // set the upper bound
00226     upper = timeSyncResults.delta + stdDev + 1.0;
00227     // loop through the samples
00228     while (loop < samples->items) {
00229         // check for a bad -- out of bounds -- sample
00230         if ((sample->delta > upper) || (sample->delta < lower)) {
00231             // remove the sample
00232             VectorRemove(samples, loop);
00233         }
00234         else {
00235             // advamce to the next sample
00236             sample++;
00237             loop++;
00238         }
00239     }
00240 }
00241 
00242 extern Uint32 frameTime;
00243 
00251 static void ClientSync(void *ignore) {
00252     Vector samples;
00253     SocketAddr addr;
00254     int sock, time;
00255     unsigned int required;
00256     
00257     // initialize the vector that will contain time samples
00258     samples.destructOp = NULL;
00259     samples.itemSize = sizeof(TimeSample);
00260     if (!VectorInit(&samples, samples.minSize = 32)) {
00261         // not enough memory; this game isn't going to work
00262         timeSyncState = TIMESYNC_ERROR;
00263         return;
00264     }
00265     // get the socket address to use
00266     assert((outgoingAddr.addr.sa_family == AF_INET) ||
00267     (outgoingAddr.addr.sa_family == AF_INET6));
00268     // IPv4?
00269     if ((addr.addr.sa_family = outgoingAddr.addr.sa_family) == AF_INET) {
00270         // set the address
00271         addr.ipv4.sin_addr.s_addr = htonl(INADDR_ANY);
00272         // set the port
00273         addr.ipv4.sin_port = htons(PORT_CTSYNC);
00274     }
00275     // IPv6?
00276     else {
00278         // set the port
00279         addr.ipv6.sin6_port = htons(PORT_CTSYNC);
00280     }
00281     // get a socket to communicate with the server
00282     if ((sock = UnicastSocket(&addr, TRUE)) == -1) {
00283         // couldn't communicate for time sync
00284         timeSyncState = TIMESYNC_ERROR;
00285         goto ClientSync_end;
00286     }
00287     // set address to be the destination (server)
00288     addr = outgoingAddr;
00289     if (addr.addr.sa_family == AF_INET) {
00290         // change the port
00291         addr.ipv4.sin_port = htons(PORT_STSYNC);
00292     }
00293     // IPv6?
00294     else {
00295         // change the port
00296         addr.ipv6.sin6_port = htons(PORT_STSYNC);
00297     }
00298     // set the required number of samples
00299     required = REQ_SAMPLES;
00300     // loop for samples
00301     do {
00302         // get some samples
00303         if (!GetSamples(&samples, &addr, sock, required)) {
00304             // failed
00305             timeSyncState = TIMESYNC_ERROR;
00306             goto ClientSync_end;
00307         }
00308         // if there can be more attempts at gathering samples . . .
00309         if (required < MAX_SAMPLES) {
00310             // remove samples that appear to be poor
00311             RemoveBadSamples(&samples);
00312         }
00313         // increase the minimum number of samples needed
00314         required += INC_SAMPLES;
00315     } while ((samples.items < required) && (required <= MAX_SAMPLES)
00316     && !IsCancelationRequested());
00317     // compute an average
00318     ComputeAverages(&samples);
00319     // set the integer used for common game time computations
00320     timeDelta = (Sint32)timeSyncResults.delta;
00321     // change the next frame target time
00322     time = SDL_GetTicks() + timeDelta;
00323     frameTime = time - (time % FRAME_DURATION) + FRAME_DURATION;
00324     // check for too few samples
00325     if (samples.items < required) {
00326         // flag poor results
00327         timeSyncState = TIMESYNC_POOR;
00328     }
00329     else {
00330         // good results
00331         timeSyncState = TIMESYNC_FINISHED;
00332     }
00333     ClientSync_end:
00334     #if !defined(NDEBUG) && !defined(GETTEXT)
00335     {
00336         int error;
00337         printf("Time synchronization ended ");
00338         switch (timeSyncState) {
00339             case TIMESYNC_FINISHED:
00340                 printf("successfully.");
00341                 break;
00342             case TIMESYNC_POOR:
00343                 printf("with poor quality results.");
00344                 break;
00345             case TIMESYNC_ERROR:
00346                 printf("on an error.");
00347                 if ((error = GetNetErrorVal()) != 0) {
00348                     #ifdef USE_GETTEXT
00349                     printf("\n\tNetwork error: %ls\n", GetNetErrorString(error));
00350                     #else
00351                     printf("\n\tNetwork error: %s\n", GetNetErrorString(error));
00352                     #endif
00353                 }
00354                 return;
00355             default:
00356                 printf("with undefined state 0x%X.\n", timeSyncState);
00357                 return;
00358         }
00359         printf("\n\tsamples = %d\n\tdelta = %lfms\n\tlatency = %lfms\n",
00360         samples.items, timeSyncResults.delta, timeSyncResults.latency);
00361         #ifdef WIN32
00362         fflush(stdout);
00363         #endif
00364     }
00365     #endif
00366     // close the socket
00367     CloseSocket(sock);
00368     // destroy the samples
00369     VectorDestroy(&samples);
00370 }
00371 
00372 void StartClientTimeSync() {
00373     timeSyncState = TIMESYNC_INPROG;
00374     QueueOperation(ClientSync, NULL);
00375 }
00376 
00382 static int ServerTimeSyncThread(void *ignore) {
00383     SocketAddr origin;
00384     int error = 0;
00385     Uint32 now;
00386     Uint8 buff[9];
00387     // loop to receive requests
00388     do {
00389         // reset the length of the origin struct
00390         origin.length = sizeof(struct sockaddr_in6);
00391         // read in 5 bytes
00392         if (recvfrom(tsyncSock, buff, 5, 0, &origin.addr, &origin.length) == 5) {
00393             // validate the client
00394             if (ValidateClient(&origin, buff[0])) {
00395                 // add the server's time to the message
00396                 now = SDL_GetTicks();
00397                 MSG_WRITE_32C(buff, 5, now);
00398                 // adjust the port for IPv4
00399                 if (origin.addr.sa_family == AF_INET) {
00400                     // change the port
00401                     origin.ipv4.sin_port = htons(PORT_CTSYNC);
00402                 }
00403                 // IPv6?
00404                 else {
00405                     // change the port
00406                     origin.ipv6.sin6_port = htons(PORT_CTSYNC);
00407                 }
00408                 // send it back
00409                 sendto(tsyncSock, buff, 9, 0, &origin.addr, origin.length);
00410             }
00411             // clear the error
00412             error = 0;
00413         }
00414         else {
00415             // grab the error
00416             error = GetNetErrorVal();
00417         }
00418     } while ((tsyncSock != -1) && ((error == 0) || (error == ECONNREFUSED)));
00419     #ifndef NDEBUG
00420     if ((tsyncSock != -1) && error) {
00421         #ifndef WIN32
00422         #ifdef USE_GETTEXT
00423         fwprintf(stderr, L"Server time synchronization ended on error: %ls\n",
00424         GetNetErrorString(error));
00425         #else
00426         fprintf(stderr, "Server time synchronization ended on error: %s\n",
00427         GetNetErrorString(error));
00428         #endif
00429         #endif
00430     }
00431     #endif
00432     return 0;
00433 }
00434 
00435 Bool StartServerTimeSync() {
00436     SocketAddr addr;
00437     // setup an address
00439     addr.ipv4.sin_family = AF_INET;
00440     addr.ipv4.sin_port = htons(PORT_STSYNC);
00441     addr.ipv4.sin_addr.s_addr = htonl(INADDR_ANY);
00442     // open a socket to receive time sync requests
00443     if ((tsyncSock = UnicastSocket(&addr, TRUE)) == -1) {
00444         // failed
00445         return FALSE;
00446     }
00447     // start the thread
00448     thread = SDL_CreateThread(ServerTimeSyncThread, NULL);
00449     return TRUE;
00450 }
00451 
00452 void StopServerTimeSync() {
00453     CloseSocket(tsyncSock);
00454     timeSyncState = TIMESYNC_IDLE;
00455 }
00456 

Generated on Mon May 28 04:41:39 2007 for Retro Tank Super Attack by  doxygen 1.5.2