gameconfig.c

Go to the documentation of this file.
00001 
00025 #ifdef WIN32
00026 #define OpenFile  W32OpenFile
00027 #endif
00028 #include "gameconfig.h"
00029 #include "net.h"
00030 #include "playernet.h"
00031 #include "obstacle.h"
00032 #include "vector.h"
00033 #include "notice.h"
00034 #include <assert.h>
00035 #include <ctype.h>
00036 #include <stdarg.h>
00037 #include <stdio.h>
00038 #include <stdlib.h>
00039 #include <string.h>
00040 
00041 #ifdef WIN32
00042 #undef OpenFile
00043 #endif
00044 
00045 ClientData *configListStart = NULL, *configListEnd = NULL;
00046 
00047 GameOptions gameOpts = { 0 };
00048 
00049 SDL_Rect *spawnAreas[TEAM_MAX];
00050 SDL_Rect *goalAreas[TEAM_MAX];
00051 
00052 BattleFieldError bfErr;
00053 
00057 #define MAX_TOKEN_LEN  8
00058 
00063 static Vector cfgMsgBuffVec = {
00064     NULL,                              // Vector::lessOp
00065     BufferVecDestr,                    // Vector::destructOp
00066     NULL,                              // Vector::array
00067     sizeof(NetBuffer*),                // Vector::itemSize
00068     0,                                 // Vector::items
00069     0,                                 // Vector::size
00070     32                                 // Vector::minSize
00071 };
00072 
00077 static Uint32 *optionMsgFlags = NULL; 
00078 
00082 static Uint32 lastTransmit = 0;
00083 
00087 static Uint16 nextItem  = 0;
00088 
00093 static Uint16 remainOptionMsgs = 0;
00094 
00101 static DESTMAPTYPE destMap[DESTMAPLEN] = { 0 };
00102 
00108 static const Vector configInitVec = {
00109     NULL,                      // Vector::lessOp
00110     NULL,                      // Vector::destructOp
00111     NULL,                      // Vector::array
00112     sizeof(SDL_Rect),          // Vector::itemSize
00113     0,                         // Vector::items
00114     0,                         // Vector::size
00115     64                         // Vector::minSize
00116 };
00117 
00123 #define FILENAMELEN  (64 - sizeof(FILE*) - sizeof(int) - 4)
00124 
00132 struct ConfigFileStackFrame_t {
00136     FILE *file;
00140     int line;
00145     Sint16 xoffset;
00150     Sint16 yoffset;
00154     Uint16 width;
00158     Uint16 height;
00162     char filename[FILENAMELEN];
00163     union {
00167         Uint8 flags;
00168         struct {
00172             Bool  flipHoriz : 1;
00176             Bool  flipVert  : 1;
00180             Bool  transpose : 1;
00181         };
00182     };
00183 };
00184 
00185 typedef struct ConfigFileStackFrame_t  ConfigFileStackFrame;
00186 
00195 static void ErrorOut(ConfigFileStackFrame *frame, const rtsa_char *format, ...) {
00196     va_list args;
00197     rtsa_char errmsg[BFLDERRLEN];
00198     // get the variable argument list
00199     va_start(args, format);
00200     // produce the error message minus the header
00201     vprnstr(errmsg, BFLDERRLEN, format, args);
00202     // finished with the variable argument list
00203     va_end(args);
00204     // check for a frame
00205     if (frame) {
00206         // grab the error location
00207         bfErr.line = frame->line;
00208         strncpy(bfErr.file, frame->filename, 80);
00209         // produce the final error sting including the header
00210         prnstr(bfErr.msg, BFLDERRLEN, strings[STRING_ERR_READ], frame->filename,
00211         frame->line, errmsg);
00212     }
00213     else {
00214         // set the error location to indicate the lack of a location
00215         bfErr.line = -1;
00216         bfErr.file[0] = 0;
00217         bfErr.msg[0] = 0;
00218         // produce the final error sting including the header
00219         prnstr(bfErr.msg, BFLDERRLEN, strings[STRING_ERR_READ], errmsg);
00220     }       
00221     // output to stderr the message
00222     prnfile(stderr, bfErr.msg);
00223     // newline
00224     witechar('\n', stderr);
00225     // set the error flag
00226     gameOpts.flags |= OPT_ERROR;
00227 }
00228 
00247 static Bool OpenFile(ConfigFileStackFrame *frame) {
00248     // No backslashes!  It prevents cross platform compatibility.
00249     if (strchr(frame->filename, '\\')) {
00250         ErrorOut(frame, strings[STRING_ERR_BACKSLASH], frame->filename);
00251         frame->file = NULL;
00252         return FALSE;
00253     }
00254     // try to open the file in the user's home directory
00255         // try to open the file in the working directory
00256         if ((frame->file = fopen(frame->filename, "rb")) == NULL) {
00257             // try to open the file in the program's directory
00258                 // failed!
00259                 return FALSE;
00260         }
00261     // success!
00262     return TRUE;
00263 }
00264 
00274 static Bool ReadToken(ConfigFileStackFrame *frame, char *buff, int loop) {
00275     // read in the whole token
00276     for (; loop < MAX_TOKEN_LEN; loop++) {
00277         // read the next character
00278         if ((buff[loop] = tolower(fgetc(frame->file))) == EOF) {
00279             // bad token
00280             ErrorOut(frame, strings[STRING_ERR_IO]);
00281             // terminate with error condition
00282             return FALSE;
00283         }
00284         // check for a line break
00285         if (buff[loop] == '\n') frame->line++;
00286         if ((buff[loop] == '\n') || (buff[loop] == '\r')) {
00287             // end of token in bad spot
00288             buff[loop] = 0;
00289             ErrorOut(frame, strings[STRING_ERR_EOL]);
00290             // terminate with error condition
00291             return FALSE;
00292         }
00293         // check for a space
00294         if (isspace(buff[loop])) {
00295             // whole token read
00296             break;
00297         }
00298     }
00299     // assure NULL termination at the end of the token
00300     buff[loop] = 0;
00301     // success!
00302     return TRUE;
00303 }
00304 
00323 static Bool ReadInts(ConfigFileStackFrame *frame, int *array, int flags,
00324 int xoff, int yoff) {
00325     // attempt to read in the parameters
00326     if (((flags & 1) &&
00327     (fscanf(frame->file, "%d %d", &array[0], &array[1]) != 2)) || ((flags & 2)
00328     && (fscanf(frame->file, "%d %d", &array[2], &array[3]) != 2))) {
00329         // did not read in all the parameters
00330         ErrorOut(frame, strings[STRING_ERR_PARAM]);
00331         // terminate with error condition
00332         return FALSE;
00333     }
00334     // when both coordinates & width/height are given, flipping may change the
00335     // data
00336     if (flags == 3) {
00337         // transpose?
00338         if (frame->transpose) {
00339             int x, y;
00340             x = array[0];
00341             y = array[1];
00342             array[0] = y;
00343             array[1] = x;
00344             x = array[2];
00345             y = array[3];
00346             array[2] = y;
00347             array[3] = x;
00348         }
00349         // flips only done after the size is set
00350         if (frame->width) {
00351             // flip hozizontally
00352             if (frame->flipHoriz) {
00353                 array[0] = frame->width - array[0] - array[2];
00354             }
00355             // flip vertically
00356             if (frame->flipVert) {
00357                 array[1] = frame->height - array[1] - array[3];
00358             }
00359         }
00360     }
00361     // if coordinates were read . . .
00362     if (flags & 1) {
00363         int xsize, ysize;
00364         // find the maximum width & height
00365         if (gameOpts.width) {
00366             // use what it already set
00367             xsize = gameOpts.width;
00368             ysize = gameOpts.height;
00369         }
00370         else {
00371             // use the absolute maximums
00372             xsize = ysize = 2048;
00373             // show a warning
00374             /*
00375             printf(
00376             "Battlefield warning: %s, line %i:\n\tSpecified location before size statement\n", \
00377             (frame)->filename, (frame)->line);
00378             */
00379         }
00380         // add in the offsets
00381         array[0] += xoff;
00382         array[1] += yoff;
00383         if ((array[0] < 0) || (array[1] < 0) || (array[0] >= xsize) ||
00384         (array[1] >= ysize)) {
00385             // bad x/y
00386             ErrorOut(frame, strings[STRING_ERR_XYRANGE]);
00387             // terminate with error condition
00388             return FALSE;
00389         }
00390     }
00391     // if width & height were read . . .
00392     if (flags & 2) {
00393         // check parameter ranges
00394         if ((array[2] < 1) || (array[3] < 1) || (array[2] >= 2048) ||
00395         (array[3] >= 2048)) {
00396             // bad width / height
00397             ErrorOut(frame, strings[STRING_ERR_WHRANGE]);
00398             // terminate with error condition
00399             return FALSE;
00400         }
00401     }
00402     // success!
00403     return TRUE;
00404 }
00405 
00414 static Bool ReadAreaParams(ConfigFileStackFrame *frame, Vector *areas) {
00415     SDL_Rect *area;
00416     int vals[4];
00417     char token[MAX_TOKEN_LEN] = { 0 };
00418     // read the owning team
00419     if (!ReadToken(frame, token, 0)) {
00420         return FALSE;
00421     }
00422     // check for red team
00423     if (strcmp(token, "red") == 0) {
00424         // obtain the place to put the spawn area
00425         area = VectorAdd(&areas[TEAM_RED]);
00426     }
00427     // check for blue team
00428     else if (strcmp(token, "blue") == 0) {
00429         // obtain the place to put the spawn area
00430         area = VectorAdd(&areas[TEAM_BLUE]);
00431     }
00432     // bad team?
00433     else {
00434         // invalid team
00435         ErrorOut(frame, strings[STRING_ERR_BADOWN], token);
00436         // terminate with error condition
00437         return FALSE;
00438     }
00439     // no memory?
00440     if (area == NULL) {
00441         // no memory
00442         ErrorOut(frame, strings[STRING_ERR_MEM]);
00443         // terminate with error condition
00444         return FALSE;
00445     }
00446     // read in the area definition
00447     if (!ReadInts(frame, vals, 3, frame->xoffset, frame->yoffset)) {
00448         return FALSE;
00449     }
00450     // set the spawn area's values
00451     area->x = vals[0];  // different datatypes; no
00452     area->y = vals[1];  // better way to do this now
00453     area->w = vals[2];
00454     area->h = vals[3];
00455     // success!
00456     return TRUE;
00457 }
00458 
00469 static Bool CopyAreaData(Vector *input, SDL_Rect **output) {
00470     // attempt to allocate memory
00471     if ((*output = malloc(sizeof(SDL_Rect) * input->items)) == NULL) {
00472         // no more memory
00473         free(obstacles); // already allocated
00474         obstacles = NULL;
00475         ErrorOut(NULL, strings[STRING_ERR_MEM]);
00476         return FALSE;
00477     }
00478     // copy the area data
00479     memcpy(*output, input->array, sizeof(SDL_Rect) * input->items);
00480     return TRUE;
00481 }
00482 
00488 static void StartLoadConfig(const char *fname) {
00489     // signals an error and exits the function cleanly
00490     #define SigErr   goto StartLoadConfig_cleanup
00491 
00492     ConfigFileStackFrame *frame;
00493     Vector stack = configInitVec, blocks = configInitVec;
00494     Vector goals[TEAM_MAX], spawns[TEAM_MAX];
00495     int xoff = 0, yoff = 0;  // offset values
00496     int loop;
00497     // finish the vector initalization
00498     stack.itemSize = sizeof(ConfigFileStackFrame);
00499     spawns[TEAM_RED] = spawns[TEAM_BLUE] = goals[TEAM_RED] = goals[TEAM_BLUE]
00500     = configInitVec;
00501     // this part allocates memory for the vectors -- check for error
00502     if (!VectorInit(&stack, 64) || !VectorInit(&blocks, 64) ||
00503     !VectorInit(&spawns[TEAM_RED], 64) || !VectorInit(&spawns[TEAM_BLUE], 64) ||
00504     !VectorInit(&goals[TEAM_RED], 64) || !VectorInit(&goals[TEAM_BLUE], 64)) {
00505         // failure!
00506         SigErr;
00507     }
00508     // clear out the size of the battle field
00509     gameOpts.width = gameOpts.height = 0;
00510 
00511     // create the initial stack frame
00512     if ((frame = VectorAdd(&stack)) == NULL) {
00513         // no memory
00514         ErrorOut(NULL, strings[STRING_ERR_MEM]);
00515         // exit with error
00516         SigErr;
00517     }
00518     frame->flags = 0;
00519     frame->width = frame->height = 0;
00520     frame->xoffset = frame->yoffset = 0;
00521     frame->line = 1;
00522 
00523     // ----- Read in the configuration from a file -----
00524 
00525     // copy over the file name
00526     strncpy(frame->filename, fname, FILENAMELEN);
00527     // assure null termination (if needed, this is probably going to fail)
00528     frame->filename[FILENAMELEN - 1] = 0;
00529     // open the file and check for error
00530     if (!OpenFile(frame)) {
00531         // show an error
00532         ErrorOut(frame, strings[STRING_ERR_FOPEN], frame->filename);
00533         SigErr;
00534     }
00535 
00536     // loop through the files
00537     do {
00538         char token[MAX_TOKEN_LEN] = { 0 };
00539         // loop while reading the file
00540         while ((token[0] = tolower(fgetc(frame->file))) != EOF) {
00541             int vals[4];  // x, y, w, h
00542             // check for a newline
00543             if (token[0] == '\n') {
00544                 // increment the line count
00545                 frame->line++;
00546             }
00547             // check for a pound sign -- indicates a comment
00548             else if (token[0] == '#') {
00549                 // read to the end-of-line
00550                 do {
00551                     // check for error or end-of-file
00552                     if ((token[0] = fgetc(frame->file)) == EOF) {
00553                         // continue on with the outer loop
00554                         break;
00555                     }
00556                 } while ((token[0] != '\n') && (token[0] != '\r'));
00557                 // increment the line count
00558                 frame->line++;
00559             }
00560             // check for the start of a keyword
00561             else if (isalnum(token[0])) {
00562                 SDL_Rect *rect;
00563                 if (!ReadToken(frame, token, 1)) {
00564                     // cleanup on error
00565                     goto StartLoadConfig_cleanup;
00566                 }
00567 
00568                 // -- check for a block token --
00569                 if (strcmp(token, "block") == 0) {
00570                     // attempt to read in the parameters
00571                     if (!ReadInts(frame, vals, 3, frame->xoffset,
00572                     frame->yoffset)) {
00573                         goto StartLoadConfig_cleanup;
00574                     }
00575                     // add room for the block and check for failure
00576                     if ((rect = (SDL_Rect*)VectorAdd(&blocks)) == NULL) {
00577                         // no memory
00578                         ErrorOut(frame, strings[STRING_ERR_MEM]);
00579                         // terminate with error condition
00580                         SigErr;
00581                     }
00582                     // set the obstacle's values
00583                     rect->x = vals[0];  // different datatypes; no
00584                     rect->y = vals[1];  // better way to do this now
00585                     rect->w = vals[2];
00586                     rect->h = vals[3];
00587                 }
00588 
00589                 // -- check for a flip token --
00590                 else if (strcmp(token, "flip") == 0) {
00591                     // read in the next token
00592                     if (!ReadToken(frame, token, 0)) {
00593                         // cleanup on error
00594                         goto StartLoadConfig_cleanup;
00595                     }
00596                     // check the tokem
00597                     if (strcmp(token, "none") == 0) {
00598                         // clear the flipping flags
00599                         frame->flags = 0;
00600                     }
00601                     else if (strcmp(token, "trans") == 0) {
00602                         // set the transpose flag
00603                         frame->transpose = TRUE;
00604                     }
00605                     else if (strcmp(token, "horiz") == 0) {
00606                         // set the horizontal flip flag
00607                         frame->flipHoriz = TRUE;
00608                     }
00609                     else if (strcmp(token, "vert") == 0) {
00610                         // set the vertical flip flag
00611                         frame->flipVert = TRUE;
00612                     }
00613                     else {
00614                         // bad token
00615                         ErrorOut(frame, strings[STRING_ERR_INVLDKEY], token);
00616                         // terminate with error condition
00617                         SigErr;
00618                     }
00619                 }
00620                 
00621                 // -- check for a goal token --
00622                 else if (strcmp(token, "goal") == 0) {
00623                     if (!ReadAreaParams(frame, goals)) {
00624                         SigErr;
00625                     }
00626                 }
00627                 
00628                 // -- check for an include token --
00629                 else if (strcmp(token, "include") == 0) {
00630                     char fname[128];
00631                     ConfigFileStackFrame *incFrame;
00632                     // prepare offsets
00633                     xoff = frame->xoffset;
00634                     yoff = frame->yoffset;
00635                     // loop through white space
00636                     do {
00637                         // check for EOF
00638                         if ((fname[0] = fgetc(frame->file)) == EOF) {
00639                             // early EOF
00640                             ErrorOut(frame, strings[STRING_ERR_EOFINC]);
00641                             // terminate with error condition
00642                             SigErr;
00643                         }
00644                     } while (isspace(fname[0]));
00645                     // loop until semi-colon while filling the file name
00646                     for (loop = 1; loop < 128; loop++) {
00647                         // get the next byte
00648                         if ((fname[loop] = fgetc(frame->file)) == EOF) {
00649                             // bad name
00650                             fname[loop] = 0;
00651                             ErrorOut(frame, strings[STRING_ERR_EOFNAME], fname);
00652                             // terminate with error condition
00653                             SigErr;
00654                         }
00655                         // check for the end of the file name
00656                         if ((fname[loop] == ';') || (fname[loop] == '\n') ||
00657                         (fname[loop] == '\r')) {
00658                             // terminate the string
00659                             fname[loop] = 0;
00660                             // get out of this loop
00661                             break;
00662                         }
00663                     }
00664                     // check for too long of a string
00665                     if (loop == 127) {
00666                         // shorten the name
00667                         fname[10] = 0;
00668                         // output the error
00669                         ErrorOut(frame, strings[STRING_ERR_NAMELONG], fname);
00670                         // terminate with error condition
00671                         SigErr;
00672                     }
00673                     // create a new stack frame
00674                     if ((incFrame = VectorAdd(&stack)) == NULL) {
00675                         // inadequate memory
00676                         ErrorOut(frame, strings[STRING_ERR_MEM]);
00677                         // terminate with error condition
00678                         SigErr;
00679                     }
00680                     // copy over the file name
00681                     strcpy(incFrame->filename, fname);
00682                     // attempt to open the file
00683                     if (!OpenFile(incFrame)) {
00684                         // could not open file
00685                         ErrorOut(frame, strings[STRING_ERR_FOPEN], fname);
00686                         // terminate with error condition
00687                         SigErr;
00688                     }
00689                     // push the rest of the values onto the stack
00690                     incFrame->flags  = frame->flags;
00691                     incFrame->width  = frame->width;
00692                     incFrame->height = frame->height;
00693                     frame = incFrame;
00694                     frame->line = 1;
00695                     frame->xoffset = xoff;
00696                     frame->yoffset = yoff;
00697                 }
00698 
00699                 // -- check for an offset token --
00700                 else if (strcmp(token, "offset") == 0) {
00701                     // attempt to read in the parameters
00702                     if (!ReadInts(frame, vals, 1, xoff, yoff)) {
00703                         goto StartLoadConfig_cleanup;
00704                     }
00705                     // copy over the new offsets
00706                     frame->xoffset = vals[0];
00707                     frame->yoffset = vals[1];
00708                 }
00709 
00710                 // -- check for a size token --
00711                 else if (strcmp(token, "size") == 0) {
00712                     // attempt to read in the parameters
00713                     if (!ReadInts(frame, vals, 2, 0, 0)) {
00714                         goto StartLoadConfig_cleanup;
00715                     }
00716                     // see if a size was not already set
00717                     if (gameOpts.width == 0) {
00718                         // check for a field that is too small
00719                         if ((vals[2] < 32) || (vals[3] < 32)) {
00720                             // need some room for tanks
00721                             ErrorOut(frame, strings[STRING_ERR_FLDSMALL],
00722                             vals[2], vals[3]);
00723                             // terminate with error condition
00724                             SigErr;
00725                         }
00726                         // copy over the new size
00727                         gameOpts.width = vals[2];
00728                         gameOpts.height = vals[3];
00729                     }
00730                     // copy the size to this stack frame
00731                     frame->width = vals[2];
00732                     frame->height = vals[3];
00733                 }
00734 
00735                 // -- check for a spawn token --
00736                 else if (strcmp(token, "spawn") == 0) {
00737                     if (!ReadAreaParams(frame, spawns)) {
00738                         SigErr;
00739                     }
00740                 }
00741                 else {
00742                     // assure null termination
00743                     token[MAX_TOKEN_LEN - 1] = 0;
00744                     // this isn't a tokem
00745                     ErrorOut(frame, strings[STRING_ERR_INVLDKEY], token);
00746                     // terminate with error condition
00747                     SigErr;
00748                 }
00749             }
00750         }
00751         // pop the stack
00752         fclose(frame->file);
00753         VectorPop(&stack);
00754         // get a pointer to the current frame
00755         frame = &(((ConfigFileStackFrame*)(stack.array))[stack.items - 1]);
00756         // check for another frame at an earlier position on the stack
00757         if (stack.items > 1) {
00758             // grab the previous stack frame's offset values
00759             xoff = (frame - 1)->xoffset;
00760             yoff = (frame - 1)->yoffset;
00761         }
00762         // check for top of stack
00763         else if (stack.items == 1) {
00764             // no previous offsets
00765             xoff = yoff = 0;
00766         }
00767     } while (stack.items > 0); // loop until the stack is exhausted
00768     // see if a size for the battle field wasn't specified
00769     if (gameOpts.height == 0) {
00770         // no size
00771         ErrorOut(NULL, strings[STRING_ERR_NOSIZE]);
00772         // terminate with error condition
00773         SigErr;
00774     }
00775     // check for no spawn areas for each team
00776     if (spawns[TEAM_RED].items == 0) {
00777         // must have spawn areas
00778         ErrorOut(NULL, strings[STRING_ERR_NOREDSPN]);
00779         // terminate with error condition
00780         SigErr;
00781     }
00782     if (spawns[TEAM_BLUE].items == 0) {
00783         // must have spawn areas
00784         ErrorOut(NULL, strings[STRING_ERR_NOBLSPN]);
00785         // terminate with error condition
00786         SigErr;
00787     }
00788     // for CTF games . . .
00789     if (gameOpts.flags & OPT_ISCTF) {
00790         // check for no spawn areas for each team
00791         if (goals[TEAM_RED].items == 0) {
00792             // must have goal areas
00793             ErrorOut(NULL, strings[STRING_ERR_NOREDGOAL]);
00794             // terminate with error condition
00795             SigErr;
00796         }
00797         if (goals[TEAM_BLUE].items == 0) {
00798             // must have goal areas
00799             ErrorOut(NULL, strings[STRING_ERR_NOBLGOAL]);
00800             // terminate with error condition
00801             SigErr;
00802         }
00803     }
00804 
00805     // provide status output on the battlefield
00806     prnout(strings[STRING_BF_STAT], spawns[TEAM_RED].items,
00807     spawns[TEAM_BLUE].items, goals[TEAM_RED].items, goals[TEAM_BLUE].items,
00808     blocks.items);
00809 
00810     // ----- Move the accumulated data from the vectors to the config -----
00811     //       data structures
00812 
00813     // Start by allocating memory.
00814     // obstacles
00815     if (blocks.items > 0) {
00816         Obstacle *obst;
00817         SDL_Rect *rect;
00818         if ((obst = obstacles = malloc((loop = blocks.items) *
00819         sizeof(Obstacle))) == NULL) {
00820             // no more memory
00821             ErrorOut(NULL, strings[STRING_ERR_MEM]);
00822             SigErr;
00823         }
00824         // copy them
00825         for (rect = blocks.array; loop > 0; loop--, rect++, obst++) {
00826             obst->loc = *rect;
00827         }
00828     }
00829 
00830     // spawn areas - red team
00831     if (!CopyAreaData(&(spawns[TEAM_RED]), &(spawnAreas[TEAM_RED]))) {
00832         SigErr;
00833     }
00834     // spawn areas - blue team
00835     if (!CopyAreaData(&(spawns[TEAM_BLUE]), (&spawnAreas[TEAM_BLUE]))) {
00836         SigErr;
00837     }
00838     // for CTF games . . .
00839     if (gameOpts.flags & OPT_ISCTF) {
00840         // goals - red team
00841         if (!CopyAreaData(&(goals[TEAM_RED]), &(goalAreas[TEAM_RED]))) {
00842             SigErr;
00843         }
00844         // goals - blue team
00845         if (!CopyAreaData(&(goals[TEAM_BLUE]), &(goalAreas[TEAM_BLUE]))) {
00846             SigErr;
00847         }
00848     }
00849     // set the game options to match
00850     gameOpts.obstacles = blocks.items;
00851     gameOpts.spawnAreas[TEAM_RED] = spawns[TEAM_RED].items;
00852     gameOpts.spawnAreas[TEAM_BLUE] = spawns[TEAM_BLUE].items;
00853     
00854     // cleanup
00855     StartLoadConfig_cleanup:
00856     // close any files still open in case of error
00857     frame = &(((ConfigFileStackFrame*)(stack.array))[stack.items - 1]);
00858     for (loop = stack.items - 1; loop >=0; loop--, frame--) {
00859         if (frame->file) {
00860             fclose(frame->file);
00861         }
00862     }
00863     // deallocate all vectors
00864     VectorDestroy(&stack);
00865     VectorDestroy(&blocks);
00866     VectorDestroy(&spawns[TEAM_RED]);
00867     VectorDestroy(&spawns[TEAM_BLUE]);
00868     VectorDestroy(&goals[TEAM_RED]);
00869     VectorDestroy(&goals[TEAM_BLUE]);
00870     #undef SigErr
00871 }
00872 
00883 static Bool MakeAreaBuffers(Uint8 *totalAreas, SDL_Rect **areas, Uint8 type) {
00884     // set stores the number of rectangkes that can be stored in a message.
00885     // Becasue of the message format, the number will vary.
00886     unsigned int loop, team, set = 0;
00887     SDL_Rect *area;
00888     Uint8 *ptr = NULL;
00889     NetBuffer *buff = NULL;
00890 
00891     // make the messages describing the areas (goal or spawn)
00892     for (team = 0; team < 2; team++) {  // loop through teams
00893         for (area = areas[team], loop = 0;  // loop through areas
00894         loop < totalAreas[team]; area++, loop++) {
00895             // see if a new buffer is needed
00896             if ((ptr == NULL) ||
00897             ((MSG_MAX_SIZE - (int)(buff->data - ptr)) < 10)) {
00898                 NetBuffer **spot;
00899                 // first, fill out the stored buffer struct for the just
00900                 // completed message
00901                 if (buff) {
00902                     buff->length = (Sint16)(ptr - buff->data);
00903                 }
00904                 // allocate another buffer and check for failure
00905                 if ((buff = BufferNew()) == NULL) {
00906                     // failed
00907                     return FALSE;
00908                 }
00909                 // get a spot in the vector
00910                 if ((spot = VectorAdd(&cfgMsgBuffVec)) == NULL) {
00911                     // free the buffer
00912                     BufferFree(buff);
00913                     return FALSE;
00914                 }
00915                 // finish storing the new buffer's data
00916                 *spot = buff;
00917                 // write the start of the buffer
00918                 buff->data[0] = type;
00919                 ptr = buff->data + 5;  // skip past the message ID & total msgs
00920                 MSG_WRITE_16(ptr, cfgMsgBuffVec.items - 1);
00921                 MSG_WRITE_8(ptr, totalAreas[TEAM_RED]);
00922                 MSG_WRITE_8(ptr, totalAreas[TEAM_BLUE]);
00923                 // clear the number of areas in the set
00924                 set = 0;
00925             }
00926             // see if a new set needs to be made
00927             if (set == 0) {
00928                 // compute the max number of areas for the next set
00929                 if ((set = (MSG_MAX_SIZE - (int)(ptr - buff->data) - 2) >> 3) >
00930                 (totalAreas[team] - loop)) {
00931                     // the maximum number of areas exceeds the number of
00932                     // remaining areas, so handle the remaining areas only
00933                     set = totalAreas[team] - loop;
00934                 }
00935                 // check for too many areas to fit into a set
00936                 if (set > 64) {
00937                     // restrict to the amounmt that will fit
00938                     set = 64;
00939                 }
00940                 // write out the set header
00941                 MSG_WRITE_8(ptr, loop);
00942                 MSG_WRITE_8(ptr, (set - 1) | (team << 6));
00943             }
00944             // decrement the count for the next area
00945             set--;
00946             // write the next area
00947             MSG_WRITE_16(ptr, area->x);
00948             MSG_WRITE_16(ptr, area->y);
00949             MSG_WRITE_16(ptr, area->w);
00950             MSG_WRITE_16(ptr, area->h);
00951         }
00952         assert(set == 0);
00953     }
00954     // filled any buffers?
00955     if (buff) {
00956         // set its length
00957         buff->length = (Sint16)(ptr - buff->data);
00958     }
00959     return TRUE;
00960 }
00961 
00980 static Bool MakeBuffers() {
00981     unsigned int loop;
00982     Uint8 *ptr;
00983     Obstacle *obst;
00984     NetBuffer *buff, **spot;
00985     
00986     // allocate the game options buffer
00987     if ((buff = BufferNew()) == NULL) {
00988         return FALSE;
00989     }
00990     // get a spot in the vector
00991     if ((spot = VectorAdd(&cfgMsgBuffVec)) == NULL) {
00992         // free the buffer
00993         BufferFree(buff);
00994         return FALSE;
00995     }
00996     *spot = buff;
00997     ptr = buff->data;
00998     buff->length = 20;
00999     // don't change the length in the next loop
01000     buff = NULL;
01001     // build the game options message
01002     ptr[0] = MSG_TYPE_OPTION;
01003     ptr[3] = gameOpts.flags;
01004     ptr += 4;
01005     MSG_WRITE_32(ptr, gameOpts.duration);
01006     MSG_WRITE_16(ptr, gameOpts.teamScore[TEAM_RED]);
01007     MSG_WRITE_16(ptr, gameOpts.teamScore[TEAM_BLUE]);
01008     MSG_WRITE_16(ptr, gameOpts.winScore);
01009     MSG_WRITE_16(ptr, gameOpts.width);
01010     MSG_WRITE_16(ptr, gameOpts.height);
01011     // Do not write the total number of messages now. The value must be written
01012     // later when it is known how many messages are in the game configuration.
01013 
01014     // make the messages describing all the obstacles
01015     for (obst = obstacles, loop = 0; loop < gameOpts.obstacles; obst++, loop++) {
01016         // check for a need to get a new buffer
01017         if ((loop % MAX_OBSTACLES_PER_MSG) == 0) {
01018             // first, fill out the length of the completed messages in the
01019             // stored buffer struct
01020             if (buff) {
01021                 buff->length = (Sint16)(ptr - buff->data);
01022             }
01023             // allocate another buffer and check for failure
01024             if ((buff = BufferNew()) == NULL) {
01025                 // failed
01026                 goto MakeBuffers_failure;
01027             }
01028             // get a spot in the vector
01029             if ((spot = VectorAdd(&cfgMsgBuffVec)) == NULL) {
01030                 // free the buffer
01031                 BufferFree(buff);
01032                 goto MakeBuffers_failure;
01033             }
01034             // store the  buffer
01035             *spot = buff;
01036             // write the start of the buffer
01037             buff->data[0] = MSG_TYPE_OBSTACLE;
01038             ptr = buff->data + 5;    // skip past the message ID & total msgs
01039             MSG_WRITE_16(ptr, cfgMsgBuffVec.items - 1);
01040             MSG_WRITE_16(ptr, gameOpts.obstacles);
01041             MSG_WRITE_16(ptr, loop);
01042         }
01043         // add the next obstacle to the buffer
01044         MSG_WRITE_16(ptr, obst->loc.x);
01045         MSG_WRITE_16(ptr, obst->loc.y);
01046         MSG_WRITE_16(ptr, obst->loc.w);
01047         MSG_WRITE_16(ptr, obst->loc.h);
01048     }
01049     // if there were any obstacle messages . . .
01050     if (buff) {
01051         // store the length of the last message
01052         buff->length = (Sint16)(ptr - buff->data);
01053         // prevent the next loop from changing the length
01054         buff = NULL;
01055     }
01056     // make messages describing the spawn areas
01057     if (!MakeAreaBuffers(gameOpts.spawnAreas, spawnAreas, MSG_TYPE_SPAWN)) {
01058         goto MakeBuffers_failure;
01059     }
01060     // make the messages describing the goal areas
01061     if (!MakeAreaBuffers(gameOpts.goalAreas, goalAreas, MSG_TYPE_GOAL)) {
01062         goto MakeBuffers_failure;
01063     }
01064 
01065     // success
01066     prnout(strings[STRING_CMPLOPT],
01067     gameOpts.totalMsgs = gameOpts.rcvdMsgs = cfgMsgBuffVec.items);
01068     
01069     // get a pointer to the first option message
01070     spot = (NetBuffer**)(cfgMsgBuffVec.array);
01071     // write the number of messages to the game options buffer
01072     MSG_WRITE_16C((*spot)->data, 18, gameOpts.totalMsgs);
01073     // loop through the other messages to write the total value to them
01074     for (spot++, loop = gameOpts.totalMsgs - 1; loop > 0; loop--, spot++) {
01075         MSG_WRITE_16C((*spot)->data, 3, gameOpts.totalMsgs);
01076     }
01077     return TRUE;
01078 
01079     // failure
01080     MakeBuffers_failure:
01081     // deallocate all messages
01082     VectorDestroy(&cfgMsgBuffVec);
01083     // send back failure code
01084     return FALSE;
01085 }
01086 
01087 // should setup some data and start the game config loading on the I/O thread
01088 Bool InitGameConfig(const char *fname) {
01089     // clear error status
01090     gameOpts.flags &= ~OPT_ERROR;
01091     // initialize the vector of configuration message buffers
01092     if (!VectorInit(&cfgMsgBuffVec, cfgMsgBuffVec.minSize)) {
01093         // failure
01094         return FALSE;
01095     }
01096     // load the configuration from the file
01097     StartLoadConfig(fname);
01098     // check for failure
01099     if (gameOpts.flags & OPT_ERROR) {
01100         // failure -- deallocate the vector
01101         free(cfgMsgBuffVec.array);
01102         cfgMsgBuffVec.array = NULL;
01103         return FALSE;
01104     }
01105     // build the network messages that will describe the field to clients
01106     if (!MakeBuffers()) {
01107         // failure -- deallocate the vector
01108         free(cfgMsgBuffVec.array);
01109         cfgMsgBuffVec.array = NULL;
01110         return FALSE;
01111     }
01112     // success!
01113     gameOpts.flags |= OPT_ISSET;
01114     return TRUE;
01115 }
01116 
01117 void UninitGameConfig() {
01118     int loop;
01119     // clear the pointers for the game config sending list
01120     configListStart = configListEnd = NULL;
01121     // clear the next transmission index
01122     nextItem = 0;
01123     // eliminate obstacles
01124     ObstacleUninit();
01125     // eliminate areas
01126     for (loop = 0; loop < 2; loop++) {
01127         if ((gameOpts.goalAreas[loop] > 0) && goalAreas[loop]) {
01128             free(goalAreas[loop]);
01129         }
01130         goalAreas[loop] = NULL;
01131         gameOpts.goalAreas[loop] = 0;
01132         if ((gameOpts.spawnAreas[loop] > 0) && spawnAreas[loop]) {
01133             free(spawnAreas[loop]);
01134         }
01135         spawnAreas[loop] = NULL;
01136         gameOpts.spawnAreas[loop] = 0;
01137     }
01138     // if the message flags are still allocated . . .
01139     if (optionMsgFlags) {
01140         // deallocate them
01141         free(optionMsgFlags);
01142         optionMsgFlags = NULL;
01143     }
01144     // clear the game options
01145     memset(&gameOpts, 0, sizeof(gameOpts));
01146     // clear the destination map for config messages
01147     memset(destMap, 0, sizeof(destMap));
01148     // remove all message buffers
01149     VectorDestroy(&cfgMsgBuffVec);
01150 }
01151 
01152 void AddToConfigList(ClientData *client) {
01153     MsgDescr *msg = NULL;
01154     Player *p;
01155     int loop, now, ind, destflag, flags;
01156     // add the client to the config linked list
01157     if ((client->configPrev = configListEnd) == NULL) {
01158         configListStart = configListEnd = client;
01159     }
01160     else {
01161         configListEnd->configNext = client;
01162         configListEnd = client;
01163     }
01164     client->configNext = NULL;
01165     // record the index of the first item it should receive
01166     client->configFirst = nextItem;
01167     // add the destination flag
01168     destMap[ind = GetDestMapIndex(client->cid)] |=
01169         destflag = GetDestMapFlag(client->cid);
01170     // use now for the update time
01171     now = SDL_GetTicks();
01172     // create a message with player idents for the new client
01173     for (p = players, loop = MAX_PLAYERS; loop > 0; loop--, p++) {
01174         // skip this player?
01175         if (!(p->flags & PLAYER_ACTIVE))
01176             continue;
01177         // store flags
01178         flags = p->flags;
01179         // set update flags
01180         p->flags |= PLAYER_UPDATE_IDENT | PLAYER_UPDATE_DFS;
01181         if (!(p->flags & PLAYER_DEAD)) {
01182             p->flags |= PLAYER_UPDATE_TANK | PLAYER_UPDATE_SHELL;
01183         }
01184         // produce the message
01185         if ((msg = AppendPlayerData(msg, p, now)) == NULL) {
01186             // oh, bother
01187             return;
01188         }
01189         // change the message -- no broadcast, and destined only for this new
01190         // client
01191         msg->bcast = msg->mcast = FALSE;
01192         msg->destMap[ind] = destflag;
01193         // restore original flags
01194         p->flags = flags;
01195     }
01196     // send it
01197     if (msg) {
01198         AddMessage(msg);
01199     }
01200     // send the score, too
01202     SendScoreUpdate(NULL, 0);
01203 }
01204 
01205 void RemoveFromConfigList(ClientData *client) {
01206     // in the list and not the first
01207     if (client->configPrev != NULL) {
01208         client->configPrev->configNext = client->configNext;
01209         // last in the list
01210         if (client->configNext == NULL) {
01211             configListEnd = client->configPrev;
01212         }
01213     }
01214     // first in the list
01215     else if (client == configListStart) {
01216         // also last in the list?
01217         if ((configListStart = client->configNext) == NULL) {
01218             configListEnd = NULL;
01219         }
01220     }
01221     // clear the client's list pointers
01222     client->configNext = client->configPrev = NULL;
01223     // remove the destination flag
01224     destMap[GetDestMapIndex(client->cid)] &=
01225         ~GetDestMapFlag(client->cid);
01226 }
01227 
01228 Bool ServiceGameConfig() {
01229     ClientData *client;
01230     MsgDescr *msg;
01231     Uint32 now, loop;
01232     // check for no clients waiting on the game config, or an unset config, or
01233     // still waiting for more time to pass
01234 
01240     if ((configListStart == NULL) || !(gameOpts.flags & OPT_ISSET) ||
01241     (((now = SDL_GetTicks()) - lastTransmit) < 64)) {
01242         //  do something else
01243         return TRUE;
01244     }
01245     // grab a new message and check for failure
01246     if ((msg = MessageNew()) == NULL) {
01247         // could not allocate a message
01248         return FALSE;
01249     }
01250     // grab the corresponding message buffer
01251     msg->buffer = *(NetBuffer**)VectorItem(&cfgMsgBuffVec, nextItem);
01252     assert(msg->buffer != NULL);
01253     assert(msg->buffer->length > 0);
01254     // Setup the message descriptor.
01255     msg->approveFunc = NULL;
01256     msg->ackFunc =  NULL;
01257     msg->failFunc = NULL;
01258     msg->bcast = msg->buffLifeUnmanaged = TRUE;  // broadcast OK, keep the buffer
01259     for (loop = 0; loop < DESTMAPLEN; loop++) {
01260         msg->destMap[loop] = destMap[loop];  // hope for an optimizing compiler
01261     }
01262     // send the messgae to all the clients still in the list
01263     if (!AddMessage(msg)) {
01264         //printf("Failed to add config message\n");
01265         // could not send the message
01266         return FALSE;
01267     }
01268     // advance the index
01269     if (++nextItem == cfgMsgBuffVec.items) {
01270         // advanced past the last message -- wrap around back to the first
01271         // message
01272         nextItem = 0;
01273     }
01274     //msg = NULL;
01275     // remove clients that have been sent all the config data
01276     while (configListStart->configFirst == nextItem) {
01277         // if a message with player data has not been created yet . . .
01278         //if (!msg) {
01279             // create it
01280         //}
01281         // add this client to the destination list of the player list
01282         // get a reference to the client that will be removed from the list
01283         client = configListStart;
01284         // remove the destination flag
01285         destMap[GetDestMapIndex(client->cid)] &=
01286             ~GetDestMapFlag(client->cid);
01287         // remove the client and see if it was the last client in the list
01288         if ((configListStart = configListStart->configNext) == NULL) {
01289             assert(client == configListEnd);
01290             // fixup the end of the list
01291             configListEnd = NULL;
01292             // nothing left to do
01293             break;
01294         }
01295         // clear the client's list pointers
01296         client->configNext = client->configPrev = NULL;
01297     }
01298     // success!
01299     return TRUE;
01300 }
01301 
01316 static Bool FlagCfgMsg(int max, int index) {
01317     int arrayInd, arrayBit;
01318     // check for an already allocated array for option message flags
01319     if (optionMsgFlags == NULL) {
01320         // allocate the array
01321         if ((optionMsgFlags = calloc(rounddivup(max, 32), 4)) == NULL) {
01322             // failure
01323             return FALSE;
01324         }
01325         // set the sizes
01326         gameOpts.totalMsgs = remainOptionMsgs = max;
01327     }
01328     // check for an inconsistent number of option messages
01329     else if (max != gameOpts.totalMsgs) {
01330         // could be bad data, so reject this message
01331         return FALSE;
01332     }
01333     // by this point, there should be more options to read
01334     assert(remainOptionMsgs > 0);
01335     // compute the array spot of the flag
01336     arrayInd = bitindex(index, Uint32);
01337     arrayBit = bitflag(index, Uint32);
01338     // see if the flag is not already set
01339     if (!(optionMsgFlags[arrayInd] & arrayBit)) {
01340         // flag the indicated message as received
01341         optionMsgFlags[arrayInd] |= arrayBit;
01342         // one less message to receive
01343         if (--remainOptionMsgs == 0) {
01344             // transition to the client state
01345             networkState = NETSTATE_CLIENT;
01346             // deallocate the message flags
01347             free(optionMsgFlags);
01348             optionMsgFlags = NULL;
01349         }
01350         // message is good & new
01351         return TRUE;
01352     }
01353     // message is old -- do not parse
01354     return FALSE;
01355 }
01356 
01357 Bool HandleGameOption(SocketAddr *origin, NetBuffer *buffer, Uint8 *unused,
01358 Uint16 noid) {
01359     int total;
01360     // this message is only valid when received by clients
01361     if ((networkState < NETSTATE_RCVCFG) || (networkState > NETSTATE_CLIENT) ||
01362     (buffer->length != 20) || !ValidateServer(origin)) {
01363         // ignore this message
01364         return FALSE;
01365     }
01366     // ack but ignore if this client is no longer reading in the config options
01367     if (networkState != NETSTATE_RCVCFG) {
01368         return TRUE;
01369     }
01370     // determine the total number of messages
01371     MSG_READ_16C(buffer->data, 18, total);
01372     // see if this message should be parsed
01373     if (FlagCfgMsg(total, 0)) {
01374         // read in the options
01375         gameOpts.flags = buffer->data[3];
01376         MSG_READ_32C(buffer->data, 4,  gameOpts.duration);
01377         MSG_READ_16C(buffer->data, 8,  gameOpts.teamScore[TEAM_RED]);
01378         MSG_READ_16C(buffer->data, 10, gameOpts.teamScore[TEAM_BLUE]);
01379         MSG_READ_16C(buffer->data, 12, gameOpts.winScore);
01380         MSG_READ_16C(buffer->data, 14, gameOpts.width);
01381         MSG_READ_16C(buffer->data, 16, gameOpts.height);
01382     }
01383     return TRUE;
01384 }
01385 
01386 Bool HandleObstacle(SocketAddr *origin, NetBuffer *buffer, Uint8 *unused,
01387 Uint16 noid) {
01388     int total, msgIndex;
01389     Uint16 numObst, index, loop;
01390     Obstacle *obst;
01391     Uint8 *ptr;
01392     // this message is only valid when received by clients
01393     if ((networkState < NETSTATE_RCVCFG) || (networkState > NETSTATE_CLIENT) ||
01394     !ValidateServer(origin)) {
01395         // ignore this message
01396         return FALSE;
01397     }
01398     // ack but ignore if this client is no longer reading in the config options
01399     if (networkState != NETSTATE_RCVCFG) {
01400         return TRUE;
01401     }
01402     // move to the number of option messages
01403     ptr = buffer->data + 3;
01404     // read the number of messages
01405     MSG_READ_16(ptr, total);
01406     // read the index of this message
01407     MSG_READ_16(ptr, msgIndex);
01408     // see if this message has already been read
01409     if (!FlagCfgMsg(total, msgIndex)) {
01410         // quit now, but still acknowledge the message
01411         return TRUE;
01412     }
01413     // read the number of obstacles
01414     MSG_READ_16(ptr, numObst);
01415     // no obstacles already made?
01416     if ((gameOpts.obstacles == 0) && (numObst > 0)) {
01417         // set the new total, allocate memory for it, check for error
01418         if ((obstacles =
01419         malloc(sizeof(Obstacle) * (gameOpts.obstacles = numObst))) == NULL) {
01420             // assure size is zero
01421             gameOpts.obstacles = 0;
01422             // set error condition
01423             gameOpts.flags |= OPT_ERROR;
01424             // quit this
01425             return FALSE;
01426         }
01427     }
01428     // check for a change in the total
01429     else if (gameOpts.obstacles != numObst) {
01430         // this shouldn't normally happen, but ignore it when it does
01431         return FALSE;
01432     }
01433     // read in the starting index
01434     MSG_READ_16(ptr, index);
01435     // not a valid start index?
01436     if ((index >= numObst) || (index % MAX_OBSTACLES_PER_MSG)) {
01437         // set error condition
01438         gameOpts.flags |= OPT_ERROR;
01439         // quit
01440         return FALSE;
01441     }
01442     // set the maximum number of obstacles
01443     loop = ((numObst - index) >= MAX_OBSTACLES_PER_MSG) ? MAX_OBSTACLES_PER_MSG
01444     : numObst - index;
01445     // loop through all transmitted obstacles
01446     for (obst = &(obstacles[index]); loop > 0; loop--, obst++) {
01447         // x coordinate
01448         MSG_READ_16(ptr, obst->loc.x);
01449         // y coordinate
01450         MSG_READ_16(ptr, obst->loc.y);
01451         // width
01452         MSG_READ_16(ptr, obst->loc.w);
01453         // height
01454         MSG_READ_16(ptr, obst->loc.h);
01455     }
01456     return TRUE;
01457 }
01458 
01459 Bool HandleArea(SocketAddr *origin, NetBuffer *buffer, Uint8 *unused,
01460 Uint16 noid) {
01461     int loop, total, msgIndex;
01462     SDL_Rect *rect, **areas;
01463     Uint8 *ptr, *numAreas, team, togo;
01464     // this message is only valid when received by clients
01465     if ((networkState < NETSTATE_RCVCFG) || (networkState > NETSTATE_CLIENT) ||
01466     !ValidateServer(origin)) {
01467         // ignore this message
01468         return FALSE;
01469     }
01470     // ack but ignore if this client is no longer reading in the config options
01471     if (networkState != NETSTATE_RCVCFG) {
01472         return TRUE;
01473     }
01474     // move to the number of option messages
01475     ptr = buffer->data + 3;
01476     // read the number of messages
01477     MSG_READ_16(ptr, total);
01478     // read the index of this message
01479     MSG_READ_16(ptr, msgIndex);
01480     // see if this message has already been read
01481     if (!FlagCfgMsg(total, msgIndex)) {
01482         // quit now, but still acknowledge the message
01483         return TRUE;
01484     }
01485     // setup pointers to the type of area to operate upon
01486     switch (buffer->data[0]) {
01487         // spawn areas
01488         case MSG_TYPE_SPAWN:
01489             numAreas = gameOpts.spawnAreas;
01490             areas = spawnAreas;
01491             break;
01492         // goal areas
01493         case MSG_TYPE_GOAL:
01494             numAreas = gameOpts.goalAreas;
01495             areas = goalAreas;
01496             break;
01497         default:
01498             // shouldn't ever get here; otherwise, something in the message
01499             // system is screwed
01500             assert(0);
01501             exit(__LINE__);
01502     }
01503     // loop through supported teams
01504     for (loop = 0; loop < 2; loop++, ptr++) {
01505         // see if the areas have not been created
01506         if ((numAreas[loop] == 0) && (*ptr > 0)) {
01507             // set the new total, allocate memory for it, check for error
01508             if ((areas[loop] =
01509             malloc(sizeof(SDL_Rect) * (numAreas[loop] = *ptr))) == NULL) {
01510                 // assure size is zero
01511                 numAreas[loop] = 0;
01512                 // set error condition
01513                 gameOpts.flags |= OPT_ERROR;
01514                 // quit this
01515                 return FALSE;
01516             }
01517         }
01518         // check for a change in the total
01519         else if (numAreas[loop] != *ptr) {
01520             // this shouldn't normally happen, but ignore it when it does
01521             return FALSE;
01522         }
01523     }
01524     // loop while there is enough data left in the buffer for at least one more
01525     // header and one more area rect
01526     while ((buffer->length - (ptr - buffer->data)) >= 10) {
01527         // read in the index of the next area
01528         MSG_READ_8(ptr, loop);
01529         // read in the number of areas to read
01530         togo = (*ptr & 0x3F) + 1;
01531         // read in the owning team
01532         team = *(ptr++) >> 6;
01533         // assure the range to be read is within bounds
01534         if (((loop + togo) > numAreas[team]) ||
01535         ((ptr - buffer->data + togo * 8) > buffer->length)) {
01536             // bad data; quit now
01537             return FALSE;
01538         }
01539         // loop through all the indicated areas
01540         for (rect = &(areas[team][loop]); togo > 0; togo--, rect++) {
01541             // x coordinate
01542             MSG_READ_16(ptr, rect->x);
01543             // y coordinate
01544             MSG_READ_16(ptr, rect->y);
01545             // width
01546             MSG_READ_16(ptr, rect->w);
01547             // height
01548             MSG_READ_16(ptr, rect->h);
01549         }
01550     }
01551     return TRUE;
01552 }
01553 
01554 Bool HandleScoreUpdate(SocketAddr *origin, NetBuffer *buffer, Uint8 *unused,
01555 Uint16 noid) {
01556     int playerUpdates, killerID, killedID;
01557     Uint8 *data;
01558     Sint16 score;
01559     // this message is only valid when received by clients
01560     if (((networkState != NETSTATE_RCVCFG) && (networkState != NETSTATE_CLIENT))
01561     || (buffer->length < 8) || !ValidateServer(origin)) {
01562         // ignore this message
01563         return FALSE;
01564     }
01565     // move past message header
01566     data = buffer->data + 3;
01567     // read in the new score(s)
01568     MSG_READ_8(data, playerUpdates);
01569     // check the range -- can't have more players score than players
01570     if (playerUpdates > MAX_PLAYERS) {
01571         // bad data
01572         return FALSE;
01573     }
01574     MSG_READ_16(data, gameOpts.teamScore[TEAM_RED]);
01575     MSG_READ_16(data, gameOpts.teamScore[TEAM_BLUE]);
01576     // for each player score update . . .
01577     for (; playerUpdates > 0; playerUpdates--) {
01578         MSG_READ_8(data, killerID);
01579         MSG_READ_8(data, killedID);
01580         MSG_READ_16(data, score);
01581         // check data ranges
01582         if ((killerID >= MAX_PLAYERS) || (killedID >= MAX_PLAYERS)) {
01583             // bad data
01584             return FALSE;
01585         }
01586         // add the notice on-screen
01587         AddScoreNotice(killerID, killedID, score);
01588     }
01589     // signal the need to render the new score
01590     gameOpts.flags |= OPT_UPDATESCORE;
01591     return TRUE;
01592 }
01593 
01594 Bool SendScoreUpdate(ScoreUpdate *scores, int len) {
01595     MsgDescr *msg;
01596     Uint8 *data;
01597     // only the server should send scores
01598     assert(networkState == NETSTATE_SERVER);
01599     // either scores is not NULL or there are no player score updates
01600     assert((len == 0) || (scores != NULL));
01601     // get a message descriptior
01602     if ((msg = MessageNew()) == NULL) {
01603         // didn't work
01604         return FALSE;
01605     }
01606     // get a buffer
01607     if ((msg->buffer = BufferNew()) == NULL) {
01608         // no buffer
01609         MessageFree(msg);
01610         return FALSE;
01611     }
01612     data = msg->buffer->data;
01613     // setup the message descriptor
01614     msg->approveFunc = NULL;
01615     msg->ackFunc = NULL;
01616     msg->failFunc = MsgFailDisconnect;
01617     msg->interval <<= 2;
01618     SetDestToAll(msg);
01619     // build the response message
01620     MSG_WRITE_8(data, MSG_TYPE_SCORE);
01621     data += 2;         // move past ID
01622     MSG_WRITE_8(data, len);
01623     MSG_WRITE_16(data, gameOpts.teamScore[TEAM_RED]);
01624     MSG_WRITE_16(data, gameOpts.teamScore[TEAM_BLUE]);
01625     for (; len > 0; len--, scores++) {
01626         // killer
01627         MSG_WRITE_8(data, scores->killerId);
01628         // killed
01629         MSG_WRITE_8(data, scores->killedId);
01630         // new score
01631         MSG_WRITE_16(data, players[scores->killerId].kills);
01632     }
01633     // compute the number of bytes written
01634     msg->buffer->length = data - msg->buffer->data;
01635     msg->bcast = TRUE;
01636     // send it
01637     SetDestToAll(msg);
01638     AddMessage(msg);
01639     return TRUE;
01640 }
01641 

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