00001
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
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
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
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
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
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
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
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
00255 if (*token != '}')
00256 Com_Printf("SV_ParseMapTile: Bad tile desc in '%s' - too many entries for size\n", target->id);
00257
00258
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
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
00340 token = Com_EParse(text, errhead, filename);
00341 if (!*text)
00342 return qfalse;
00343
00344
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
00358 token = Com_EParse(text, errhead, filename);
00359 if (!text || *token == '}')
00360 break;
00361
00362 if (!strcmp(token, "title")) {
00363
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
00372 token = Com_EParse(text, errhead, filename);
00373 if (!text)
00374 break;
00375
00376
00377
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
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
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
00407 } else if (!strcmp(token, "tileset")) {
00408 token = SV_GetTileFromTileSet(map, filename, text, a);
00409
00410 } else if (!strcmp(token, "fix")) {
00411 const mTile_t *t;
00412
00413
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
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
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
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
00498 if (IS_SOLID(*mapAlts) || (tileAlts == ALL_TILES))
00499 return;
00500
00501
00502 assert((*mapAlts != ALL_TILES) || (*mapRating == 0));
00503
00504
00505 if (IS_SOLID(tileAlts)) {
00506 *mapAlts = tileAlts;
00507 *mapRating = 1;
00508
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
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
00556 if (x + tile->w > mAsm->width + 2 || y + tile->h > mAsm->height + 2)
00557 return qfalse;
00558
00559
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
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
00687 assert(x % mAsm->dx == 0);
00688 assert(y % mAsm->dy == 0);
00689 #endif
00690
00691
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
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
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
00840 if (SV_TestFilled(map))
00841 return qtrue;
00842
00843
00844 for (i = 0; i < CHECK_ALTERNATIVES_COUNT; i++) {
00845 int x, y;
00846 if (!SV_PickRandomTile(map, &idx[i], &pos[i])) {
00847
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
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
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
00919 SV_AddTile(map, mToPlace[idx].tile, x, y, idx, pos);
00920 break;
00921 }
00922 }
00923
00924 if (pos < mapSize)
00925 continue;
00926
00927
00928 if (!mToPlace[idx].cnt)
00929 break;
00930
00931
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
00941 if (pos < mapSize) {
00942 pos = 0;
00943 idx++;
00944 } else {
00945
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
01003 if (SDL_SemTryWait(mapSem) != 0)
01004 return -1;
01005 SDL_LockMutex(mapLock);
01006
01007 assert(threadID == 0);
01008 threadID = SDL_ThreadID();
01009
01010
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;
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
01067 if (SDL_CondWaitTimeout(mapCond, mapLock, timeout) != 0) {
01068 Com_Printf("SV_ParallelSearch: timeout at %i ms, restarting\n", timeout);
01069 timeout += timeout;
01070
01071 if (SDL_SemTryWait(mapSem) != 0) {
01072
01073 continue;
01074 }
01075
01076 for (i = 0; i < threadno; i++) {
01077 SDL_WaitThread(threads[i], NULL);
01078 }
01079
01080 SDL_SemPost(mapSem);
01081
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
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
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
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
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
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
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
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
01222 map->mAsm = rand() % map->numAssemblies;
01223
01224
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
01242 assert(mAsm->width <= MAX_RANDOM_MAP_WIDTH);
01243 assert(mAsm->height <= MAX_RANDOM_MAP_HEIGHT);
01244
01245 SV_PrepareTilesToPlace(map);
01246
01247
01248 map->numPlaced = 0;
01249 SV_ClearMap(map);
01250
01251
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
01265 if (map->basePath[0])
01266 Com_sprintf(asmMap, sizeof(map->basePath) + 1, "-%s", map->basePath);
01267
01268 asmPos[0] = 0;
01269
01270
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 }