sv_rma.c

Go to the documentation of this file.
00001 
00008 /*
00009 All original material Copyright (C) 2002-2010 UFO: Alien Invasion.
00010 
00011 Original file from Quake 2 v3.21: quake2-2.31/server/sv_init.c
00012 
00013 Copyright (C) 1997-2001 Id Software, Inc.
00014 
00015 This program is free software; you can redistribute it and/or
00016 modify it under the terms of the GNU General Public License
00017 as published by the Free Software Foundation; either version 2
00018 of the License, or (at your option) any later version.
00019 
00020 This program is distributed in the hope that it will be useful,
00021 but WITHOUT ANY WARRANTY; without even the implied warranty of
00022 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00023 
00024 See the GNU General Public License for more details.
00025 
00026 You should have received a copy of the GNU General Public License
00027 along with this program; if not, write to the Free Software
00028 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00029 
00030 */
00031 
00032 #include "server.h"
00033 #include "sv_rma.h"
00034 #include "../shared/parse.h"
00035 #include "SDL_thread.h"
00036 
00037 #define ASSEMBLE_THREADS 2
00038 static SDL_sem *mapSem;
00039 static SDL_cond *mapCond;
00040 static SDL_mutex *mapLock;
00041 static Uint32 threadID;
00042 
00048 static void RandomList (const int n, short *list)
00049 {
00050     short i;
00051 
00052     for (i = 0; i < n; i++)
00053         list[i] = i;
00054 
00055     for (i = 0; i < n; i++) {
00056         const short r = rand() % (i + (n - i));
00057         const short t = list[r];
00058         list[r] = list[i];
00059         list[i] = t;
00060     }
00061 }
00062 
00063 #define ALL_TILES (0xfffffffeUL)
00064 #define IS_SOLID(x) ((x)&1UL)
00065 
00090 static unsigned long tileMask (const char chr)
00091 {
00092     if (chr == '+')
00093         return 1UL;
00094     else if (chr == '0')
00095         return ALL_TILES;
00096     else if (chr >= '1' && chr <= '5')
00097         return 1UL << (chr - '0');
00098     else if (chr >= 'a' && chr <= 'z')
00099         return 1UL << (chr - 'a' + 6);
00100     else if (chr >= 'A' && chr <= 'Z')
00101         return 1UL << (chr - 'A' + 6);
00102 
00103     Com_Error(ERR_DROP, "SV_ParseMapTile: Invalid tile char '%c'", chr);
00104 }
00105 
00106 static const mTileSet_t *SV_GetMapTileSet (const mapInfo_t *map, const char *tileSetName)
00107 {
00108     int i;
00109 
00110     for (i = 0; i < map->numTileSets; i++)
00111         if (!strcmp(tileSetName, map->mTileSets[i].id))
00112             return &map->mTileSets[i];
00113 
00114     return NULL;
00115 }
00116 
00117 static inline const mTile_t *SV_GetMapTile (const mapInfo_t *map, const char *tileName)
00118 {
00119     int i;
00120 
00121     for (i = 0; i < map->numTiles; i++)
00122         if (!strcmp(tileName, map->mTile[i].id))
00123             return &map->mTile[i];
00124 
00125     return NULL;
00126 }
00127 
00133 static qboolean SV_ParseMapTileSet (const char *filename, const char **text, mapInfo_t *map, qboolean inherit)
00134 {
00135     const char *errhead = "SV_ParseMapTileSet: Unexpected end of file (";
00136     const char *token;
00137     mTileSet_t *target = &map->mTileSets[map->numTileSets];
00138 
00139     memset(target, 0, sizeof(*target));
00140 
00141     /* get tileset name */
00142     token = Com_EParse(text, errhead, filename);
00143     if (!*text)
00144         return qfalse;
00145 
00146     Q_strncpyz(target->id, token, sizeof(target->id));
00147 
00148     /* start parsing the block */
00149     token = Com_EParse(text, errhead, filename);
00150     if (!*text)
00151         return qfalse;
00152     if (*token != '{') {
00153         Com_Printf("SV_ParseMapTileSet: Expected '{' for tileset '%s' (%s)\n", target->id, filename);
00154         return qfalse;
00155     }
00156 
00157     do {
00158         token = Com_EParse(text, errhead, filename);
00159         if (!*text)
00160             return qfalse;
00161         if (token[0] != '}') {
00162             char *tileTarget = target->tiles[target->numTiles];
00163             const size_t size = sizeof(target->tiles[target->numTiles]);
00164             if (inherit) {
00165                 if (token[0] == '+')
00166                     token++;
00167 
00168                 Com_sprintf(tileTarget, size, "%s%s", map->inheritBasePath, token);
00169             } else {
00170                 Q_strncpyz(tileTarget, token, size);
00171             }
00172 
00173             if (SV_GetMapTile(map, tileTarget) != NULL)
00174                 target->numTiles++;
00175             else
00176                 Com_Error(ERR_DROP, "Did not find tile '%s' from tileset '%s'", tileTarget, target->id);
00177         }
00178     } while (token[0] != '}');
00179 
00180     map->numTileSets++;
00181     return qfalse;
00182 }
00183 
00189 static qboolean SV_ParseMapTile (const char *filename, const char **text, mapInfo_t *map, qboolean inherit)
00190 {
00191     const char *errhead = "SV_ParseMapTile: Unexpected end of file (";
00192     const char *token;
00193     int x, y, i;
00194     mTile_t *target = &map->mTile[map->numTiles];
00195 
00196     /* get tile name */
00197     token = Com_EParse(text, errhead, filename);
00198     if (!*text)
00199         return qfalse;
00200 
00201     memset(target, 0, sizeof(*target));
00202 
00203     if (inherit) {
00204         if (token[0] == '+')
00205             token++;
00206         Com_sprintf(target->id, sizeof(target->id), "%s%s", map->inheritBasePath, token);
00207     } else {
00208         Q_strncpyz(target->id, token, sizeof(target->id));
00209     }
00210 
00211     /* start parsing the block */
00212     token = Com_EParse(text, errhead, filename);
00213     if (!*text)
00214         return qfalse;
00215     if (*token != '{') {
00216         Com_Printf("SV_ParseMapTile: Expected '{' for tile '%s' (%s)\n", target->id, filename);
00217         return qfalse;
00218     }
00219 
00220     /* get width and height */
00221     token = Com_EParse(text, errhead, filename);
00222     if (!*text)
00223         return qfalse;
00224     target->w = atoi(token);
00225 
00226     token = Com_EParse(text, errhead, filename);
00227     if (!*text)
00228         return qfalse;
00229     target->h = atoi(token);
00230 
00231     if (target->w > MAX_TILESIZE || target->h > MAX_TILESIZE) {
00232         Com_Printf("SV_ParseMapTile: Bad tile size [%i %i] (%s) (max. [%i %i])\n", target->w, target->h, filename, MAX_TILESIZE, MAX_TILESIZE);
00233         *text = strchr(*text, '}');
00234         return qfalse;
00235     }
00236 
00237     /* get tile specs */
00238     for (y = target->h - 1; y >= 0; y--)
00239         for (x = 0; x < target->w; x++) {
00240             token = Com_EParse(text, errhead, filename);
00241             if (!*text || *token == '}') {
00242                 Com_Printf("SV_ParseMapTile: Bad tile desc in '%s' - not enough entries for size\n", target->id);
00243                 *text = strchr(*text, '}') + 1;
00244                 return 0;
00245             }
00246             target->spec[y][x] = 0UL;
00247             for (i = 0; token[i]; i++) {
00248                 target->spec[y][x] |= tileMask(token[i]);
00249             }
00250         }
00251 
00252     token = Com_EParse(text, errhead, filename);
00253 
00254     /* get connections */
00255     if (*token != '}')
00256         Com_Printf("SV_ParseMapTile: Bad tile desc in '%s' - too many entries for size\n", target->id);
00257 
00258     /* successfully parsed - this tile counts */
00259     return qtrue;
00260 }
00261 
00271 static const char *SV_GetCvarToken (const mAssembly_t *a, const char* token, const char *filename, const char **text, const char *errhead)
00272 {
00273     const cvar_t *cvar;
00274 
00275     Com_DPrintf(DEBUG_SERVER, "SV_GetCvarToken: cvar replacement: %s\n", token);
00276 
00277     cvar = Cvar_FindVar(token);
00278 
00279     token = Com_EParse(text, errhead, filename);
00280     if (!text || token[0] == '}')
00281         return NULL;
00282 
00283     if (cvar == NULL)
00284         return token;
00285 
00286     Com_DPrintf(DEBUG_SERVER, "SV_ParseAssembly: cvar replacement value: %s\n", cvar->string);
00287     if (cvar->string[0] != '+') {
00288         Com_Printf("SV_ParseAssembly: warning - cvar '%s' value doesn't seam to be a valid tile id '%s' - set to default '%s'\n",
00289                 cvar->name, cvar->string, token);
00290         Cvar_Set(cvar->name, token);
00291         if (token[0] != '+')
00292             Com_Error(ERR_DROP, "SV_ParseAssembly: wrong tile id in assembly '%s'", a->id);
00293 
00294         return token;
00295     }
00296     return cvar->string;
00297 }
00298 
00299 static inline const char *SV_GetTileFromTileSet (const mapInfo_t *map, const char *filename, const char **text, const mAssembly_t *a)
00300 {
00301     const char *errhead = "SV_GetTileFromTileSet: Unexpected end of file (";
00302     const mTileSet_t *tileSet;
00303     int random;
00304     const char *token;
00305 
00306     /* get tileset id */
00307     token = Com_EParse(text, errhead, filename);
00308     if (!text)
00309         Com_Error(ERR_DROP, "SV_GetTileFromTileSet: illegal tileset syntax in assembly '%s' in %s", a->id, filename);
00310 
00311     tileSet = SV_GetMapTileSet(map, token);
00312     if (tileSet == NULL)
00313         Com_Error(ERR_DROP, "SV_GetTileFromTileSet: Could not find tileset %s   in %s (assembly %s)", token, filename, a->id);
00314 
00315     random = rand() % tileSet->numTiles;
00316     return tileSet->tiles[random];
00317 }
00318 
00332 static qboolean SV_ParseAssembly (mapInfo_t *map, const char *filename, const char **text, mAssembly_t *a)
00333 {
00334     const char *errhead = "SV_ParseAssembly: Unexpected end of file (";
00335     const char *token;
00336     int x, y;
00337     const mTile_t *tile;
00338 
00339     /* get assembly name */
00340     token = Com_EParse(text, errhead, filename);
00341     if (!*text)
00342         return qfalse;
00343 
00344     /* init */
00345     memset(a, 0, sizeof(*a));
00346     Q_strncpyz(a->id, token, sizeof(a->id));
00347     a->width = 8;
00348     a->height = 8;
00349     a->dx = 1;
00350     a->dy = 1;
00351 
00352     token = Com_EParse(text, errhead, filename);
00353     if (!*text || *token != '{')
00354         Com_Error(ERR_DROP, "Invalid assembly definition '%s' - invalid token '%s' (%s)", a->id, token, filename);
00355 
00356     do {
00357         /* get tile name */
00358         token = Com_EParse(text, errhead, filename);
00359         if (!text || *token == '}')
00360             break;
00361 
00362         if (!strcmp(token, "title")) {
00363             /* get map title */
00364             token = Com_EParse(text, errhead, filename);
00365             if (!text)
00366                 break;
00367 
00368             Q_strncpyz(a->title, token, sizeof(a->title));
00369             continue;
00370         } else if (!strcmp(token, "multiplayer")) {
00371             /* get map title */
00372             token = Com_EParse(text, errhead, filename);
00373             if (!text)
00374                 break;
00375 
00376             /* a multiplayer only tile - forced to be exactly once in the map when
00377              * we are playing a multiplayer match */
00378             if (sv_maxclients->integer >= 2) {
00379                 const mTile_t *t = SV_GetMapTile(map, token);
00380                 if (t != NULL) {
00381                     const ptrdiff_t i = t - map->mTile;
00382                     a->min[i] = 1;
00383                     a->max[i] = 1;
00384                 } else {
00385                     Com_Error(ERR_DROP, "Could not find multiplayer tile: '%s' in assembly '%s' (%s)", token, a->id, filename);
00386                 }
00387             }
00388             continue;
00389         } else if (!strcmp(token, "size")) {
00390             /* get map size */
00391             token = Com_EParse(text, errhead, filename);
00392             if (!text)
00393                 break;
00394 
00395             sscanf(token, "%i %i", &a->width, &a->height);
00396             a->size = a->width * a->height;
00397             continue;
00398         } else if (!strcmp(token, "grid")) {
00399             /* get map size */
00400             token = Com_EParse(text, errhead, filename);
00401             if (!text)
00402                 break;
00403 
00404             sscanf(token, "%i %i", &a->dx, &a->dy);
00405             continue;
00406         /* chose a tile from a tileset */
00407         } else if (!strcmp(token, "tileset")) {
00408             token = SV_GetTileFromTileSet(map, filename, text, a);
00409         /* fix tilename "x y" */
00410         } else if (!strcmp(token, "fix")) {
00411             const mTile_t *t;
00412 
00413             /* get tile */
00414             token = Com_EParse(text, errhead, filename);
00415             if (!text)
00416                 break;
00417 
00418             if (token[0] == '*') {
00419                 token = SV_GetCvarToken(a, token + 1, filename, text, errhead);
00420                 if (token == NULL)
00421                     break;
00422             } else if (!strcmp(token, "tileset")) {
00423                 token = SV_GetTileFromTileSet(map, filename, text, a);
00424             }
00425 
00426             t = SV_GetMapTile(map, token);
00427             if (t != NULL) {
00428                 const ptrdiff_t i = t - map->mTile;
00429                 if (a->numFixed >= MAX_FIXEDTILES)
00430                     Com_Error(ERR_DROP, "SV_ParseAssembly: Too many fixed tiles in assembly '%s' (%s)", a->id, filename);
00431 
00432                 /* get coordinates */
00433                 token = Com_EParse(text, errhead, filename);
00434                 if (!text)
00435                     Com_Error(ERR_DROP, "SV_ParseAssembly: Error in assembly %s - could not get coordinates for fixed tile", filename);
00436 
00437                 sscanf(token, "%i %i", &x, &y);
00438                 if (x < 0 || x >= MAX_RANDOM_MAP_WIDTH) {
00439                     Com_Error(ERR_DROP, "SV_ParseAssembly: Error, invalid fixed coordinates given for x (%i) boundaries are: [0:%i] (%s).",
00440                             x, MAX_RANDOM_MAP_WIDTH - 1, filename);
00441                 } else if (y < 0 || y >= MAX_RANDOM_MAP_HEIGHT) {
00442                     Com_Error(ERR_DROP, "SV_ParseAssembly: Error, invalid fixed coordinates given for y (%i) - boundaries are: [0:%i] (%s).",
00443                             y, MAX_RANDOM_MAP_HEIGHT - 1, filename);
00444                 }
00445                 a->fX[a->numFixed] = x;
00446                 a->fY[a->numFixed] = y;
00447                 a->fT[a->numFixed] = i;
00448                 a->numFixed++;
00449             } else
00450                 Com_Error(ERR_DROP, "Could not find fixed tile: '%s' in assembly '%s' (%s)", token, a->id, filename);
00451             continue;
00452         /* <format>*cvarname <defaultvalue> "min max"</format> */
00453         } else if (token[0] == '*') {
00454             token = SV_GetCvarToken(a, token + 1, filename, text, errhead);
00455             if (token == NULL)
00456                 break;
00457         }
00458 
00459         tile = SV_GetMapTile(map, token);
00460         if (tile != NULL) {
00461             const ptrdiff_t i = tile - map->mTile;
00462             /* get min and max tile number */
00463             token = Com_EParse(text, errhead, filename);
00464             if (!text || *token == '}')
00465                 Com_Error(ERR_DROP, "SV_ParseAssembly: Error in assembly %s (invalid syntax for tile %s)", filename, tile->id);
00466 
00467             if (!strstr(token, " "))
00468                 Com_Error(ERR_DROP, "SV_ParseAssembly: Error in assembly %s (min max value of tile %s)", filename, tile->id);
00469 
00470             sscanf(token, "%i %i", &x, &y);
00471             a->min[i] = x;
00472             a->max[i] = y;
00473             if (a->min[i] > a->max[i])
00474                 Com_Error(ERR_DROP, "SV_ParseAssembly: Error in assembly %s (min is bigger than max for tile %s)", filename, tile->id);
00475             if (a->max[i] <= 0)
00476                 Com_Error(ERR_DROP, "SV_ParseAssembly: Error in assembly %s (max is <= 0 for tile %s)", filename, tile->id);
00477         } else {
00478             Com_Error(ERR_DROP, "Could not find tile: '%s' in assembly '%s' (%s)", token, a->id, filename);
00479         }
00480     } while (text);
00481 
00482     return qtrue;
00483 }
00484 
00485 
00495 static void SV_CombineAlternatives (unsigned long *mapAlts, const unsigned long tileAlts, char *mapRating)
00496 {
00497     /* don't touch solid fields of the map, return if tile has no connection info */
00498     if (IS_SOLID(*mapAlts) || (tileAlts == ALL_TILES))
00499         return;
00500 
00501     /* for an empty map tile the rating must be zero */
00502     assert((*mapAlts != ALL_TILES) || (*mapRating == 0));
00503 
00504     /* copy if tile is solid */
00505     if (IS_SOLID(tileAlts)) {
00506         *mapAlts = tileAlts;
00507         *mapRating = 1;
00508     /* combine otherways */
00509     } else {
00510         *mapAlts &= tileAlts;
00511         (*mapRating)--;
00512     }
00513 }
00514 
00518 static void SV_ClearMap (mapInfo_t *map)
00519 {
00520     unsigned long *mp = &map->curMap[0][0];
00521     unsigned long *end = &map->curMap[MAX_RANDOM_MAP_HEIGHT - 1][MAX_RANDOM_MAP_WIDTH - 1];
00522 
00523     memset(map->curRating, 0, sizeof(map->curRating));
00524 
00525     while (mp <= end)
00526         *(mp++) = ALL_TILES;
00527 }
00528 
00540 static qboolean SV_FitTile (const mapInfo_t *map, mTile_t * tile, const int x, const int y)
00541 {
00542     int tx, ty;
00543     const unsigned long *spec = NULL;
00544     const unsigned long *m = NULL;
00545     const mAssembly_t *mAsm = &map->mAssembly[map->mAsm];
00546 
00547     /* check for valid grid positions */
00548     assert(x % mAsm->dx == 0);
00549     assert(y % mAsm->dy == 0);
00550     assert(tile);
00551 
00552     if (x < 0 || y < 0)
00553         return qfalse;
00554 
00555     /* check for map border */
00556     if (x + tile->w > mAsm->width + 2 || y + tile->h > mAsm->height + 2)
00557         return qfalse;
00558 
00559     /* test for fit */
00560     spec = &tile->spec[0][0];
00561     m = &map->curMap[y][x];
00562     for (ty = 0; ty < tile->h; ty++) {
00563         for (tx = 0; tx < tile->w; tx++, spec++, m++) {
00564             const unsigned long combined = (*m) & (*spec);
00565 
00566             /* quit if both are solid or no equal connection is found */
00567             if (IS_SOLID(combined) || !combined)
00568                 return qfalse;
00569         }
00570         spec += (MAX_TILESIZE - tile->w);
00571         m += (MAX_RANDOM_MAP_WIDTH - tile->w);
00572     }
00573 
00574     return qtrue;
00575 }
00576 
00585 static qboolean SV_TestFilled (const mapInfo_t *map)
00586 {
00587     int x, y;
00588     const mAssembly_t *mAsm = &map->mAssembly[map->mAsm];
00589 
00590     for (y = 1; y < mAsm->height + 1; y++)
00591         for (x = 1; x < mAsm->width + 1; x++)
00592             if (!IS_SOLID(map->curMap[y][x]))
00593                 return qfalse;
00594 
00595     return qtrue;
00596 }
00597 
00601 static void SV_DumpRating (const mapInfo_t *map)
00602 {
00603     int x, y;
00604     const mAssembly_t *mAsm = &map->mAssembly[map->mAsm];
00605 
00606     Com_Printf("Rating:\n");
00607     for (y = mAsm->height; y >= 1; y--) {
00608         for (x = 1; x < mAsm->width + 1; x++)
00609             Com_Printf(" %2d", (int) map->curRating[y][x]);
00610         Com_Printf("\n");
00611     }
00612     Com_Printf("\n");
00613 }
00614 
00618 static void SV_DumpPlaced (const mapInfo_t *map, int pl)
00619 {
00620     int x, y;
00621     const mAssembly_t *mAsm = &map->mAssembly[map->mAsm];
00622     const int h = mAsm->height;
00623     const int w = mAsm->width;
00624     const mPlaced_t *placed = &map->mPlaced[pl];
00625 
00626     Com_Printf("Placed tile %s at %d %d\n", placed->tile->id, placed->x, placed->y);
00627 
00628     for (y = h; y >= 1; y--) {
00629         for (x = 1; x < w + 1; x++) {
00630             const int dx = x - placed->x;
00631             const int dy = y - placed->y;
00632 
00633             if ((dx >= 0) && (dx < placed->tile->w) &&
00634                     (dy >= 0) && (dy < placed->tile->h) &&
00635                     IS_SOLID(placed->tile->spec[dy][dx]))
00636                 Com_Printf(" X");
00637             else
00638                 Com_Printf(" .");
00639         }
00640         Com_Printf("\n");
00641     }
00642     Com_Printf("\n");
00643 }
00644 
00652 static int SV_CalcRating (const mapInfo_t *map)
00653 {
00654     int x, y, rating = 0;
00655     const mAssembly_t *mAsm = &map->mAssembly[map->mAsm];
00656 
00657     for (y = 1; y <= mAsm->height; y++)
00658         for (x = 1; x <= mAsm->width; x++)
00659             rating += map->curRating[y][x];
00660 
00661     if (sv_dumpmapassembly->integer)
00662         SV_DumpRating(map);
00663 
00664     return rating;
00665 }
00666 
00680 static void SV_AddTile (mapInfo_t *map, const mTile_t *tile, int x, int y, int idx, int pos)
00681 {
00682     int tx, ty;
00683 #ifdef DEBUG
00684     const mAssembly_t *mAsm = &map->mAssembly[map->mAsm];
00685 
00686     /* check vor valid grid positions */
00687     assert(x % mAsm->dx == 0);
00688     assert(y % mAsm->dy == 0);
00689 #endif
00690 
00691     /* add the new tile */
00692     for (ty = 0; ty < tile->h; ty++)
00693         for (tx = 0; tx < tile->w; tx++) {
00694             assert(y + ty < MAX_RANDOM_MAP_HEIGHT);
00695             assert(x + tx < MAX_RANDOM_MAP_WIDTH);
00696 
00697             SV_CombineAlternatives(&map->curMap[y + ty][x + tx], tile->spec[ty][tx], &map->curRating[y + ty][x + tx]);
00698         }
00699 
00700     /* add the tile to the array of placed tiles*/
00701     if (map->numPlaced >= MAX_MAPTILES)
00702         Com_Error(ERR_DROP, "SV_AddTile: Too many map tiles");
00703 
00704     map->mPlaced[map->numPlaced].tile = tile;
00705     map->mPlaced[map->numPlaced].x = x;
00706     map->mPlaced[map->numPlaced].y = y;
00707     map->mPlaced[map->numPlaced].idx = idx;
00708     map->mPlaced[map->numPlaced].pos = pos;
00709 
00710     map->numPlaced++;
00711 
00712     if (idx >= 0) {
00713         map->mToPlace[idx].cnt++;
00714     }
00715 }
00716 
00726 static void SV_RemoveTile (mapInfo_t *map, int* idx, int* pos)
00727 {
00728     int tx, ty;
00729     int i, index;
00730 
00731     SV_ClearMap(map);
00732 
00733     if (map->numPlaced == 0)
00734         return;
00735 
00736     map->numPlaced--;
00737     index = map->mPlaced[map->numPlaced].idx;
00738 
00739     if (index >= 0) {
00740         map->mToPlace[index].cnt--;
00741     }
00742 
00743     for (i = map->numPlaced; i--;) {
00744         const mTile_t *tile = map->mPlaced[i].tile;
00745         const int x = map->mPlaced[i].x;
00746         const int y = map->mPlaced[i].y;
00747         assert(i >= 0);
00748         assert(tile);
00749 
00750         /* add the tile again*/
00751         for (ty = 0; ty < tile->h; ty++) {
00752             for (tx = 0; tx < tile->w; tx++) {
00753                 assert(y + ty < MAX_RANDOM_MAP_HEIGHT);
00754                 assert(x + tx < MAX_RANDOM_MAP_WIDTH);
00755 
00756                 SV_CombineAlternatives(&map->curMap[y + ty][x + tx], tile->spec[ty][tx], &map->curRating[y + ty][x + tx]);
00757             }
00758         }
00759     }
00760 
00761     if (idx)
00762         *idx = index;
00763 
00764     if (pos)
00765         *pos = map->mPlaced[map->numPlaced].pos;
00766 }
00767 
00775 static qboolean SV_PickRandomTile (mapInfo_t *map, int* idx, int* pos)
00776 {
00777     const mAssembly_t *mAsm = &map->mAssembly[map->mAsm];
00778     const int numToPlace = map->numToPlace;
00779     const int mapSize = mAsm->size;
00780     const int mapW = mAsm->width;
00781     const int start_idx = *idx = rand() % numToPlace;
00782     const int start_pos = *pos = rand() % mapSize;
00783     const mToPlace_t *mToPlace = map->mToPlace;
00784 
00785     do {
00786         if (mToPlace[*idx].cnt < mToPlace[*idx].max) {
00787             do {
00788                 const int x = (*pos) % mapW;
00789                 const int y = (*pos) / mapW;
00790 
00791                 if ((x % mAsm->dx == 0)
00792                     && (y % mAsm->dy == 0)
00793                     && SV_FitTile(map, mToPlace[*idx].tile, x, y)) {
00794                     return qtrue;
00795                 }
00796 
00797                 (*pos) += 1;
00798                 (*pos) %= mapSize;
00799 
00800             } while ((*pos) != start_pos);
00801         }
00802 
00803         (*idx) += 1;
00804         (*idx) %= numToPlace;
00805 
00806     } while ((*idx) != start_idx);
00807 
00808     return qfalse;
00809 }
00810 
00815 #define CHECK_ALTERNATIVES_COUNT 10
00816 
00823 static qboolean SV_AddMissingTiles (mapInfo_t *map)
00824 {
00825     int i;
00826     int idx[CHECK_ALTERNATIVES_COUNT];
00827     int pos[CHECK_ALTERNATIVES_COUNT];
00828     int rating[CHECK_ALTERNATIVES_COUNT];
00829     mapInfo_t backup;
00830     const mAssembly_t *mAsm = &map->mAssembly[map->mAsm];
00831     const int mapW = mAsm->width;
00832     const int mapH = mAsm->height;
00833     const mToPlace_t *mToPlace = map->mToPlace;
00834 
00835     memcpy(&backup, map, sizeof(*map));
00836     while (1) {
00837         int max_rating = -mapW * mapH * 4;
00838 
00839         /* check if the map is already filled */
00840         if (SV_TestFilled(map))
00841             return qtrue;
00842 
00843         /* try some random tiles at random positions */
00844         for (i = 0; i < CHECK_ALTERNATIVES_COUNT; i++) {
00845             int x, y;
00846             if (!SV_PickRandomTile(map, &idx[i], &pos[i])) {
00847                 /* remove all tiles placed by this function */
00848                 memcpy(map, &backup, sizeof(*map));
00849                 return qfalse;
00850             }
00851 
00852             x = pos[i] % mapW;
00853             y = pos[i] / mapW;
00854             SV_AddTile(map, mToPlace[idx[i]].tile, x, y, idx[i], pos[i]);
00855 
00856             if (SV_TestFilled(map))
00857                 return qtrue;
00858 
00859             rating[i] = SV_CalcRating(map);
00860 
00861             if (rating[i] > max_rating)
00862                 max_rating = rating[i];
00863 
00864             SV_RemoveTile(map, NULL, NULL);
00865         }
00866 
00867         for (i = 0; i < CHECK_ALTERNATIVES_COUNT; i++) {
00868             if (rating[i] == max_rating) {
00869                 const int x = pos[i] % mapW;
00870                 const int y = pos[i] / mapW;
00871                 SV_AddTile(map, mToPlace[idx[i]].tile, x, y, idx[i], pos[i]);
00872                 break;
00873             }
00874         }
00875     }
00876 }
00877 
00883 static void SV_AddMapTiles (mapInfo_t *map)
00884 {
00885     int idx, pos;
00886     const mAssembly_t *mAsm = &map->mAssembly[map->mAsm];
00887     const int mapW = mAsm->width;
00888     const int mapSize = mAsm->size;
00889     const int numToPlace = map->numToPlace;
00890     const mToPlace_t *mToPlace = map->mToPlace;
00891     short prList[MAX_RANDOM_MAP_HEIGHT * MAX_RANDOM_MAP_WIDTH];
00892     const int start = map->numPlaced;
00893 #ifdef DEBUG
00894     const mPlaced_t *mPlaced = map->mPlaced;
00895 #endif
00896 
00897     /* shuffle only once, the map will be build with that seed */
00898     RandomList(mapSize, prList);
00899 
00900     pos = 0;
00901     idx = 0;
00902     while (idx < numToPlace) {
00903         while (mToPlace[idx].cnt < mToPlace[idx].min) {
00904             for (; pos < mapSize; pos++) {
00905                 const int x = prList[pos] % mapW;
00906                 const int y = prList[pos] / mapW;
00907                 if (sv_threads->integer) {
00908                     if (SDL_SemValue(mapSem) != 1) {
00909                         /* someone else beat me to it */
00910                         return;
00911                     }
00912                 }
00913 
00914                 if ((x % mAsm->dx != 0) || (y % mAsm->dy != 0))
00915                     continue;
00916 
00917                 if (SV_FitTile(map, mToPlace[idx].tile, x, y)) {
00918                     /* add tile */
00919                     SV_AddTile(map, mToPlace[idx].tile, x, y, idx, pos);
00920                     break;
00921                 }
00922             }
00923             /* tile fits, try another tile of the same type */
00924             if (pos < mapSize)
00925                 continue;
00926 
00927             /* tile doesn't fit and no try left with this tile */
00928             if (!mToPlace[idx].cnt)
00929                 break;
00930 
00931             /* tile does not fit, restore last status - replace the last tile */
00932             assert(map->numPlaced > 0);
00933 #ifdef DEBUG
00934             assert(idx == mPlaced[map->numPlaced - 1].idx);
00935 #endif
00936             SV_RemoveTile(map, &idx, &pos);
00937             pos++;
00938         }
00939 
00940         /* tile fits, try next tile */
00941         if (pos < mapSize) {
00942             pos = 0;
00943             idx++;
00944         } else {
00945             /* no more retries */
00946             if (start == map->numPlaced) {
00947                 Com_Error(ERR_DROP, "SV_AddMapTiles: Impossible to assemble map '%s' with assembly '%s'\n",
00948                         map->name, mAsm->id ? mAsm->id : "");
00949             }
00950             SV_RemoveTile(map, &idx, &pos);
00951             pos++;
00952         }
00953 
00954         if ((idx == numToPlace) && !SV_AddMissingTiles(map)) {
00955             SV_RemoveTile(map, &idx, &pos);
00956             pos++;
00957         }
00958     }
00959 }
00960 
00966 static void SV_PrepareTilesToPlace (mapInfo_t *map)
00967 {
00968     int i;
00969     const mAssembly_t *mAsm = &map->mAssembly[map->mAsm];
00970 
00971     map->numToPlace = 0;
00972     memset(&map->mToPlace[0], 0, sizeof(map->mToPlace));
00973 
00974     for (i = 0; i < map->numTiles; i++) {
00975         if (mAsm->max[i]) {
00976             map->mToPlace[map->numToPlace].tile = &map->mTile[i];
00977             map->mToPlace[map->numToPlace].min = mAsm->min[i];
00978             map->mToPlace[map->numToPlace].max = mAsm->max[i];
00979             map->numToPlace++;
00980         }
00981     }
00982 }
00983 
00994 static int SV_AssemblyThread (void* data)
00995 {
00996     mapInfo_t *map = (mapInfo_t*) data;
00997 
00998     srand(time(NULL));
00999 
01000     SV_AddMapTiles(map);
01001 
01002     /* the first thread to reach this point, gets the semaphore */
01003     if (SDL_SemTryWait(mapSem) != 0)
01004         return -1;
01005     SDL_LockMutex(mapLock);
01006 
01007     assert(threadID == 0);
01008     threadID = SDL_ThreadID();
01009 
01010     /* tell main we're done */
01011     SDL_CondSignal(mapCond);
01012     SDL_UnlockMutex(mapLock);
01013 
01014     return 0;
01015 }
01016 
01041 static int SV_ParallelSearch (mapInfo_t *map)
01042 {
01043     SDL_Thread *threads[ASSEMBLE_THREADS];
01044     mapInfo_t *maps[ASSEMBLE_THREADS];
01045     int i;
01046     static int timeout = 5000;  /* wait for 5 sec initially, double it every time it times out */
01047     const int threadno = sv_threads->integer < ASSEMBLE_THREADS ? sv_threads->integer : ASSEMBLE_THREADS;
01048 
01049     threadID = 0;
01050     assert(mapSem == NULL);
01051     mapSem = SDL_CreateSemaphore(1);
01052 
01053     if (mapLock == NULL)
01054         mapLock = SDL_CreateMutex();
01055 
01056     if (mapCond == NULL)
01057         mapCond = SDL_CreateCond();
01058 
01059     SDL_LockMutex(mapLock);
01060     for (i = 0; i < threadno; i++) {
01061         maps[i] = Mem_Alloc(sizeof(*map));
01062         memcpy(maps[i], map, sizeof(*map));
01063         threads[i] = SDL_CreateThread(&SV_AssemblyThread, (void*) maps[i]);
01064     }
01065     while (threadID == 0) {
01066         /* if nobody is done after 5 sec, restart, double the timeout. */
01067         if (SDL_CondWaitTimeout(mapCond, mapLock, timeout) != 0) {
01068             Com_Printf("SV_ParallelSearch: timeout at %i ms, restarting\n", timeout);
01069             timeout += timeout;
01070             /* tell them all to die */
01071             if (SDL_SemTryWait(mapSem) != 0) {
01072                 /* couldn't tell everyone to die, someone must have finished since the last line... */
01073                 continue;
01074             }
01075             /* collect the dead */
01076             for (i = 0; i < threadno; i++) {
01077                 SDL_WaitThread(threads[i], NULL);
01078             }
01079             /* reset semaphore */
01080             SDL_SemPost(mapSem);
01081             /* start'em again */
01082             for (i = 0; i < threadno; i++) {
01083                 memcpy(maps[i], map, sizeof(*map));
01084                 threads[i] = SDL_CreateThread(&SV_AssemblyThread, (void*) maps[i]);
01085             }
01086         } else {
01087             /* someone finished */
01088             assert(threadID != 0);
01089         }
01090     }
01091     SDL_UnlockMutex(mapLock);
01092     for (i = 0; i < threadno; i++) {
01093         if (SDL_GetThreadID(threads[i]) == threadID) {
01094             memcpy(map, maps[i], sizeof(*map));
01095         }
01096 
01097         SDL_WaitThread(threads[i], NULL);
01098         Mem_Free(maps[i]);
01099     }
01100 
01101     /* cleanup, for possible next time */
01102     SDL_DestroySemaphore(mapSem);
01103     mapSem = NULL;
01104     threadID = 0;
01105     timeout = 5000;
01106 
01107     return 0;
01108 }
01109 
01117 static void SV_ParseUMP (const char *name, mapInfo_t *map, qboolean inherit)
01118 {
01119     char filename[MAX_QPATH];
01120     byte *buf;
01121     const char *text, *token;
01122 
01123     /* load the map info */
01124     Com_sprintf(filename, sizeof(filename), "maps/%s.ump", name);
01125     FS_LoadFile(filename, &buf);
01126     if (!buf)
01127         Com_Error(ERR_DROP, "SV_ParseUMP: Map assembly info '%s' not found", filename);
01128 
01129     /* parse it */
01130     text = (const char*)buf;
01131     do {
01132         token = Com_Parse(&text);
01133         if (!text)
01134             break;
01135 
01136         if (!strcmp(token, "extends")) {
01137             token = Com_Parse(&text);
01138             SV_ParseUMP(token, map, qtrue);
01139         } else if (!strcmp(token, "base")) {
01140             token = Com_Parse(&text);
01141             if (inherit)
01142                 Q_strncpyz(map->inheritBasePath, token, sizeof(map->inheritBasePath));
01143             else
01144                 Q_strncpyz(map->basePath, token, sizeof(map->basePath));
01145         } else if (!strcmp(token, "tileset")) {
01146             if (map->numTileSets >= MAX_TILESETS)
01147                 Com_Printf("SV_ParseUMP: Too many map tileset found in (%s)\n", filename);
01148             else if (SV_ParseMapTileSet(filename, &text, map, inherit))
01149                 map->numTileSets++;
01150         } else if (!strcmp(token, "tile")) {
01151             if (map->numTiles >= MAX_TILETYPES)
01152                 Com_Printf("SV_ParseUMP: Too many map tile types (%s)\n", filename);
01153             else if (SV_ParseMapTile(filename, &text, map, inherit))
01154                 map->numTiles++;
01155         } else if (!strcmp(token, "assembly")) {
01156             if (inherit) {
01157                 FS_SkipBlock(&text);
01158             } else {
01159                 if (map->numAssemblies >= MAX_MAPASSEMBLIES)
01160                     Com_Printf("SV_ParseUMP: Too many map assemblies (%s)\n", filename);
01161                 else if (SV_ParseAssembly(map, filename, &text, &map->mAssembly[map->numAssemblies]))
01162                     map->numAssemblies++;
01163             }
01164         } else if (token[0] == '{') {
01165             Com_Printf("SV_ParseUMP: Skipping unknown block\n");
01166             /* ignore unknown block */
01167             text = strchr(text, '}') + 1;
01168             if (!text)
01169                 break;
01170         } else
01171             Com_Printf("SV_ParseUMP: Unknown token '%s' (%s)\n", token, filename);
01172     } while (text);
01173 
01174     /* free the file */
01175     FS_FreeFile(buf);
01176 }
01177 
01195 mapInfo_t* SV_AssembleMap (const char *name, const char *assembly, char *asmMap, char *asmPos)
01196 {
01197     int i;
01198     mAssembly_t *mAsm;
01199     mapInfo_t *map;
01200 
01201     map = Mem_Alloc(sizeof(*map));
01202     Q_strncpyz(map->name, name, sizeof(map->name));
01203 
01204     SV_ParseUMP(name, map, qfalse);
01205 
01206     /* check for parsed tiles and assemblies */
01207     if (!map->numTiles)
01208         Com_Error(ERR_DROP, "No map tiles defined (%s)!", name);
01209 #ifdef DEBUG
01210     else
01211         Com_DPrintf(DEBUG_SERVER, "numTiles: %i\n", map->numTiles);
01212 #endif
01213 
01214     if (!map->numAssemblies)
01215         Com_Error(ERR_DROP, "No map assemblies defined (%s)!", name);
01216 #ifdef DEBUG
01217     else
01218         Com_DPrintf(DEBUG_SERVER, "numAssemblies: %i\n", map->numAssemblies);
01219 #endif
01220 
01221     /* use random assembly, if no valid one has been specified */
01222     map->mAsm = rand() % map->numAssemblies;
01223 
01224     /* overwrite with specified, if any */
01225     if (assembly && assembly[0]) {
01226         for (i = 0; i < map->numAssemblies; i++)
01227             if (!strcmp(assembly, map->mAssembly[i].id)) {
01228                 map->mAsm = i;
01229                 break;
01230             }
01231         if (i == map->numAssemblies) {
01232             Com_Printf("SV_AssembleMap: Map assembly '%s' not found\n", assembly);
01233         }
01234     }
01235 
01236     mAsm = &map->mAssembly[map->mAsm];
01237     assert(mAsm);
01238 
01239     Com_DPrintf(DEBUG_SERVER, "Use assembly: '%s'\n", mAsm->id);
01240 
01241     /* check size */
01242     assert(mAsm->width <= MAX_RANDOM_MAP_WIDTH);
01243     assert(mAsm->height <= MAX_RANDOM_MAP_HEIGHT);
01244 
01245     SV_PrepareTilesToPlace(map);
01246 
01247     /* assemble the map */
01248     map->numPlaced = 0;
01249     SV_ClearMap(map);
01250 
01251     /* place fixed parts - defined in ump via fix parameter */
01252     for (i = 0; i < mAsm->numFixed; i++)
01253         SV_AddTile(map, &map->mTile[mAsm->fT[i]], mAsm->fX[i], mAsm->fY[i], -1, -1);
01254 
01255     if (sv_threads->integer) {
01256         if (SV_ParallelSearch(map) < 0) {
01257             Mem_Free(map);
01258             return NULL;
01259         }
01260     } else {
01261         SV_AddMapTiles(map);
01262     }
01263 
01264     /* prepare map and pos strings */
01265     if (map->basePath[0])
01266         Com_sprintf(asmMap, sizeof(map->basePath) + 1, "-%s", map->basePath);
01267 
01268     asmPos[0] = 0;
01269 
01270     /* generate the strings */
01271     for (i = 0; i < map->numPlaced; i++) {
01272         const mPlaced_t *pl = &map->mPlaced[i];
01273 
01274         if (sv_dumpmapassembly->integer)
01275             SV_DumpPlaced(map, i);
01276 
01277         if (asmMap[0])
01278             Q_strcat(asmMap, " ", MAX_TOKEN_CHARS * MAX_TILESTRINGS);
01279         if (asmPos[0])
01280             Q_strcat(asmPos, " ", MAX_TOKEN_CHARS * MAX_TILESTRINGS);
01281 
01282         Q_strcat(asmMap, va("%s", pl->tile->id), MAX_TOKEN_CHARS * MAX_TILESTRINGS);
01283         Q_strcat(asmPos, va("%i %i %i", (pl->x - mAsm->width / 2) * 8, (pl->y - mAsm->height / 2) * 8, 0), MAX_TOKEN_CHARS * MAX_TILESTRINGS);
01284     }
01285 
01286     Com_DPrintf(DEBUG_SERVER, "tiles: %s\n", asmMap);
01287     Com_DPrintf(DEBUG_SERVER, "pos: %s\n", asmPos);
01288     Com_DPrintf(DEBUG_SERVER, "tiles: %i\n", map->numPlaced);
01289 
01290     assert(map);
01291     return map;
01292 }

Generated by  doxygen 1.6.2