diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8b1aab2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +cmake-build-debug \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f9e51c5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) +project(Viewer) + +set(CMAKE_CXX_STANDARD 14) +find_package(PNG REQUIRED) +set(CMAKE_CXX_FLAGS "${CMAKE_XX_FLAGS} -Wall") +include_directories(${PNG_INCLUDE_DIR}) + +add_executable(MapViewer generationByCubitect/generator.cpp generationByCubitect/layers.cpp generationByCubitect/finders.cpp xmap.cpp main.cpp) +target_link_libraries(MapViewer ${PNG_LIBRARY}) diff --git a/generationByCubitect/finders.cpp b/generationByCubitect/finders.cpp new file mode 100644 index 0000000..d108764 --- /dev/null +++ b/generationByCubitect/finders.cpp @@ -0,0 +1,2097 @@ +#include "finders.hpp" + +#include +#include +#include +#include + + +STRUCT(quad_threadinfo_t) +{ + int64_t start, end; + StructureConfig sconf; + int threadID; + int quality; + const char *fnam; +}; + + +//============================================================================== +// Globals +//============================================================================== + + + + + +const int64_t lowerBaseBitsQ1[] = // for quad-structure with quality 1 + { + 0x3f18,0x520a,0x751a,0x9a0a + }; + +const int64_t lowerBaseBitsQ2[] = // for quad-structure with quality 2 + { + 0x0770,0x0775,0x07ad,0x07b2,0x0c3a,0x0c58,0x0cba,0x0cd8,0x0e38, + 0x0e5a,0x0ed8,0x0eda,0x111c,0x1c96,0x2048,0x20e8,0x2248,0x224a, + 0x22c8,0x258d,0x272d,0x2732,0x2739,0x2758,0x275d,0x27c8,0x27c9, + 0x2aa9,0x2c3a,0x2cba,0x2eb8,0x308c,0x3206,0x371a,0x3890,0x3d0a, + 0x3f18,0x4068,0x40ca,0x40e8,0x418a,0x4248,0x426a,0x42ea,0x4732, + 0x4738,0x4739,0x4765,0x4768,0x476a,0x47b0,0x47b5,0x47d4,0x47d9, + 0x47e8,0x4c58,0x4e38,0x4eb8,0x4eda,0x5118,0x520a,0x5618,0x5918, + 0x591d,0x5a08,0x5e18,0x5f1c,0x60ca,0x6739,0x6748,0x6749,0x6758, + 0x6776,0x67b4,0x67b9,0x67c9,0x67d8,0x67dd,0x67ec,0x6c3a,0x6c58, + 0x6cba,0x6d9a,0x6e5a,0x6ed8,0x6eda,0x7108,0x717a,0x751a,0x7618, + 0x791c,0x8068,0x8186,0x8248,0x824a,0x82c8,0x82ea,0x8730,0x8739, + 0x8748,0x8768,0x87b9,0x87c9,0x87ce,0x87d9,0x898d,0x8c3a,0x8cda, + 0x8e38,0x8eb8,0x951e,0x9718,0x9a0a,0xa04a,0xa068,0xa0ca,0xa0e8, + 0xa18a,0xa26a,0xa2e8,0xa2ea,0xa43d,0xa4e1,0xa589,0xa76d,0xa7ac, + 0xa7b1,0xa7ed,0xa85d,0xa86d,0xaa2d,0xb1f8,0xb217,0xb9f8,0xba09, + 0xba17,0xbb0f,0xc54c,0xc6f9,0xc954,0xc9ce,0xd70b,0xd719,0xdc55, + 0xdf0b,0xe1c4,0xe556,0xe589,0xea5d + }; + + +//============================================================================== +// Saving & Loading Seeds +//============================================================================== + + +int64_t *loadSavedSeeds(const char *fnam, int64_t *scnt) +{ + FILE *fp = fopen(fnam, "r"); + + int64_t seed; + int64_t *baseSeeds; + + if (fp == NULL) + { + perror("ERR loadSavedSeeds: "); + return NULL; + } + + *scnt = 0; + + while (!feof(fp)) + { + if (fscanf(fp, "%" PRId64, &seed) == 1) (*scnt)++; + else while (!feof(fp) && fgetc(fp) != '\n'); + } + + baseSeeds = (int64_t*) calloc(*scnt, sizeof(*baseSeeds)); + + rewind(fp); + + for (int64_t i = 0; i < *scnt && !feof(fp);) + { + if (fscanf(fp, "%" PRId64, &baseSeeds[i]) == 1) i++; + else while (!feof(fp) && fgetc(fp) != '\n'); + } + + fclose(fp); + + return baseSeeds; +} + + +//============================================================================== +// Multi-Structure Checks +//============================================================================== + +int isQuadFeatureBase(const StructureConfig sconf, const int64_t seed, const int qual) +{ + // seed offsets for the regions (0,0) to (1,1) + const int64_t reg00base = sconf.seed; + const int64_t reg01base = 341873128712 + sconf.seed; + const int64_t reg10base = 132897987541 + sconf.seed; + const int64_t reg11base = 341873128712 + 132897987541 + sconf.seed; + + const int range = sconf.chunkRange; + const int upper = range - qual - 1; + const int lower = qual; + + int64_t s; + + s = (reg00base + seed) ^ 0x5deece66dLL; // & 0xffffffffffff; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + if ((int)(s >> 17) % range < upper) return 0; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + if ((int)(s >> 17) % range < upper) return 0; + + s = (reg01base + seed) ^ 0x5deece66dLL; // & 0xffffffffffff; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + if ((int)(s >> 17) % range > lower) return 0; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + if ((int)(s >> 17) % range < upper) return 0; + + s = (reg10base + seed) ^ 0x5deece66dLL; // & 0xffffffffffff; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + if ((int)(s >> 17) % range < upper) return 0; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + if ((int)(s >> 17) % range > lower) return 0; + + s = (reg11base + seed) ^ 0x5deece66dLL; // & 0xffffffffffff; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + if ((int)(s >> 17) % range > lower) return 0; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + if ((int)(s >> 17) % range > lower) return 0; + + return 1; +} + + +int isTriFeatureBase(const StructureConfig sconf, const int64_t seed, const int qual) +{ + // seed offsets for the regions (0,0) to (1,1) + const int64_t reg00base = sconf.seed; + const int64_t reg01base = 341873128712 + sconf.seed; + const int64_t reg10base = 132897987541 + sconf.seed; + const int64_t reg11base = 341873128712 + 132897987541 + sconf.seed; + + const int range = sconf.chunkRange; + const int upper = range - qual - 1; + const int lower = qual; + + int64_t s; + int missing = 0; + + s = (reg00base + seed) ^ 0x5deece66dLL; // & 0xffffffffffff; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + if ((int)(s >> 17) % range < upper || + (int)(((s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff) >> 17) % range < upper) + { + missing++; + } + + s = (reg01base + seed) ^ 0x5deece66dLL; // & 0xffffffffffff; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + if ((int)(s >> 17) % range > lower || + (int)(((s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff) >> 17) % range < upper) + { + if (missing) return 0; + missing++; + } + + s = (reg10base + seed) ^ 0x5deece66dLL; // & 0xffffffffffff; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + if ((int)(s >> 17) % range < upper || + (int)(((s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff) >> 17) % range > lower) + { + if (missing) return 0; + missing++; + } + + s = (reg11base + seed) ^ 0x5deece66dLL; // & 0xffffffffffff; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + if ((int)(s >> 17) % range > lower || + (int)(((s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff) >> 17) % range > lower) + { + if (missing) return 0; + } + + return 1; +} + + +int isLargeQuadBase(const StructureConfig sconf, const int64_t seed, const int qual) +{ + // seed offsets for the regions (0,0) to (1,1) + const int64_t reg00base = sconf.seed; + const int64_t reg01base = 341873128712 + sconf.seed; + const int64_t reg10base = 132897987541 + sconf.seed; + const int64_t reg11base = 341873128712 + 132897987541 + sconf.seed; + + const int range = sconf.chunkRange; + const int rmin1 = range - 1; + + int64_t s; + int p; + + s = (reg00base + seed) ^ 0x5deece66dLL; // & 0xffffffffffff; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p = (int)(s >> 17) % range; + if (p < rmin1-qual) return 0; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p += (int)(s >> 17) % range; + if (p < 2*rmin1-qual) return 0; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p = (int)(s >> 17) % range; + if (p < rmin1-qual) return 0; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p += (int)(s >> 17) % range; + if (p < 2*rmin1-qual) return 0; + + s = (reg01base + seed) ^ 0x5deece66dLL; // & 0xffffffffffff; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p = (int)(s >> 17) % range; + if (p > qual) return 0; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p += (int)(s >> 17) % range; + if (p > qual) return 0; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p = (int)(s >> 17) % range; + if (p < rmin1-qual) return 0; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p += (int)(s >> 17) % range; + if (p < 2*rmin1-qual) return 0; + + s = (reg10base + seed) ^ 0x5deece66dLL; // & 0xffffffffffff; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p = (int)(s >> 17) % range; + if (p < rmin1-qual) return 0; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p += (int)(s >> 17) % range; + if (p < 2*rmin1-qual) return 0; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p = (int)(s >> 17) % range; + if (p > qual) return 0; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p += (int)(s >> 17) % range; + if (p > qual) return 0; + + s = (reg11base + seed) ^ 0x5deece66dLL; // & 0xffffffffffff; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p = (int)(s >> 17) % range; + if (p > qual) return 0; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p += (int)(s >> 17) % range; + if (p > qual) return 0; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p = (int)(s >> 17) % range; + if (p > qual) return 0; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p += (int)(s >> 17) % range; + if (p > qual) return 0; + + return 1; +} + + +int isLargeTriBase(const StructureConfig sconf, const int64_t seed, const int qual) +{ + // seed offsets for the regions (0,0) to (1,1) + const int64_t reg00base = sconf.seed; + const int64_t reg01base = 341873128712 + sconf.seed; + const int64_t reg10base = 132897987541 + sconf.seed; + const int64_t reg11base = 341873128712 + 132897987541 + sconf.seed; + + const int range = sconf.chunkRange; + const int rmin1 = range - 1; + + int64_t s; + int p; + + int incomplete = 0; + + s = (reg00base + seed) ^ 0x5deece66dLL; // & 0xffffffffffff; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p = (int)(s >> 17) % range; + if (p < rmin1-qual) goto incomp11; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p += (int)(s >> 17) % range; + if (p < 2*rmin1-qual) goto incomp11; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p = (int)(s >> 17) % range; + if (p < rmin1-qual) goto incomp11; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p += (int)(s >> 17) % range; + if (p < 2*rmin1-qual) goto incomp11; + + if (0) + { + incomp11: + incomplete = 1; + } + + s = (reg01base + seed) ^ 0x5deece66dLL; // & 0xffffffffffff; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p = (int)(s >> 17) % range; + if (p > qual) goto incomp01; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p += (int)(s >> 17) % range; + if (p > qual) goto incomp01; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p = (int)(s >> 17) % range; + if (p < rmin1-qual) goto incomp01; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p += (int)(s >> 17) % range; + if (p < 2*rmin1-qual) goto incomp01; + + if (0) + { + incomp01: + if (incomplete) return 0; + incomplete = 2; + } + + s = (reg10base + seed) ^ 0x5deece66dLL; // & 0xffffffffffff; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p = (int)(s >> 17) % range; + if (p < rmin1-qual) goto incomp10; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p += (int)(s >> 17) % range; + if (p < 2*rmin1-qual) goto incomp10; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p = (int)(s >> 17) % range; + if (p > qual) goto incomp10; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p += (int)(s >> 17) % range; + if (p > qual) goto incomp10; + + if (0) + { + incomp10: + if (incomplete) return 0; + incomplete = 3; + } + + s = (reg11base + seed) ^ 0x5deece66dLL; // & 0xffffffffffff; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p = (int)(s >> 17) % range; + if (p > qual) goto incomp00; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p += (int)(s >> 17) % range; + if (p > qual) goto incomp00; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p = (int)(s >> 17) % range; + if (p > qual) goto incomp00; + s = (s * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + p += (int)(s >> 17) % range; + if (p > qual) goto incomp00; + + if (0) + { + incomp00: + if (incomplete) return 0; + incomplete = 4; + } + + return incomplete ? incomplete : -1; +} + + +/* Calls the correct quad-base finder for the structure config, if available. + * (Exits program otherwise.) + */ +int isQuadBase(const StructureConfig sconf, const int64_t seed, const int64_t qual) +{ + switch(sconf.properties) + { + case 0: + return isQuadFeatureBase(sconf, seed, qual); + case LARGE_STRUCT: + return isLargeQuadBase(sconf, seed, qual); + case USE_POW2_RNG: + fprintf(stderr, + "Quad-finder using power of 2 RNG is not implemented yet.\n"); + exit(-1); + case LARGE_STRUCT | USE_POW2_RNG: + fprintf(stderr, + "Quad-finder for large structures using power of 2 RNG" + " is not implemented yet.\n"); + exit(-1); + default: + fprintf(stderr, + "Unknown properties field for structure: 0x%04X\n", + sconf.properties); + exit(-1); + } +} + +/* Calls the correct triple-base finder for the structure config, if available. + * (Exits program otherwise.) + */ +int isTriBase(const StructureConfig sconf, const int64_t seed, const int64_t qual) +{ + switch(sconf.properties) + { + case 0: + return isTriFeatureBase(sconf, seed, qual); + case LARGE_STRUCT: + return isLargeTriBase(sconf, seed, qual); + case USE_POW2_RNG: + fprintf(stderr, + "Quad-finder using power of 2 RNG is not implemented yet.\n"); + exit(-1); + case LARGE_STRUCT | USE_POW2_RNG: + fprintf(stderr, + "Quad-finder for large structures using power of 2 RNG" + " is not implemented yet.\n"); + exit(-1); + default: + fprintf(stderr, + "Unknown properties field for structure: 0x%04X\n", + sconf.properties); + exit(-1); + } +} + + + +/* Searches for the optimal AFK position given four structures at positions 'p', + * each of volume (ax,ay,az). + * + * Returned is the number of spawning spaces within reach. + */ +int countBlocksInSpawnRange(Pos p[4], const int ax, const int ay, const int az) +{ + int minX = 3e7, minZ = 3e7, maxX = -3e7, maxZ = -3e7; + int best; + + + // Find corners + for (int i = 0; i < 4; i++) + { + if (p[i].x < minX) minX = p[i].x; + if (p[i].z < minZ) minZ = p[i].z; + if (p[i].x > maxX) maxX = p[i].x; + if (p[i].z > maxZ) maxZ = p[i].z; + } + + + // assume that the search area is bound by the inner corners + maxX += ax; + maxZ += az; + best = 0; + + double thsq = 128.0*128.0 - az*az/4.0; + + for (int x = minX; x < maxX; x++) + { + for (int z = minZ; z < maxZ; z++) + { + int inrange = 0; + + for (int i = 0; i < 4; i++) + { + double dx = p[i].x - (x+0.5); + double dz = p[i].z - (z+0.5); + + for (int px = 0; px < ax; px++) + { + for (int pz = 0; pz < az; pz++) + { + double ddx = px + dx; + double ddz = pz + dz; + inrange += (ddx*ddx + ddz*ddz <= thsq); + } + } + } + + if (inrange > best) + { + best = inrange; + } + } + } + + return best; +} + +#ifdef USE_PTHREAD +static void *search4QuadBasesThread(void *data) +#else +static DWORD WINAPI search4QuadBasesThread(LPVOID data) +#endif +{ + quad_threadinfo_t info = *(quad_threadinfo_t*)data; + + const int64_t start = info.start; + const int64_t end = info.end; + const int64_t structureSeed = info.sconf.seed; + + int64_t seed; + + int64_t *lowerBits; + int lowerBitsCnt; + int lowerBitsIdx = 0; + int i; + + lowerBits = (int64_t *) malloc(0x10000 * sizeof(int64_t)); + + if (info.quality == 1) + { + lowerBitsCnt = sizeof(lowerBaseBitsQ1) / sizeof(lowerBaseBitsQ1[0]); + for (i = 0; i < lowerBitsCnt; i++) + { + lowerBits[i] = (lowerBaseBitsQ1[i] - structureSeed) & 0xffff; + } + } + else if (info.quality == 2) + { + lowerBitsCnt = sizeof(lowerBaseBitsQ2) / sizeof(lowerBaseBitsQ2[0]); + for (i = 0; i < lowerBitsCnt; i++) + { + lowerBits[i] = (lowerBaseBitsQ2[i] - structureSeed) & 0xffff; + } + } + else + { + printf("WARN search4QuadBasesThread: " + "Lower bits for quality %d have not been defined => " + "will try all combinations.\n", info.quality); + + lowerBitsCnt = 0x10000; + for (i = 0; i < lowerBitsCnt; i++) lowerBits[i] = i; + } + + char fnam[256]; + sprintf(fnam, "%s.part%d", info.fnam, info.threadID); + + FILE *fp = fopen(fnam, "a+"); + if (fp == NULL) + { + fprintf(stderr, "Could not open \"%s\" for writing.\n", fnam); + free(lowerBits); + exit(-1); + } + + seed = start; + + // Check the last entry in the file and use it as a starting point if it + // exists. (I.e. loading the saved progress.) + if (!fseek(fp, -31, SEEK_END)) + { + char buf[32]; + if (fread(buf, 30, 1, fp) > 0) + { + char *last_newline = strrchr(buf, '\n'); + + if (sscanf(last_newline, "%" PRId64, &seed) == 1) + { + while (lowerBits[lowerBitsIdx] <= (seed & 0xffff)) + lowerBitsIdx++; + + seed = (seed & 0x0000ffffffff0000) + lowerBits[lowerBitsIdx]; + + printf("Thread %d starting from: %" PRId64"\n", info.threadID, seed); + } + else + { + seed = start; + } + } + } + + + fseek(fp, 0, SEEK_END); + + + while (seed < end) + { + if (isQuadBase(info.sconf, seed, info.quality)) + { + fprintf(fp, "%" PRId64"\n", seed); + fflush(fp); + //printf("Thread %d: %"PRId64"\n", info.threadID, seed); + } + + lowerBitsIdx++; + if (lowerBitsIdx >= lowerBitsCnt) + { + lowerBitsIdx = 0; + seed += 0x10000; + } + seed = (seed & 0x0000ffffffff0000) + lowerBits[lowerBitsIdx]; + } + + fclose(fp); + free(lowerBits); + +#ifdef USE_PTHREAD + pthread_exit(NULL); +#endif + return 0; +} + + +void search4QuadBases(const char *fnam, const int threads, + const StructureConfig structureConfig, const int quality) +{ + thread_id_t threadID[threads]; + quad_threadinfo_t info[threads]; + int64_t t; + + for (t = 0; t < threads; t++) + { + info[t].threadID = t; + info[t].start = (t * SEED_BASE_MAX / threads) & 0x0000ffffffff0000; + info[t].end = ((info[t].start + (SEED_BASE_MAX-1) / threads) & 0x0000ffffffff0000) + 1; + info[t].fnam = fnam; + info[t].quality = quality; + info[t].sconf = structureConfig; + } + + +#ifdef USE_PTHREAD + + for (t = 0; t < threads; t++) + { + pthread_create(&threadID[t], NULL, search4QuadBasesThread, (void*)&info[t]); + } + + for (t = 0; t < threads; t++) + { + pthread_join(threadID[t], NULL); + } + +#else + + for (t = 0; t < threads; t++) + { + threadID[t] = CreateThread(NULL, 0, search4QuadBasesThread, (LPVOID)&info[t], 0, NULL); + } + + WaitForMultipleObjects(threads, threadID, TRUE, INFINITE); + +#endif + + + + // merge thread parts + + char fnamThread[256]; + char buffer[4097]; + FILE *fp = fopen(fnam, "w"); + if (fp == NULL) { + fprintf(stderr, "Could not open \"%s\" for writing.\n", fnam); + exit(-1); + } + FILE *fpart; + int n; + + for (t = 0; t < threads; t++) + { + sprintf(fnamThread, "%s.part%d", info[t].fnam, info[t].threadID); + + fpart = fopen(fnamThread, "r"); + + if (fpart == NULL) + { + perror("ERR search4QuadBases: "); + break; + } + + while ((n = fread(buffer, sizeof(char), 4096, fpart))) + { + if (!fwrite(buffer, sizeof(char), n, fp)) + { + perror("ERR search4QuadBases: "); + fclose(fp); + fclose(fpart); + return; + } + } + + fclose(fpart); + + remove(fnamThread); + } + + fclose(fp); +} + + +//============================================================================== +// Finding Structure Positions +//============================================================================== + + +Pos getStructurePos(const StructureConfig config, int64_t seed, + const int regionX, const int regionZ) +{ + Pos pos; + + // set seed + seed = regionX*341873128712 + regionZ*132897987541 + seed + config.seed; + seed = (seed ^ 0x5deece66dLL);// & ((1LL << 48) - 1); + + seed = (seed * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + + if (config.properties & USE_POW2_RNG) + { + // Java RNG treats powers of 2 as a special case. + pos.x = (config.chunkRange * (seed >> 17)) >> 31; + + seed = (seed * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + pos.z = (config.chunkRange * (seed >> 17)) >> 31; + } + else + { + pos.x = (int)(seed >> 17) % config.chunkRange; + + seed = (seed * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + pos.z = (int)(seed >> 17) % config.chunkRange; + } + + pos.x = ((regionX*config.regionSize + pos.x) << 4) + 8; + pos.z = ((regionZ*config.regionSize + pos.z) << 4) + 8; + return pos; +} + +Pos getStructureChunkInRegion(const StructureConfig config, int64_t seed, + const int regionX, const int regionZ) +{ + /* + // Vanilla like implementation. + seed = regionX*341873128712 + regionZ*132897987541 + seed + structureSeed; + setSeed(&(seed)); + + Pos pos; + pos.x = nextInt(&seed, 24); + pos.z = nextInt(&seed, 24); + */ + Pos pos; + + seed = regionX*341873128712 + regionZ*132897987541 + seed + config.seed; + seed = (seed ^ 0x5deece66dLL);// & ((1LL << 48) - 1); + + seed = (seed * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + + if (config.properties & USE_POW2_RNG) + { + // Java RNG treats powers of 2 as a special case. + pos.x = (config.chunkRange * (seed >> 17)) >> 31; + + seed = (seed * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + pos.z = (config.chunkRange * (seed >> 17)) >> 31; + } + else + { + pos.x = (int)(seed >> 17) % config.chunkRange; + + seed = (seed * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + pos.z = (int)(seed >> 17) % config.chunkRange; + } + + return pos; +} + + +Pos getLargeStructurePos(StructureConfig config, int64_t seed, + const int regionX, const int regionZ) +{ + Pos pos; + + //TODO: if (config.properties & USE_POW2_RNG)... + + // set seed + seed = regionX*341873128712 + regionZ*132897987541 + seed + config.seed; + seed = (seed ^ 0x5deece66dLL) & ((1LL << 48) - 1); + + seed = (seed * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + pos.x = (seed >> 17) % config.chunkRange; + seed = (seed * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + pos.x += (seed >> 17) % config.chunkRange; + + seed = (seed * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + pos.z = (seed >> 17) % config.chunkRange; + seed = (seed * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + pos.z += (seed >> 17) % config.chunkRange; + + pos.x = regionX*config.regionSize + (pos.x >> 1); + pos.z = regionZ*config.regionSize + (pos.z >> 1); + pos.x = pos.x*16 + 8; + pos.z = pos.z*16 + 8; + return pos; +} + + +Pos getLargeStructureChunkInRegion(StructureConfig config, int64_t seed, + const int regionX, const int regionZ) +{ + Pos pos; + + //TODO: if (config.properties & USE_POW2_RNG)... + + // set seed + seed = regionX*341873128712 + regionZ*132897987541 + seed + config.seed; + seed = (seed ^ 0x5deece66dLL) & ((1LL << 48) - 1); + + seed = (seed * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + pos.x = (seed >> 17) % config.chunkRange; + seed = (seed * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + pos.x += (seed >> 17) % config.chunkRange; + + seed = (seed * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + pos.z = (seed >> 17) % config.chunkRange; + seed = (seed * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + pos.z += (seed >> 17) % config.chunkRange; + + pos.x >>= 1; + pos.z >>= 1; + + return pos; +} + + +//============================================================================== +// Checking Biomes & Biome Helper Functions +//============================================================================== + + +int getBiomeAtPos(const LayerStack g, const Pos pos) +{ + int *map = allocCache(&g.layers[g.layerNum-1], 1, 1); + genArea(&g.layers[g.layerNum-1], map, pos.x, pos.z, 1, 1); + int biomeID = map[0]; + free(map); + return biomeID; +} + +Pos findBiomePosition( + const int mcversion, + const LayerStack g, + int *cache, + const int centerX, + const int centerZ, + const int range, + const int *isValid, + int64_t *seed, + int *passes + ) +{ + int x1 = (centerX-range) >> 2; + int z1 = (centerZ-range) >> 2; + int x2 = (centerX+range) >> 2; + int z2 = (centerZ+range) >> 2; + int width = x2 - x1 + 1; + int height = z2 - z1 + 1; + int *map; + int i, j, found; + + Layer *layer = &g.layers[L_RIVER_MIX_4]; + Pos out; + + if (layer->scale != 4) + { + printf("WARN findBiomePosition: The generator has unexpected scale %d at layer %d.\n", + layer->scale, L_RIVER_MIX_4); + } + + map = cache ? cache : allocCache(layer, width, height); + + genArea(layer, map, x1, z1, width, height); + + out.x = centerX; + out.z = centerZ; + found = 0; + + if (mcversion >= MC_1_13) + { + for (i = 0, j = 2; i < width*height; i++) + { + if (!isValid[map[i] & 0xff]) continue; + if ((found == 0 || nextInt(seed, j++) == 0)) + { + out.x = (x1 + i%width) << 2; + out.z = (z1 + i/width) << 2; + found = 1; + } + } + found = j - 2; + } + else + { + for (i = 0; i < width*height; i++) + { + if (isValid[map[i] & 0xff] && + (found == 0 || nextInt(seed, found + 1) == 0)) + { + out.x = (x1 + i%width) << 2; + out.z = (z1 + i/width) << 2; + ++found; + } + } + } + + + if (cache == NULL) + { + free(map); + } + + if (passes != NULL) + { + *passes = found; + } + + return out; +} + + +int areBiomesViable( + const LayerStack g, + int * cache, + const int posX, + const int posZ, + const int radius, + const int * isValid + ) +{ + int x1 = (posX - radius) >> 2; + int z1 = (posZ - radius) >> 2; + int x2 = (posX + radius) >> 2; + int z2 = (posZ + radius) >> 2; + int width = x2 - x1 + 1; + int height = z2 - z1 + 1; + int i; + int *map; + + Layer *layer = &g.layers[L_RIVER_MIX_4]; + + if (layer->scale != 4) + { + printf("WARN areBiomesViable: The generator has unexpected scale %d at layer %d.\n", + layer->scale, L_RIVER_MIX_4); + } + + map = cache ? cache : allocCache(layer, width, height); + genArea(layer, map, x1, z1, width, height); + + for (i = 0; i < width*height; i++) + { + if (!isValid[ map[i] & 0xff ]) + { + if (cache == NULL) free(map); + return 0; + } + } + + if (cache == NULL) free(map); + return 1; +} + + +int getBiomeRadius( + const int * map, + const int mapSide, + const int * biomes, + const int bnum, + const int ignoreMutations) +{ + int r, i, b; + int blist[0x100]; + int mask = ignoreMutations ? 0x7f : 0xff; + int radiusMax = mapSide / 2; + + if ((mapSide & 1) == 0) + { + printf("WARN getBiomeRadius: Side length of the square map should be an odd integer.\n"); + } + + memset(blist, 0, sizeof(blist)); + + for (r = 1; r < radiusMax; r++) + { + for (i = radiusMax-r; i <= radiusMax+r; i++) + { + blist[ map[(radiusMax-r) * mapSide+ i] & mask ] = 1; + blist[ map[(radiusMax+r-1) * mapSide + i] & mask ] = 1; + blist[ map[mapSide*i + (radiusMax-r)] & mask ] = 1; + blist[ map[mapSide*i + (radiusMax+r-1)] & mask ] = 1; + } + + for (b = 0; b < bnum && blist[biomes[b] & mask]; b++); + if (b >= bnum) + { + break; + } + } + + return r != radiusMax ? r : -1; +} + + + +//============================================================================== +// Finding Strongholds and Spawn +//============================================================================== + + +int* getValidStrongholdBiomes() +{ + static int validStrongholdBiomes[256]; + + if (!validStrongholdBiomes[plains]) + { + int id; + for (id = 0; id < 256; id++) + { + if (biomeExists(id) && biomes[id].height > 0.0) + validStrongholdBiomes[id] = 1; + } + } + + return validStrongholdBiomes; +} + + +int findStrongholds(const int mcversion, LayerStack *g, int *cache, + Pos *locations, int64_t worldSeed, int maxSH, const int maxRadius) +{ + const int *validStrongholdBiomes = getValidStrongholdBiomes(); + int i, x, z; + double distance; + + int currentRing = 0; + int currentCount = 0; + int perRing = 3; + + setSeed(&worldSeed); + double angle = nextDouble(&worldSeed) * PI * 2.0; + + if (mcversion >= MC_1_9) + { + if (maxSH <= 0) maxSH = 128; + + for (i = 0; i < maxSH; i++) + { + distance = (4.0 * 32.0) + (6.0 * currentRing * 32.0) + + (nextDouble(&worldSeed) - 0.5) * 32 * 2.5; + + if (maxRadius && distance*16 > maxRadius) + return i; + + x = (int)round(cos(angle) * distance); + z = (int)round(sin(angle) * distance); + + locations[i] = findBiomePosition(mcversion, *g, cache, + (x << 4) + 8, (z << 4) + 8, 112, validStrongholdBiomes, + &worldSeed, NULL); + + angle += 2 * PI / perRing; + + currentCount++; + if (currentCount == perRing) + { + // Current ring is complete, move to next ring. + currentRing++; + currentCount = 0; + perRing = perRing + 2*perRing/(currentRing+1); + if (perRing > 128-i) + perRing = 128-i; + angle = angle + nextDouble(&worldSeed) * PI * 2.0; + } + } + } + else + { + if (maxSH <= 0) maxSH = 3; + + for (i = 0; i < maxSH; i++) + { + distance = (1.25 + nextDouble(&worldSeed)) * 32.0; + + if (maxRadius && distance*16 > maxRadius) + return i; + + x = (int)round(cos(angle) * distance); + z = (int)round(sin(angle) * distance); + + locations[i] = findBiomePosition(mcversion, *g, cache, + (x << 4) + 8, (z << 4) + 8, 112, validStrongholdBiomes, + &worldSeed, NULL); + + angle += 2 * PI / 3.0; + } + } + + return maxSH; +} + + +static double getGrassProbability(int64_t seed, int biome, int x, int z) +{ + // TODO: Use ChunkGeneratorOverworld.generateHeightmap for better estimate. + // TODO: Try to determine the actual probabilities and build a statistic. + switch (biome) + { + case plains: return 1.0; + case extremeHills: return 0.8; // height dependent + case forest: return 1.0; + case taiga: return 1.0; + case swampland: return 0.6; // height dependent + case river: return 0.2; + case beach: return 0.1; + case forestHills: return 1.0; + case taigaHills: return 1.0; + case extremeHillsEdge: return 1.0; // height dependent + case jungle: return 1.0; + case jungleHills: return 1.0; + case jungleEdge: return 1.0; + case birchForest: return 1.0; + case birchForestHills: return 1.0; + case roofedForest: return 0.9; + case coldTaiga: return 0.1; // below trees + case coldTaigaHills: return 0.1; // below trees + case megaTaiga: return 0.6; + case megaTaigaHills: return 0.6; + case extremeHillsPlus: return 0.2; // height dependent + case savanna: return 1.0; + case savannaPlateau: return 1.0; + case mesaPlateau_F: return 0.1; // height dependent + case mesaPlateau: return 0.1; // height dependent + // NOTE: in rare circumstances you can get also get grass islands that are + // completely ocean variants... + default: return 0; + } +} + +static int canCoordinateBeSpawn(const int64_t seed, LayerStack *g, int *cache, Pos pos) +{ + int biome = getBiomeAtPos(*g, pos); + return getGrassProbability(seed, biome, pos.x, pos.z) >= 0.5; +} + +static int* getValidSpawnBiomes() +{ + static int isSpawnBiome[256]; + unsigned int i; + + if (!isSpawnBiome[biomesToSpawnIn[0]]) + { + for (i = 0; i < sizeof(biomesToSpawnIn) / sizeof(int); i++) + { + isSpawnBiome[ biomesToSpawnIn[i] ] = 1; + } + } + + return isSpawnBiome; +} + + +Pos getSpawn(const int mcversion, LayerStack *g, int *cache, int64_t worldSeed) +{ + const int *isSpawnBiome = getValidSpawnBiomes(); + Pos spawn; + int found; + int i; + + setSeed(&worldSeed); + spawn = findBiomePosition(mcversion, *g, cache, 0, 0, 256, isSpawnBiome, + &worldSeed, &found); + + if (!found) + { + //printf("Unable to find spawn biome.\n"); + spawn.x = spawn.z = 8; + } + + if (mcversion >= MC_1_13) + { + // TODO: The 1.13 section may need further checking! + int n2 = 0; + int n3 = 0; + int n4 = 0; + int n5 = -1; + + for (i = 0; i < 1024; i++) + { + if (n2 > -16 && n2 <= 16 && n3 > -16 && n3 <= 16) + { + int cx = ((spawn.x >> 4) + n2) << 4; + int cz = ((spawn.z >> 4) + n3) << 4; + + for (int i2 = cx; i2 <= cx+15; i2++) + { + for (int i3 = cz; i3 <= cz+15; i3++) + { + Pos pos = {i2, i3}; + if (canCoordinateBeSpawn(worldSeed, g, cache, pos)) + { + return pos; + } + } + } + } + + if (n2 == n3 || (n2 < 0 && n2 == - n3) || (n2 > 0 && n2 == 1 - n3)) + { + int n7 = n4; + n4 = - n5; + n5 = n7; + } + n2 += n4; + n3 += n5; + } + } + else + { + for (i = 0; i < 1000 && !canCoordinateBeSpawn(worldSeed, g, cache, spawn); i++) + { + spawn.x += nextInt(&worldSeed, 64) - nextInt(&worldSeed, 64); + spawn.z += nextInt(&worldSeed, 64) - nextInt(&worldSeed, 64); + } + } + + return spawn; +} + + +Pos estimateSpawn(const int mcversion, LayerStack *g, int *cache, int64_t worldSeed) +{ + const int *isSpawnBiome = getValidSpawnBiomes(); + Pos spawn; + int found; + + setSeed(&worldSeed); + spawn = findBiomePosition(mcversion, *g, cache, 0, 0, 256, isSpawnBiome, + &worldSeed, &found); + + if (!found) + { + spawn.x = spawn.z = 8; + } + + return spawn; +} + + + +//============================================================================== +// Validating Structure Positions +//============================================================================== + + +int isViableFeaturePos(const int structureType, const LayerStack g, int *cache, + const int blockX, const int blockZ) +{ + int *map = cache ? cache : allocCache(&g.layers[g.layerNum-1], 1, 1); + genArea(&g.layers[g.layerNum-1], map, blockX, blockZ, 1, 1); + int biomeID = map[0]; + if (!cache) free(map); + + switch(structureType) + { + case Desert_Pyramid: + return biomeID == desert || biomeID == desertHills; + case Igloo: + return biomeID == icePlains || biomeID == coldTaiga; + case Jungle_Pyramid: + return biomeID == jungle || biomeID == jungleHills; + case Swamp_Hut: + return biomeID == swampland; + case Ocean_Ruin: + case Shipwreck: + return isOceanic(biomeID); + default: + fprintf(stderr, "Structure type is not valid for the scattered feature biome check.\n"); + exit(1); + } +} + +int isViableVillagePos(const LayerStack g, int *cache, + const int blockX, const int blockZ) +{ + static int isVillageBiome[0x100]; + + if (!isVillageBiome[villageBiomeList[0]]) + { + unsigned int i; + for (i = 0; i < sizeof(villageBiomeList) / sizeof(int); i++) + { + isVillageBiome[ villageBiomeList[i] ] = 1; + } + } + + return areBiomesViable(g, cache, blockX, blockZ, 0, isVillageBiome); +} + +int isViableOceanMonumentPos(const LayerStack g, int *cache, + const int blockX, const int blockZ) +{ + static int isWaterBiome[0x100]; + static int isDeepOcean[0x100]; + + if (!isWaterBiome[oceanMonumentBiomeList1[1]]) + { + unsigned int i; + for (i = 0; i < sizeof(oceanMonumentBiomeList1) / sizeof(int); i++) + { + isWaterBiome[ oceanMonumentBiomeList1[i] ] = 1; + } + + for (i = 0; i < sizeof(oceanMonumentBiomeList2) / sizeof(int); i++) + { + isDeepOcean[ oceanMonumentBiomeList2[i] ] = 1; + } + } + + return areBiomesViable(g, cache, blockX, blockZ, 16, isDeepOcean) && + areBiomesViable(g, cache, blockX, blockZ, 29, isWaterBiome); +} + +int isViableMansionPos(const LayerStack g, int *cache, + const int blockX, const int blockZ) +{ + static int isMansionBiome[0x100]; + + if (!isMansionBiome[mansionBiomeList[0]]) + { + unsigned int i; + for (i = 0; i < sizeof(mansionBiomeList) / sizeof(int); i++) + { + isMansionBiome[ mansionBiomeList[i] ] = 1; + } + } + + return areBiomesViable(g, cache, blockX, blockZ, 32, isMansionBiome); +} + + + + +//============================================================================== +// Finding Properties of Structures +//============================================================================== + + +int isZombieVillage(const int mcversion, const int64_t worldSeed, + const int regionX, const int regionZ) +{ + Pos pos; + int64_t seed = worldSeed; + + if (mcversion < MC_1_10) + { + printf("Warning: Zombie villages were only introduced in MC 1.10.\n"); + } + + // get the chunk position of the village + seed = regionX*341873128712 + regionZ*132897987541 + seed + VILLAGE_CONFIG.seed; + seed = (seed ^ 0x5deece66dLL);// & ((1LL << 48) - 1); + + seed = (seed * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + pos.x = (seed >> 17) % VILLAGE_CONFIG.chunkRange; + + seed = (seed * 0x5deece66dLL + 0xbLL) & 0xffffffffffff; + pos.z = (seed >> 17) % VILLAGE_CONFIG.chunkRange; + + pos.x += regionX * VILLAGE_CONFIG.regionSize; + pos.z += regionZ * VILLAGE_CONFIG.regionSize; + + // jump to the random number check that determines whether this is village + // is zombie infested + int64_t rnd = chunkGenerateRnd(worldSeed, pos.x , pos.z); + // TODO: check for versions <= 1.11 + skipNextN(&rnd, mcversion >= MC_1_13 ? 10 : 11); + + return nextInt(&rnd, 50) == 0; +} + + +int isBabyZombieVillage(const int mcversion, const int64_t worldSeed, + const int regionX, const int regionZ) +{ + if (!isZombieVillage(mcversion, worldSeed, regionX, regionZ)) + return 0; + + // Whether the zombie is a child or not is dependent on the world random + // object which is not reset for villages. The last reset is instead + // performed during the positioning of Mansions. + int64_t rnd = worldSeed; + rnd = regionX*341873128712 + regionZ*132897987541 + rnd + MANSION_CONFIG.seed; + setSeed(&rnd); + skipNextN(&rnd, 5); + + int isChild = nextFloat(&rnd) < 0.05; + //int mountNearbyChicken = nextFloat(&rnd) < 0.05; + //int spawnNewChicken = nextFloat(&rnd) < 0.05; + + return isChild; +} + + +int64_t getHouseList(const int64_t worldSeed, const int chunkX, const int chunkZ, + int *out) +{ + int64_t rnd = chunkGenerateRnd(worldSeed, chunkX, chunkZ); + skipNextN(&rnd, 1); + + out[HouseSmall] = nextInt(&rnd, 4 - 2 + 1) + 2; + out[Church] = nextInt(&rnd, 1 - 0 + 1) + 0; + out[Library] = nextInt(&rnd, 2 - 0 + 1) + 0; + out[WoodHut] = nextInt(&rnd, 5 - 2 + 1) + 2; + out[Butcher] = nextInt(&rnd, 2 - 0 + 1) + 0; + out[FarmLarge] = nextInt(&rnd, 4 - 1 + 1) + 1; + out[FarmSmall] = nextInt(&rnd, 4 - 2 + 1) + 2; + out[Blacksmith] = nextInt(&rnd, 1 - 0 + 1) + 0; + out[HouseLarge] = nextInt(&rnd, 3 - 0 + 1) + 0; + + return rnd; +} + +//============================================================================== +// Seed Filters +//============================================================================== + + +int64_t filterAllTempCats( + LayerStack * g, + int * cache, + const int64_t * seedsIn, + int64_t * seedsOut, + const int64_t seedCnt, + const int centX, + const int centZ) +{ + /* We require all temperature categories, including the special variations + * in order to get all main biomes. This gives 8 required values: + * Oceanic, Warm, Lush, Cold, Freezing, + * Special Warm, Special Lush, Special Cold + * These categories generate at Layer 13: Edge, Special. + * + * Note: The scale at this layer is 1:1024 and each element can "leak" its + * biome values up to 1024 blocks outwards into the negative coordinates + * (due to the Zoom layers). + * + * The plan is to check if the 3x3 area contains all 8 temperature types. + * For this, we can check even earlier at Layer 10: Add Island, that each of + * the Warm, Cold and Freezing categories are present. + */ + + /* Edit: + * All the biomes that are generated by a simple Cold climate can actually + * be generated later on. So I have commented out the Cold requirements. + */ + + const int pX = centX-1, pZ = centZ-1; + const int sX = 3, sZ = 3; + int *map; + + Layer *lFilterSnow = &g->layers[L_ADD_SNOW_1024]; + Layer *lFilterSpecial = &g->layers[L_SPECIAL_1024]; + + map = cache ? cache : allocCache(lFilterSpecial, sX, sZ); + + // Construct a dummy Edge,Special layer. + Layer layerSpecial; + setupLayer(1024, &layerSpecial, NULL, 3, NULL); + + int64_t sidx, hits, seed; + int types[9]; + int specialCnt; + int i, j; + + hits = 0; + + for (sidx = 0; sidx < seedCnt; sidx++) + { + seed = seedsIn[sidx]; + + /*** Pre-Generation Checks ***/ + + // We require at least 3 special temperature categories which can be + // tested for without going through the previous layers. (We'll get + // false positives due to Oceans, but this works fine to rule out some + // seeds early on.) + setWorldSeed(&layerSpecial, seed); + specialCnt = 0; + for (i = 0; i < sX; i++) + { + for (j = 0; j < sZ; j++) + { + setChunkSeed(&layerSpecial, (int64_t)(i+pX), (int64_t)(j+pZ)); + if (mcNextInt(&layerSpecial, 13) == 0) + specialCnt++; + } + } + + if (specialCnt < 3) + { + continue; + } + + /*** Cold/Warm Check ***/ + + // Continue by checking if enough cold and warm categories are present. + setWorldSeed(lFilterSnow, seed); + genArea(lFilterSnow, map, pX,pZ, sX,sZ); + + memset(types, 0, sizeof(types)); + for (i = 0; i < sX*sZ; i++) + types[map[i]]++; + + // 1xOcean needs to be present + // 4xWarm need to turn into Warm, Lush, Special Warm and Special Lush + // 1xFreezing that needs to stay Freezing + // 3x(Cold + Freezing) for Cold, Special Cold and Freezing + if ( types[Ocean] < 1 || types[Warm] < 4 || types[Freezing] < 1 || + types[Cold]+types[Freezing] < 2) + { + continue; + } + + /*** Complete Temperature Category Check ***/ + + // Check that all temperature variants are present. + setWorldSeed(lFilterSpecial, seed); + genArea(lFilterSpecial, map, pX,pZ, sX,sZ); + + memset(types, 0, sizeof(types)); + for (i = 0; i < sX*sZ; i++) + types[ map[i] > 4 ? (map[i]&0xf) + 4 : map[i] ]++; + + if ( types[Ocean] < 1 || types[Warm] < 1 || types[Lush] < 1 || + /*types[Cold] < 1 ||*/ types[Freezing] < 1 || + types[Warm+4] < 1 || types[Lush+4] < 1 || types[Cold+4] < 1) + { + continue; + } + + /* + for (i = 0; i < sX*sZ; i++) + { + printf("%c%d ", " s"[cache[i] > 4], cache[i]&0xf); + if (i % sX == sX-1) printf("\n"); + } + printf("\n");*/ + + // Save the candidate. + seedsOut[hits] = seed; + hits++; + } + + if (cache == NULL) free(map); + return hits; +} + + +const int majorBiomes[] = { + ocean, plains, desert, extremeHills, forest, taiga, swampland, + icePlains, mushroomIsland, jungle, deepOcean, birchForest, roofedForest, + coldTaiga, megaTaiga, savanna, mesaPlateau_F, mesaPlateau +}; + +int64_t filterAllMajorBiomes( + LayerStack * g, + int * cache, + const int64_t * seedsIn, + int64_t * seedsOut, + const int64_t seedCnt, + const int pX, + const int pZ, + const unsigned int sX, + const unsigned int sZ) +{ + Layer *lFilterMushroom = &g->layers[L_ADD_MUSHROOM_ISLAND_256]; + Layer *lFilterBiomes = &g->layers[L_BIOME_256]; + + int *map; + int64_t sidx, seed, hits; + unsigned int i, id, hasAll; + + int types[BIOME_NUM]; + + map = cache ? cache : allocCache(lFilterBiomes, sX, sZ); + + hits = 0; + + for (sidx = 0; sidx < seedCnt; sidx++) + { + /* We can use the Mushroom layer both to check for mushroomIsland biomes + * and to make sure all temperature categories are present in the area. + */ + seed = seedsIn[sidx]; + setWorldSeed(lFilterMushroom, seed); + genArea(lFilterMushroom, map, pX,pZ, sX,sZ); + + memset(types, 0, sizeof(types)); + for (i = 0; i < sX*sZ; i++) + { + id = map[i]; + if (id >= BIOME_NUM) id = (id & 0xf) + 4; + types[id]++; + } + + if ( types[Ocean] < 1 || types[Warm] < 1 || types[Lush] < 1 || + /* types[Cold] < 1 || */ types[Freezing] < 1 || + types[Warm+4] < 1 || types[Lush+4] < 1 || types[Cold+4] < 1 || + types[mushroomIsland] < 1) + { + continue; + } + + /*** Find all major biomes ***/ + + setWorldSeed(lFilterBiomes, seed); + genArea(lFilterBiomes, map, pX,pZ, sX,sZ); + + memset(types, 0, sizeof(types)); + for (i = 0; i < sX*sZ; i++) + { + types[map[i]]++; + } + + hasAll = 1; + for (i = 0; i < sizeof(majorBiomes) / sizeof(*majorBiomes); i++) + { + // plains, taiga and deepOcean can be generated in later layers. + // Also small islands of Forests can be generated in deepOcean + // biomes, but we are going to ignore those. + if (majorBiomes[i] == plains || + majorBiomes[i] == taiga || + majorBiomes[i] == deepOcean) + { + continue; + } + + if (types[majorBiomes[i]] < 1) + { + hasAll = 0; + break; + } + } + if (!hasAll) + { + continue; + } + + seedsOut[hits] = seed; + hits++; + } + + if (cache == NULL) free(map); + return hits; +} + + + +BiomeFilter setupBiomeFilter(const int *biomeList, int listLen) +{ + BiomeFilter bf; + int i, id; + + memset(&bf, 0, sizeof(bf)); + + for (i = 0; i < listLen; i++) + { + id = biomeList[i] & 0x7f; + switch (id) + { + case mushroomIsland: + case mushroomIslandShore: + bf.requireMushroom = 1; + bf.tempCat |= (1 << Oceanic); + bf.biomesToFind |= (1ULL << id); + case mesa: + case mesaPlateau_F: + case mesaPlateau: + bf.tempCat |= (1ULL << (Warm+Special)); + bf.biomesToFind |= (1ULL << id); + break; + case savanna: + case savannaPlateau: + bf.tempCat |= (1ULL << Warm); + bf.biomesToFind |= (1ULL << id); + break; + case roofedForest: + case birchForest: + case birchForestHills: + case swampland: + bf.tempCat |= (1ULL << Lush); + bf.biomesToFind |= (1ULL << id); + break; + case jungle: + case jungleHills: + bf.tempCat |= (1ULL << (Lush+Special)); + bf.biomesToFind |= (1ULL << id); + break; + /*case jungleEdge: + bf.tempCat |= (1ULL << Lush) | (1ULL << Lush+4); + bf.biomesToFind |= (1ULL << id); + break;*/ + case megaTaiga: + case megaTaigaHills: + bf.tempCat |= (1ULL << (Cold+Special)); + bf.biomesToFind |= (1ULL << id); + break; + case icePlains: + case iceMountains: + case coldTaiga: + case coldTaigaHills: + bf.tempCat |= (1ULL << Freezing); + bf.biomesToFind |= (1ULL << id); + break; + default: + bf.biomesToFind |= (1ULL << id); + + if (isOceanic(id)) + { + if (id != ocean && id != deepOcean) + bf.doOceanTypeCheck = 1; + + if (isShallowOcean(id)) + { + bf.oceansToFind |= (1ULL < id); + } + else + { + if (id == warmDeepOcean) + bf.oceansToFind |= (1ULL << warmOcean); + else if (id == lukewarmDeepOcean) + bf.oceansToFind |= (1ULL << lukewarmOcean); + else if (id == deepOcean) + bf.oceansToFind |= (1ULL << ocean); + else if (id == coldDeepOcean) + bf.oceansToFind |= (1ULL << coldOcean); + else if (id == frozenDeepOcean) + bf.oceansToFind |= (1ULL << frozenOcean); + } + bf.tempCat |= (1ULL << Oceanic); + } + else + { + bf.biomesToFind |= (1ULL << id); + } + break; + } + } + + for (i = 0; i < Special; i++) + { + if (bf.tempCat & (1ULL << i)) bf.tempNormal++; + } + for (i = Special; i < Freezing+Special; i++) + { + if (bf.tempCat & (1ULL << i)) bf.tempSpecial++; + } + + bf.doTempCheck = (bf.tempSpecial + bf.tempNormal) >= 6; + bf.doShroomAndTempCheck = bf.requireMushroom && (bf.tempSpecial >= 1 || bf.tempNormal >= 4); + bf.doMajorBiomeCheck = 1; + bf.checkBiomePotential = 1; + bf.doScale4Check = 1; + + return bf; +} + + + +/* Tries to determine if the biomes configured in the filter will generate in + * this seed within the specified area. The smallest layer scale checked is + * given by 'minscale'. Lowering this value terminate the search earlier and + * yield more false positives. + */ +int64_t checkForBiomes( + LayerStack * g, + int * cache, + const int64_t seed, + const int blockX, + const int blockZ, + const unsigned int width, + const unsigned int height, + const BiomeFilter filter, + const int minscale) +{ + Layer *lspecial = &g->layers[L_SPECIAL_1024]; + Layer *lmushroom = &g->layers[L_ADD_MUSHROOM_ISLAND_256]; + Layer *lbiomes = &g->layers[L_BIOME_256]; + Layer *loceantemp = NULL; + + int *map = cache ? cache : allocCache(&g->layers[g->layerNum-1], width, height); + + uint64_t potential, required, modified; + int64_t ss, cs; + int id, types[0x100]; + int i, x, z; + int areaX1024, areaZ1024, areaWidth1024, areaHeight1024; + int areaX256, areaZ256, areaWidth256, areaHeight256; + int areaX4, areaZ4, areaWidth4, areaHeight4; + + // 1:1024 scale + areaX1024 = blockX >> 10; + areaZ1024 = blockZ >> 10; + areaWidth1024 = ((width-1) >> 10) + 2; + areaHeight1024 = ((height-1) >> 10) + 2; + + // 1:256 scale + areaX256 = blockX >> 8; + areaZ256 = blockZ >> 8; + areaWidth256 = ((width-1) >> 8) + 2; + areaHeight256 = ((height-1) >> 8) + 2; + + + /*** BIOME CHECKS THAT DON'T NEED OTHER LAYERS ***/ + + // Check that there is the necessary minimum of both special and normal + // temperature categories present. + if (filter.tempNormal || filter.tempSpecial) + { + ss = processWorldSeed(seed, lspecial->baseSeed); + + types[0] = types[1] = 0; + for (z = 0; z < areaHeight1024; z++) + { + for (x = 0; x < areaWidth1024; x++) + { + cs = getChunkSeed(ss, (int64_t)(x + areaX1024), (int64_t)(z + areaZ1024)); + types[(cs >> 24) % 13 == 0]++; + } + } + + if (types[0] < filter.tempNormal || types[1] < filter.tempSpecial) + { + goto return_zero; + } + } + + // Check there is a mushroom island, provided there is an ocean. + if (filter.requireMushroom) + { + ss = processWorldSeed(seed, lmushroom->baseSeed); + + for (z = 0; z < areaHeight256; z++) + { + for (x = 0; x < areaWidth256; x++) + { + cs = getChunkSeed(ss, (int64_t)(x + areaX256), (int64_t)(z + areaZ256)); + if ((cs >> 24) % 100 == 0) + { + goto after_protomushroom; + } + } + } + + goto return_zero; + } + after_protomushroom: + + if (filter.checkBiomePotential) + { + ss = processWorldSeed(seed, lbiomes->baseSeed); + + potential = 0; + required = filter.biomesToFind & ( + (1ULL << mesaPlateau) | (1ULL << mesaPlateau_F) | + (1ULL << savanna) | (1ULL << roofedForest) | + (1ULL << birchForest) | (1ULL << swampland) ); + + for (z = 0; z < areaHeight256; z++) + { + for (x = 0; x < areaWidth256; x++) + { + cs = getChunkSeed(ss, (int64_t)(x + areaX256), (int64_t)(z + areaZ256)); + cs >>= 24; + + int cs6 = cs % 6; + int cs3 = cs6 & 3; + + if (cs3 == 0) potential |= (1ULL << mesaPlateau); + else if (cs3 == 1 || cs3 == 2) potential |= (1ULL << mesaPlateau_F); + + if (cs6 == 1) potential |= (1ULL << roofedForest); + else if (cs6 == 3) potential |= (1ULL << savanna); + else if (cs6 == 4) potential |= (1ULL << savanna) | (1ULL << birchForest); + else if (cs6 == 5) potential |= (1ULL << swampland); + + if (!((potential & required) ^ required)) + { + goto after_protobiome; + } + } + } + + goto return_zero; + } + after_protobiome: + + + /*** BIOME CHECKS ***/ + + if (filter.doTempCheck) + { + setWorldSeed(lspecial, seed); + genArea(lspecial, map, areaX1024, areaZ1024, areaWidth1024, areaHeight1024); + + potential = 0; + + for (i = 0; i < areaWidth1024 * areaHeight1024; i++) + { + id = map[i]; + if (id >= Special) id = (id & 0xf) + Special; + potential |= (1ULL << id); + } + + if ((potential & filter.tempCat) ^ filter.tempCat) + { + goto return_zero; + } + } + + if (minscale > 256) goto return_one; + + if (filter.doShroomAndTempCheck) + { + setWorldSeed(lmushroom, seed); + genArea(lmushroom, map, areaX256, areaZ256, areaWidth256, areaHeight256); + + potential = 0; + + for (i = 0; i < areaWidth256 * areaHeight256; i++) + { + id = map[i]; + if (id >= BIOME_NUM) id = (id & 0xf) + Special; + potential |= (1ULL << id); + } + + required = filter.tempCat | (1ULL << mushroomIsland); + + if ((potential & required) ^ required) + { + goto return_zero; + } + } + + if (filter.doOceanTypeCheck) + { + loceantemp = &g->layers[L13_OCEAN_TEMP_256]; + setWorldSeed(loceantemp, seed); + genArea(loceantemp, map, areaX256, areaZ256, areaWidth256, areaHeight256); + + potential = 0; // ocean potential + + for (i = 0; i < areaWidth256 * areaHeight256; i++) + { + id = map[i]; + if (id == warmOcean) potential |= (1ULL << warmOcean) | (1ULL << lukewarmOcean); + if (id == lukewarmOcean) potential |= (1ULL << lukewarmOcean); + if (id == ocean) potential |= (1ULL << ocean); + if (id == coldOcean) potential |= (1ULL << coldOcean); + if (id == frozenOcean) potential |= (1ULL << frozenOcean) | (1ULL << coldOcean); + } + + if ((potential & filter.oceansToFind) ^ filter.oceansToFind) + { + goto return_zero; + } + } + + if (filter.doMajorBiomeCheck) + { + setWorldSeed(lbiomes, seed); + genArea(lbiomes, map, areaX256, areaZ256, areaWidth256, areaHeight256); + + // get biomes out of the way that we cannot check for at this layer + potential = (1ULL << beach) | (1ULL << stoneBeach) | (1ULL << coldBeach) | + (1ULL << river) | (1ULL << frozenRiver); + + for (i = 0; i < areaWidth256 * areaHeight256; i++) + { + id = map[i]; + switch (id) + { + case mesaPlateau_F: + case mesaPlateau: + potential |= (1ULL << id) | (1ULL << mesa) | (1ULL << desert); break; + case megaTaiga: + potential |= (1ULL << id) | (1ULL << taiga) | (1ULL << taigaHills) | (1ULL << megaTaigaHills); break; + case desert: + potential |= (1ULL << id) | (1ULL << extremeHillsPlus) | (1ULL << desertHills); break; + case swampland: + potential |= (1ULL << id) | (1ULL << jungleEdge) | (1ULL << plains); break; + case forest: + potential |= (1ULL << id) | (1ULL << forestHills); break; + case birchForest: + potential |= (1ULL << id) | (1ULL << birchForestHills); break; + case roofedForest: + potential |= (1ULL << id) | (1ULL << plains); break; + case taiga: + potential |= (1ULL << id) | (1ULL << taigaHills); break; + case coldTaiga: + potential |= (1ULL << id) | (1ULL << coldTaigaHills); break; + case plains: + potential |= (1ULL << id) | (1ULL << forestHills) | (1ULL << forest); break; + case icePlains: + potential |= (1ULL << id) | (1ULL << iceMountains); break; + case jungle: + potential |= (1ULL << id) | (1ULL << jungleHills); break; + case ocean: + potential |= (1ULL << id) | (1ULL << deepOcean); + // TODO: buffer possible ocean types at this location + potential |= (1ULL << frozenOcean) | (1ULL << coldOcean) | (1ULL << lukewarmOcean) | (1ULL << warmOcean); break; + case extremeHills: + potential |= (1ULL << id) | (1ULL << extremeHillsPlus); break; + case savanna: + potential |= (1ULL << id) | (1ULL << savannaPlateau); break; + case deepOcean: + potential |= (1ULL << id) | (1ULL << plains) | (1ULL << forest); + // TODO: buffer possible ocean types at this location + potential |= (1ULL << frozenDeepOcean) | (1ULL << coldDeepOcean) | (1ULL << lukewarmDeepOcean); break; + case mushroomIsland: + potential |= (1ULL << id) | (1ULL << mushroomIslandShore); + default: + potential |= (1ULL << id); + } + } + + if ((potential & filter.biomesToFind) ^ filter.biomesToFind) + { + goto return_zero; + } + } + + // TODO: Do a check at the HILLS layer, scale 1:64 + + if (minscale > 4) goto return_one; + + if (filter.doScale4Check) + { + // 1:4 scale + areaX4 = blockX >> 2; + areaZ4 = blockZ >> 2; + areaWidth4 = ((width-1) >> 2) + 2; + areaHeight4 = ((height-1) >> 2) + 2; + + applySeed(g, seed); + genArea(&g->layers[g->layerNum-2], map, areaX4, areaZ4, areaWidth4, areaHeight4); + + potential = modified = 0; + + for (i = 0; i < areaWidth4 * areaHeight4; i++) + { + id = map[i]; + if (id >= 128) modified |= (1ULL << (id & 0x7f)); + else potential |= (1ULL << id); + } + + required = filter.biomesToFind; + if ((potential & required) ^ required) + { + goto return_zero; + } + if ((modified & filter.modifiedToFind) ^ filter.modifiedToFind) + { + goto return_zero; + } + } + + + int ret; + + // clean up and return + return_one: + ret = 1; + + if (0) + { + return_zero: + ret = 0; + } + + if (cache == NULL) free(map); + + return ret; +} + + + + + + + + + + + + diff --git a/generationByCubitect/finders.hpp b/generationByCubitect/finders.hpp new file mode 100644 index 0000000..67cad0c --- /dev/null +++ b/generationByCubitect/finders.hpp @@ -0,0 +1,555 @@ +#ifndef FINDERS_H_ +#define FINDERS_H_ + +#include "generator.hpp" +#include +#include +#include + +#ifdef _WIN32 +#include + +typedef HANDLE thread_id_t; + +#else +#define USE_PTHREAD +#include + +typedef pthread_t thread_id_t; + +#endif + + +#define SEED_BASE_MAX (1LL << 48) +#define PI 3.141592653589793 + +#define LARGE_STRUCT 1 +#define USE_POW2_RNG 2 + +enum +{ + Desert_Pyramid, Igloo, Jungle_Pyramid, Swamp_Hut, + Village, Ocean_Ruin, Shipwreck, Monument, Mansion +}; + +enum +{ + HouseSmall, Church, Library, WoodHut, Butcher, FarmLarge, FarmSmall, + Blacksmith, HouseLarge, HOUSE_NUM +}; + +STRUCT(StructureConfig) +{ + int64_t seed; + int regionSize, chunkRange; + int properties; +}; + +/* For desert temples, igloos, jungle temples and witch huts prior to 1.13. */ +static const StructureConfig FEATURE_CONFIG = { 14357617, 32, 24, 0}; + +/* 1.13 separated feature seeds by type */ +static const StructureConfig DESERT_PYRAMID_CONFIG = { 14357617, 32, 24, 0}; +static const StructureConfig IGLOO_CONFIG = { 14357618, 32, 24, 0}; +static const StructureConfig JUNGLE_PYRAMID_CONFIG = { 14357619, 32, 24, 0}; +static const StructureConfig SWAMP_HUT_CONFIG = { 14357620, 32, 24, 0}; + +static const StructureConfig VILLAGE_CONFIG = { 10387312, 32, 24, 0}; +static const StructureConfig OCEAN_RUIN_CONFIG = { 14357621, 16, 8, USE_POW2_RNG}; +static const StructureConfig SHIPWRECK_CONFIG = {165745295, 15, 7, 0}; +static const StructureConfig MONUMENT_CONFIG = { 10387313, 32, 27, LARGE_STRUCT}; +static const StructureConfig MANSION_CONFIG = { 10387319, 80, 60, LARGE_STRUCT}; + +//============================================================================== +// Biome Tables +//============================================================================== + +static const int templeBiomeList[] = {desert, desertHills, jungle, jungleHills, swampland, icePlains, coldTaiga}; +static const int biomesToSpawnIn[] = {forest, plains, taiga, taigaHills, forestHills, jungle, jungleHills}; +static const int oceanMonumentBiomeList1[] = {ocean, deepOcean, river, frozenRiver, frozenOcean, frozenDeepOcean, coldOcean, coldDeepOcean, lukewarmOcean, lukewarmDeepOcean, warmOcean, warmDeepOcean}; +static const int oceanMonumentBiomeList2[] = {frozenDeepOcean, coldDeepOcean, deepOcean, lukewarmDeepOcean, warmDeepOcean}; +static const int villageBiomeList[] = {plains, desert, savanna, taiga}; +static const int mansionBiomeList[] = {roofedForest, roofedForest+128}; + +static const int achievementBiomes_1_7[] = +{ + ocean, plains, desert, extremeHills, forest, taiga, swampland, river, /*hell, sky,*/ // 0-9 + /*frozenOcean,*/ frozenRiver, icePlains, iceMountains, mushroomIsland, mushroomIslandShore, beach, desertHills, forestHills, taigaHills, // 10-19 + /*extremeHillsEdge,*/ jungle, jungleHills, jungleEdge, deepOcean, stoneBeach, coldBeach, birchForest, birchForestHills, roofedForest, // 20-29 + coldTaiga, coldTaigaHills, megaTaiga, megaTaigaHills, extremeHillsPlus, savanna, savannaPlateau, mesa, mesaPlateau_F, mesaPlateau // 30-39 +}; + + + +STRUCT(Pos) +{ + int x, z; +}; + +STRUCT(BiomeFilter) +{ + // bitfield for required temperature categories, including special variants + uint64_t tempCat; + // bitfield for the required ocean types + uint64_t oceansToFind; + // bitfield of required biomes without modification bit + uint64_t biomesToFind; + // bitfield of required modified biomes + uint64_t modifiedToFind; + + // check that there is a minimum of both special and normal temperatures + int tempNormal, tempSpecial; + // check for the temperatures specified by tempCnt (1:1024) + int doTempCheck; + // check for mushroom potential + int requireMushroom; + // combine a more detailed mushroom and temperature check (1:256) + int doShroomAndTempCheck; + // early check for 1.13 ocean types (1:256) + int doOceanTypeCheck; + // + int doMajorBiomeCheck; + // pre-generation biome checks in layer L_BIOME_256 + int checkBiomePotential; + // + int doScale4Check; +}; + + +//============================================================================== +// Globals +//============================================================================== + +extern Biome biomes[256]; + + + +/******************************** SEED FINDING ********************************* + * + * If we want to find rare seeds that meet multiple custom criteria then we + * should test each condition, starting with the one that is the cheapest + * to test for, while ruling out the most seeds. + * + * Biome checks are quite expensive and should be applied late in the + * condition chain (to avoid as many unnecessary checks as possible). + * Fortunately we can often rule out vast amounts of seeds before hand. + */ + + +/*************************** Quad-Structure Checks ***************************** + * + * Several tricks can be applied to determine candidate seeds for quad + * temples (inc. witch huts). + * + * Minecraft uses a 48-bit pseudo random number generator (PRNG) to determine + * the position of it's structures. The remaining top 16 bits do not influence + * the structure positioning. Additionally the position of most structures in a + * world can be translated by applying the following transformation to the + * seed: + * + * seed2 = seed1 - dregX * 341873128712 - dregZ * 132897987541; + * + * Here seed1 and seed2 have the same structure positioning, but moved by a + * region offset of (dregX,dregZ). [a region is 32x32 chunks]. + * + * For a quad-structure, we mainly care about relative positioning, so we can + * get away with just checking the regions near the origin: (0,0),(0,1),(1,0) + * and (1,1) and then move the structures to the desired position. + * + * Lastly we can recognise a that the transformation of relative region- + * coordinates imposes some restrictions in the PRNG, such that + * perfect-position quad-structure-seeds can only have certain values for the + * lower 16-bits in their seeds. + * + * + ** The Set of all Quad-Witch-Huts + * + * These conditions only leave 32 free bits which can comfortably be brute- + * forced to get the entire set of quad-structure candidates. Each of the seeds + * found this way describes an entire set of possible quad-witch-huts (with + * degrees of freedom for region-transposition, and the top 16-bit bits). + * + */ + + + +//============================================================================== +// Moving Structures +//============================================================================== + +/* Transposes a base seed such that structures are moved by the specified region + * vector, (regX, regZ). + */ +static inline int64_t moveStructure(const int64_t baseSeed, + const int regX, const int regZ) +{ + return (baseSeed - regX*341873128712 - regZ*132897987541) & 0xffffffffffff; +} + + + +//============================================================================== +// Saving & Loading Seeds +//============================================================================== + +/* Loads a list of seeds from a file. The seeds should be written as decimal + * UFT-8 numbers separated by newlines. + * @fnam: file path + * @scnt: number of valid seeds found in the file, which is also the number of + * elements in the returned buffer + * + * Return a pointer to dynamically allocated seed list. + */ +int64_t *loadSavedSeeds(const char *fnam, int64_t *scnt); + + + +//============================================================================== +// Multi-Structure-Base Checks +//============================================================================== + +/* Calls the correct quad-base finder for the structure config, if available. + * (Exits program otherwise.) + */ +int isQuadBase(const StructureConfig sconf, const int64_t seed, const int64_t qual); + +/* Calls the correct tri-base finder for the structure config, if available. + * (Exits program otherwise.) + */ +int isTriBase(const StructureConfig sconf, const int64_t seed, const int64_t qual); + +/* Starts a multi-threaded search for structure base seeds of the specified + * quality (chunk tolerance). The result is saved in a file of path 'fnam'. + */ +void search4QuadBases(const char *fnam, int threads, + const StructureConfig structureConfig, int quality); + + + +//============================================================================== +// Finding Structure Positions +//============================================================================== + +/* Fast implementation for finding the block position at which the structure + * generation attempt will occur within the specified region. + * This function applies for scattered-feature structureSeeds and villages. + */ +Pos getStructurePos(const StructureConfig config, int64_t seed, + const int regionX, const int regionZ); + +/* Finds the chunk position within the specified region (a square region of + * chunks depending on structure type) where the structure generation attempt + * will occur. + * This function applies for scattered-feature structureSeeds and villages. + */ +Pos getStructureChunkInRegion(const StructureConfig config, int64_t seed, + const int regionX, const int regionZ); + +/* Fast implementation for finding the block position at which the ocean + * monument or woodland mansion generation attempt will occur within the + * specified region. + */ +Pos getLargeStructurePos(const StructureConfig config, int64_t seed, + const int regionX, const int regionZ); + +/* Fast implementation for finding the chunk position at which the ocean + * monument or woodland mansion generation attempt will occur within the + * specified region. + */ +Pos getLargeStructureChunkInRegion(const StructureConfig config, int64_t seed, + const int regionX, const int regionZ); + + + +//============================================================================== +// Checking Biomes & Biome Helper Functions +//============================================================================== + +/* Returns the biome for the specified block position. + * (Alternatives should be considered first in performance critical code.) + */ +int getBiomeAtPos(const LayerStack g, const Pos pos); + +/* Finds a suitable pseudo-random location in the specified area. + * This function is used to determine the positions of spawn and strongholds. + * Warning: accurate, but slow! + * + * @mcversion : Minecraft version (changed in: 1.7, 1.13) + * @g : generator layer stack + * @cache : biome buffer, set to NULL for temporary allocation + * @centreX, centreZ : origin for the search + * @range : square 'radius' of the search + * @isValid : boolean array of valid biome ids (size = 256) + * @seed : seed used for the RNG + * (initialise RNG using setSeed(&seed)) + * @passes : number of valid biomes passed, set to NULL to ignore this + */ +Pos findBiomePosition( + const int mcversion, + const LayerStack g, + int * cache, + const int centerX, + const int centerZ, + const int range, + const int * isValid, + int64_t * seed, + int * passes + ); + +/* Determines if the given area contains only biomes specified by 'biomeList'. + * This function is used to determine the positions of villages, ocean monuments + * and mansions. + * Warning: accurate, but slow! + * + * @g : generator layer stack + * @cache : biome buffer, set to NULL for temporary allocation + * @posX, posZ : centre for the check + * @radius : 'radius' of the check area + * @isValid : boolean array of valid biome ids (size = 256) + */ +int areBiomesViable( + const LayerStack g, + int * cache, + const int posX, + const int posZ, + const int radius, + const int * isValid + ); + +/* Finds the smallest radius (by square around the origin) at which all the + * specified biomes are present. The input map is assumed to be a square of + * side length 'sideLen'. + * + * @map : square biome map to be tested + * @sideLen : side length of the square map (should be 2*radius+1) + * @biomes : list of biomes to check for + * @bnum : length of 'biomes' + * @ignoreMutations : flag to count mutated biomes as their original form + * + * Return the radius on the square map that covers all biomes in the list. + * If the map does not contain all the specified biomes, -1 is returned. + */ +int getBiomeRadius( + const int * map, + const int mapSide, + const int * biomes, + const int bnum, + const int ignoreMutations); + + + +//============================================================================== +// Finding Strongholds and Spawn +//============================================================================== + +/* Finds the block positions of the strongholds in the world. Note that the + * number of strongholds was increased from 3 to 128 in MC 1.9. + * Warning: Slow! + * + * @mcversion : Minecraft version (changed in 1.7, 1.9, 1.13) + * @g : generator layer stack [worldSeed should be applied before call!] + * @cache : biome buffer, set to NULL for temporary allocation + * @locations : output block positions + * @worldSeed : world seed of the generator + * @maxSH : Stop when this many strongholds have been found. A value of 0 + * defaults to 3 for mcversion <= MC_1_8, and to 128 for >= MC_1_9. + * @maxRadius : Stop searching if the radius exceeds this value in meters. + * Set this to 0 to ignore this condition. + * + * Returned is the number of strongholds found. + */ +int findStrongholds( + const int mcversion, + LayerStack * g, + int * cache, + Pos * locations, + int64_t worldSeed, + int maxSH, + const int maxRadius + ); + +/* Finds the spawn point in the world. + * Warning: Slow, and may be inaccurate because the world spawn depends on + * grass blocks! + * + * @mcversion : Minecraft version (changed in 1.7, 1.13) + * @g : generator layer stack [worldSeed should be applied before call!] + * @cache : biome buffer, set to NULL for temporary allocation + * @worldSeed : world seed used for the generator + */ +Pos getSpawn(const int mcversion, LayerStack *g, int *cache, int64_t worldSeed); + +/* Finds the approximate spawn point in the world. + * + * @mcversion : Minecraft version (changed in 1.7, 1.13) + * @g : generator layer stack [worldSeed should be applied before call!] + * @cache : biome buffer, set to NULL for temporary allocation + * @worldSeed : world seed used for the generator + */ +Pos estimateSpawn(const int mcversion, LayerStack *g, int *cache, int64_t worldSeed); + + +//============================================================================== +// Validating Structure Positions +//============================================================================== + +/************************ Biome Checks for Structures ************************** + * + * Scattered features only do a simple check of the biome at the block position + * of the structure origin (i.e. the north-west corner). Before 1.13 the type of + * structure was determined by the biome, while in 1.13 the scattered feature + * positions are calculated separately for each type. However, the biome + * requirements remain the same: + * + * Desert Pyramid: desert or desertHills + * Igloo : icePlains or coldTaiga + * Jungle Pyramid: jungle or jungleHills + * Swamp Hut : swampland + * + * Similarly, Ocean Ruins and Shipwrecks require any oceanic biome at their + * block position. + * + * Villages, Monuments and Mansions on the other hand require a certain area to + * be of a valid biome and the check is performed at a 1:4 scale instead of 1:1. + * (Actually the area for villages has a radius zero, which means it is a simple + * biome check at a 1:4 scale.) + */ + + +/* These functions perform a biome check at the specified block coordinates to + * determine whether the corresponding structure would spawn there. You can get + * the block positions using the appropriate getXXXPos() function. + * + * @g : generator layer stack [set seed using applySeed()] + * @cache : biome buffer, set to NULL for temporary allocation + * @blockX, blockZ : block coordinates + * + * In the case of isViableFeaturePos() the 'type' argument specifies the type of + * scattered feature (as an enum) for which the check is performed. + * + * The return value is non-zero if the position is valid. + */ +int isViableFeaturePos(const int type, const LayerStack g, int *cache, const int blockX, const int blockZ); +int isViableVillagePos(const LayerStack g, int *cache, const int blockX, const int blockZ); +int isViableOceanMonumentPos(const LayerStack g, int *cache, const int blockX, const int blockZ); +int isViableMansionPos(const LayerStack g, int *cache, const int blockX, const int blockZ); + + + +//============================================================================== +// Finding Properties of Structures +//============================================================================== + +/* Initialises and returns a random seed used in the (16x16) chunk generation. + * This random object is used for recursiveGenerate() which is responsible for + * generating caves, ravines, mineshafts, and virtually all other structures. + */ +inline static int64_t chunkGenerateRnd(const int64_t worldSeed, const int chunkX, const int chunkZ) +{ + int64_t rnd = worldSeed; + setSeed(&rnd); + rnd = (nextLong(&rnd) * chunkX) ^ (nextLong(&rnd) * chunkZ) ^ worldSeed; + setSeed(&rnd); + return rnd; +} + +/* Checks if the village in the given region would be infested by zombies. + * (Minecraft 1.10+) + */ +int isZombieVillage(const int mcversion, const int64_t worldSeed, + const int regionX, const int regionZ); + +/* Checks if the village in the given region would generate as a baby zombie + * village. (The fact that these exist could be regarded as a bug.) + * (Minecraft 1.12) + */ +int isBabyZombieVillage(const int mcversion, const int64_t worldSeed, + const int regionX, const int regionZ); + +/* Finds the number of each type of house that generate in a village. + * @worldSeed : world seed + * @chunkX, chunkZ : 16x16 chunk position of the village origin + * @housesOut : output number of houses for each entry in the house type + * enum (i.e this should be an array of length HOUSE_NUM) + * + * Returns the random object seed after finding these numbers. + */ +int64_t getHouseList(const int64_t worldSeed, const int chunkX, const int chunkZ, + int *housesOut); + + +//============================================================================== +// Seed Filters +//============================================================================== + +/* Looks through the seeds in 'seedsIn' and copies those for which all + * temperature categories are present in the 3x3 area centred on the specified + * coordinates into 'seedsOut'. The map scale at this layer is 1:1024. + * + * @g : generator layer stack, (NOTE: seed will be modified) + * @cache : biome buffer, set to NULL for temporary allocation + * @seedsIn : list of seeds to check + * @seedsOut : output buffer for the candidate seeds + * @seedCnt : number of seeds in 'seedsIn' + * @centX, centZ : search origin centre (in 1024 block units) + * + * Returns the number of found candidates. + */ +int64_t filterAllTempCats( + LayerStack * g, + int * cache, + const int64_t * seedsIn, + int64_t * seedsOut, + const int64_t seedCnt, + const int centX, + const int centZ + ); + +/* Looks through the list of seeds in 'seedsIn' and copies those that have all + * major overworld biomes in the specified area into 'seedsOut'. These checks + * are done at a scale of 1:256. + * + * @g : generator layer stack, (NOTE: seed will be modified) + * @cache : biome buffer, set to NULL for temporary allocation + * @seedsIn : list of seeds to check + * @seedsOut : output buffer for the candidate seeds + * @seedCnt : number of seeds in 'seedsIn' + * @pX, pZ : search starting coordinates (in 256 block units) + * @sX, sZ : size of the searching area (in 256 block units) + * + * Returns the number of seeds found. + */ +int64_t filterAllMajorBiomes( + LayerStack * g, + int * cache, + const int64_t * seedsIn, + int64_t * seedsOut, + const int64_t seedCnt, + const int pX, + const int pZ, + const unsigned int sX, + const unsigned int sZ + ); + +/* Creates a biome filter configuration from a given list of biomes. + */ +BiomeFilter setupBiomeFilter(const int *biomeList, int listLen); + +/* Tries to determine if the biomes configured in the filter will generate in + * this seed within the specified area. The smallest layer scale checked is + * given by 'minscale'. Lowering this value terminate the search earlier and + * yield more false positives. + */ +int64_t checkForBiomes( + LayerStack * g, + int * cache, + const int64_t seed, + const int blockX, + const int blockZ, + const unsigned int width, + const unsigned int height, + const BiomeFilter filter, + const int minscale); + + +#endif /* FINDERS_H_ */ diff --git a/generationByCubitect/generator.cpp b/generationByCubitect/generator.cpp new file mode 100644 index 0000000..3745998 --- /dev/null +++ b/generationByCubitect/generator.cpp @@ -0,0 +1,285 @@ +#include "generator.hpp" +#include "layers.hpp" + +#include +#include +#include +#include + + +void setupLayer(int scale, Layer *l, Layer *p, int s, void (*getMap)(Layer *layer, int *out, long long x, long long z, int w, int h)) +{ + setBaseSeed(l, s); + l->scale = scale; + l->p = p; + l->p2 = nullptr; + l->getMap = getMap; + l->oceanRnd = nullptr; +} + +void setupMultiLayer(int scale, Layer *l, Layer *p1, Layer *p2, int s, void (*getMap)(Layer *layer, int *out, long long x, long long z, int w, int h)) +{ + setBaseSeed(l, s); + l->scale = scale; + l->p = p1; + l->p2 = p2; + l->getMap = getMap; + l->oceanRnd = nullptr; +} + + +LayerStack setupGenerator(const int mcversion) +{ + if (mcversion <= MC_1_12) + return setupGeneratorMC17(); + else + return setupGeneratorMC113(); +} + + +LayerStack setupGeneratorMC17() +{ + if (biomes[plains].id == 0) + { + fprintf(stderr, "Warning: The biomes have to be initialised first using initBiomes() before any generator can be used.\n"); + } + + LayerStack g; + + g.layerNum = 44; + g.layers = (Layer*) malloc(sizeof(Layer)*g.layerNum); + + // SCALE LAYER PARENT SEED LAYER_FUNCTION + setupLayer(4096, &g.layers[ 0], nullptr, 1, mapIsland); + setupLayer(2048, &g.layers[ 1], &g.layers[ 0], 2000, mapZoom); + setupLayer(2048, &g.layers[ 2], &g.layers[ 1], 1, mapAddIsland); + setupLayer(1024, &g.layers[ 3], &g.layers[ 2], 2001, mapZoom); + setupLayer(1024, &g.layers[ 4], &g.layers[ 3], 2, mapAddIsland); + setupLayer(1024, &g.layers[ 5], &g.layers[ 4], 50, mapAddIsland); + setupLayer(1024, &g.layers[ 6], &g.layers[ 5], 70, mapAddIsland); + setupLayer(1024, &g.layers[ 7], &g.layers[ 6], 2, mapRemoveTooMuchOcean); + setupLayer(1024, &g.layers[ 8], &g.layers[ 7], 2, mapAddSnow); + setupLayer(1024, &g.layers[ 9], &g.layers[ 8], 3, mapAddIsland); + setupLayer(1024, &g.layers[10], &g.layers[ 9], 2, mapCoolWarm); + setupLayer(1024, &g.layers[11], &g.layers[10], 2, mapHeatIce); + setupLayer(1024, &g.layers[12], &g.layers[11], 3, mapSpecial); + setupLayer( 512, &g.layers[13], &g.layers[12], 2002, mapZoom); + setupLayer( 256, &g.layers[14], &g.layers[13], 2003, mapZoom); + setupLayer( 256, &g.layers[15], &g.layers[14], 4, mapAddIsland); + setupLayer( 256, &g.layers[16], &g.layers[15], 5, mapAddMushroomIsland); + setupLayer( 256, &g.layers[17], &g.layers[16], 4, mapDeepOcean); + // biome layer chain + setupLayer( 256, &g.layers[18], &g.layers[17], 200, mapBiome); + setupLayer( 128, &g.layers[19], &g.layers[18], 1000, mapZoom); + setupLayer( 64, &g.layers[20], &g.layers[19], 1001, mapZoom); + setupLayer( 64, &g.layers[21], &g.layers[20], 1000, mapBiomeEdge); + + // basic river layer chain, used to determine where hills generate + setupLayer( 256, &g.layers[22], &g.layers[17], 100, mapRiverInit); + setupLayer( 128, &g.layers[23], &g.layers[22], 1000, mapZoom); + setupLayer( 64, &g.layers[24], &g.layers[23], 1001, mapZoom); + + setupMultiLayer(64, &g.layers[25], &g.layers[21], &g.layers[24], 1000, mapHills); + + setupLayer( 64, &g.layers[26], &g.layers[25], 1001, mapRareBiome); + setupLayer( 32, &g.layers[27], &g.layers[26], 1000, mapZoom); + setupLayer( 32, &g.layers[28], &g.layers[27], 3, mapAddIsland); + setupLayer( 16, &g.layers[29], &g.layers[28], 1001, mapZoom); + setupLayer( 16, &g.layers[30], &g.layers[29], 1000, mapShore); + setupLayer( 8, &g.layers[31], &g.layers[30], 1002, mapZoom); + setupLayer( 4, &g.layers[32], &g.layers[31], 1003, mapZoom); + setupLayer( 4, &g.layers[33], &g.layers[32], 1000, mapSmooth); + + // river layer chain + setupLayer( 128, &g.layers[34], &g.layers[22], 1000, mapZoom); + setupLayer( 64, &g.layers[35], &g.layers[34], 1001, mapZoom); + setupLayer( 32, &g.layers[36], &g.layers[35], 1000, mapZoom); + setupLayer( 16, &g.layers[37], &g.layers[36], 1001, mapZoom); + setupLayer( 8, &g.layers[38], &g.layers[37], 1002, mapZoom); + setupLayer( 4, &g.layers[39], &g.layers[38], 1003, mapZoom); + setupLayer( 4, &g.layers[40], &g.layers[39], 1, mapRiver); + setupLayer( 4, &g.layers[41], &g.layers[40], 1000, mapSmooth); + + setupMultiLayer(4, &g.layers[42], &g.layers[33], &g.layers[41], 100, mapRiverMix); + setupLayer( 1, &g.layers[43], &g.layers[42], 10, mapVoronoiZoom); + + return g; +} + + +LayerStack setupGeneratorMC113() +{ + if (biomes[plains].id == 0) + { + fprintf(stderr, "Warning: The biomes have to be initialised first using initBiomes() before any generator can be used.\n"); + } + + LayerStack g; + + g.layerNum = 52; + g.layers = (Layer *) malloc(sizeof(Layer) * g.layerNum); + + // SCALE LAYER PARENT SEED LAYER_FUNCTION + setupLayer(4096, &g.layers[ 0], nullptr, 1, mapIsland); + setupLayer(2048, &g.layers[ 1], &g.layers[ 0], 2000, mapZoom); + setupLayer(2048, &g.layers[ 2], &g.layers[ 1], 1, mapAddIsland); + setupLayer(1024, &g.layers[ 3], &g.layers[ 2], 2001, mapZoom); + setupLayer(1024, &g.layers[ 4], &g.layers[ 3], 2, mapAddIsland); + setupLayer(1024, &g.layers[ 5], &g.layers[ 4], 50, mapAddIsland); + setupLayer(1024, &g.layers[ 6], &g.layers[ 5], 70, mapAddIsland); + setupLayer(1024, &g.layers[ 7], &g.layers[ 6], 2, mapRemoveTooMuchOcean); + + setupLayer(1024, &g.layers[ 8], &g.layers[ 7], 2, mapAddSnow); + setupLayer(1024, &g.layers[ 9], &g.layers[ 8], 3, mapAddIsland); + setupLayer(1024, &g.layers[10], &g.layers[ 9], 2, mapCoolWarm); + setupLayer(1024, &g.layers[11], &g.layers[10], 2, mapHeatIce); + setupLayer(1024, &g.layers[12], &g.layers[11], 3, mapSpecial); + setupLayer( 512, &g.layers[13], &g.layers[12], 2002, mapZoom); + setupLayer( 256, &g.layers[14], &g.layers[13], 2003, mapZoom); + setupLayer( 256, &g.layers[15], &g.layers[14], 4, mapAddIsland); + setupLayer( 256, &g.layers[16], &g.layers[15], 5, mapAddMushroomIsland); + setupLayer( 256, &g.layers[17], &g.layers[16], 4, mapDeepOcean); + // biome layer chain + setupLayer( 256, &g.layers[18], &g.layers[17], 200, mapBiome); + setupLayer( 128, &g.layers[19], &g.layers[18], 1000, mapZoom); + setupLayer( 64, &g.layers[20], &g.layers[19], 1001, mapZoom); + setupLayer( 64, &g.layers[21], &g.layers[20], 1000, mapBiomeEdge); + + // basic river layer chain, used to determine where hills generate + setupLayer( 256, &g.layers[22], &g.layers[17], 100, mapRiverInit); + setupLayer( 128, &g.layers[23], &g.layers[22], 1000, mapZoom); + setupLayer( 64, &g.layers[24], &g.layers[23], 1001, mapZoom); + + setupMultiLayer(64, &g.layers[25], &g.layers[21], &g.layers[24], 1000, mapHills113); + + setupLayer( 64, &g.layers[26], &g.layers[25], 1001, mapRareBiome); + setupLayer( 32, &g.layers[27], &g.layers[26], 1000, mapZoom); + setupLayer( 32, &g.layers[28], &g.layers[27], 3, mapAddIsland); + setupLayer( 16, &g.layers[29], &g.layers[28], 1001, mapZoom); + setupLayer( 16, &g.layers[30], &g.layers[29], 1000, mapShore); + setupLayer( 8, &g.layers[31], &g.layers[30], 1002, mapZoom); + setupLayer( 4, &g.layers[32], &g.layers[31], 1003, mapZoom); + setupLayer( 4, &g.layers[33], &g.layers[32], 1000, mapSmooth); + + // river layer chain + setupLayer( 128, &g.layers[34], &g.layers[22], 1000, mapZoom); + setupLayer( 64, &g.layers[35], &g.layers[34], 1001, mapZoom); + setupLayer( 32, &g.layers[36], &g.layers[35], 1000, mapZoom); + setupLayer( 16, &g.layers[37], &g.layers[36], 1001, mapZoom); + setupLayer( 8, &g.layers[38], &g.layers[37], 1002, mapZoom); + setupLayer( 4, &g.layers[39], &g.layers[38], 1003, mapZoom); + setupLayer( 4, &g.layers[40], &g.layers[39], 1, mapRiver); + setupLayer( 4, &g.layers[41], &g.layers[40], 1000, mapSmooth); + + setupMultiLayer(4, &g.layers[42], &g.layers[33], &g.layers[41], 100, mapRiverMix); + + // ocean variants + setupLayer( 256, &g.layers[43], nullptr, 2, mapOceanTemp); + g.layers[43].oceanRnd = (OceanRnd *) malloc(sizeof(OceanRnd)); + setupLayer( 128, &g.layers[44], &g.layers[43], 2001, mapZoom); + setupLayer( 64, &g.layers[45], &g.layers[44], 2002, mapZoom); + setupLayer( 32, &g.layers[46], &g.layers[45], 2003, mapZoom); + setupLayer( 16, &g.layers[47], &g.layers[46], 2004, mapZoom); + setupLayer( 8, &g.layers[48], &g.layers[47], 2005, mapZoom); + setupLayer( 4, &g.layers[49], &g.layers[48], 2006, mapZoom); + + setupMultiLayer(4, &g.layers[50], &g.layers[42], &g.layers[49], 100, mapOceanMix); + + setupLayer(1, &g.layers[51], &g.layers[50], 10, mapVoronoiZoom); + + return g; +} + +void freeGenerator(LayerStack g) +{ + int i; + for(i = 0; i < g.layerNum; i++) + { + if (g.layers[i].oceanRnd != nullptr) + free(g.layers[i].oceanRnd); + } + + free(g.layers); +} + + +/* Recursively calculates the minimum buffer size required to generate an area + * of the specified size from the current layer onwards. + */ +static void getMaxArea(Layer *layer, long long areaX, long long areaZ, int *maxX, int *maxZ) +{ + if (layer == nullptr) + return; + + if (layer->getMap == mapZoom) + { + areaX = (areaX >> 1) + 2; + areaZ = (areaZ >> 1) + 2; + } + else if (layer->getMap == mapVoronoiZoom) + { + areaX = (areaX >> 2) + 2; + areaZ = (areaZ >> 2) + 2; + } + else if (layer->getMap == mapOceanMix) + { + areaX += 17; + areaZ += 17; + } + else + { + if (layer->getMap != mapNull && + layer->getMap != mapSkip && + layer->getMap != mapIsland && + layer->getMap != mapSpecial && + layer->getMap != mapBiome && + layer->getMap != mapRiverInit && + layer->getMap != mapRiverMix && + layer->getMap != mapOceanTemp) + { + areaX += 2; + areaZ += 2; + } + } + + if (areaX > *maxX) *maxX = (int)areaX; + if (areaZ > *maxZ) *maxZ = (int)areaZ; + + getMaxArea(layer->p, areaX, areaZ, maxX, maxZ); + getMaxArea(layer->p2, areaX, areaZ, maxX, maxZ); +} + +int calcRequiredBuf(Layer *layer, long long areaX, long long areaZ) +{ + int maxX = (int)areaX, maxZ = (int)areaZ; + getMaxArea(layer, areaX, areaZ, &maxX, &maxZ); + return maxX * maxZ; +} + +int *allocCache(Layer *layer, int sizeX, int sizeZ) +{ + int size = calcRequiredBuf(layer, sizeX, sizeZ); + + int *ret = (int *) malloc(sizeof(*ret)*size); + memset(ret, 0, sizeof(*ret)*size); + + return ret; +} + + +void applySeed(LayerStack *g, int64_t seed) +{ + // the seed has to be applied recursively + setWorldSeed(&g->layers[g->layerNum-1], seed); +} + +void genArea(Layer *layer, int *out, long long areaX, long long areaZ, int areaWidth, int areaHeight) +{ + memset(out, 0, areaWidth*areaHeight*sizeof(*out)); + layer->getMap(layer, out, areaX, areaZ, areaWidth, areaHeight); +} + + + + + diff --git a/generationByCubitect/generator.hpp b/generationByCubitect/generator.hpp new file mode 100644 index 0000000..4df30b4 --- /dev/null +++ b/generationByCubitect/generator.hpp @@ -0,0 +1,226 @@ +#ifndef GENERATOR_H_ +#define GENERATOR_H_ + +#include "layers.hpp" + +enum versions { + MC_1_7, MC_1_8, MC_1_9, MC_1_10, MC_1_11, MC_1_12, MC_1_13, MC_1_13_2, MC_1_14, MC_LEG +}; + +/* Enumeration of the layer indices in the generator. */ +enum +{ + L_ISLAND_4096 = 0, + L_ZOOM_2048, + L_ADD_ISLAND_2048, + L_ZOOM_1024, + L_ADD_ISLAND_1024A, + L_ADD_ISLAND_1024B, + L_ADD_ISLAND_1024C, + L_REMOVE_TOO_MUCH_OCEAN_1024, + L_ADD_SNOW_1024, + L_ADD_ISLAND_1024D, + L_COOL_WARM_1024, + L_HEAT_ICE_1024, + L_SPECIAL_1024, /* Good entry for: temperature categories */ + L_ZOOM_512, + L_ZOOM_256, + L_ADD_ISLAND_256, + L_ADD_MUSHROOM_ISLAND_256, /* Good entry for: mushroom biomes */ + L_DEEP_OCEAN_256, + L_BIOME_256, /* Good entry for: major biome types */ + L_ZOOM_128, + L_ZOOM_64, + L_BIOME_EDGE_64, + L_RIVER_INIT_256, + L_ZOOM_128_HILLS, + L_ZOOM_64_HILLS, + L_HILLS_64, /* Good entry for: minor biome types */ + L_RARE_BIOME_64, + L_ZOOM_32, + L_ADD_ISLAND_32, + L_ZOOM_16, + L_SHORE_16, + L_ZOOM_8, + L_ZOOM_4, + L_SMOOTH_4, + L_ZOOM_128_RIVER, + L_ZOOM_64_RIVER, + L_ZOOM_32_RIVER, + L_ZOOM_16_RIVER, + L_ZOOM_8_RIVER, + L_ZOOM_4_RIVER, + L_RIVER_4, + L_SMOOTH_4_RIVER, + L_RIVER_MIX_4, + L_VORONOI_ZOOM_1, + + L_NUM, + + // 1.13 layers + L13_OCEAN_TEMP_256 = 43, + L13_ZOOM_128, + L13_ZOOM_64, + L13_ZOOM_32, + L13_ZOOM_16, + L13_ZOOM_8, + L13_ZOOM_4, + L13_OCEAN_MIX_4, + L13_VORONOI_ZOOM_1, + + L13_NUM +}; + + +/******************************** BIOME TABLES ********************************* + * The biome tables below are lists of the biomes that can be present at some + * notable layers. Of cause, layers that are applied later in the hierarchy will + * also contain these biomes. + */ + +//============================================================================== +// MC 1.13 Biome Tables +//============================================================================== + +static const int BIOMES_L13_OCEAN_TEMP_256[] = +{ + ocean, frozenOcean, warmOcean, lukewarmOcean, coldOcean +}; + +static const int BIOMES_L13_OCEAN_MIX_4[] = +{ + ocean, plains, desert, extremeHills, forest, taiga, swampland, river, /*hell, sky,*/ // 0-9 + frozenOcean, frozenRiver, icePlains, iceMountains, mushroomIsland, mushroomIslandShore, beach, desertHills, forestHills, taigaHills, // 10-19 + /*extremeHillsEdge,*/ jungle, jungleHills, jungleEdge, deepOcean, stoneBeach, coldBeach, birchForest, birchForestHills, roofedForest, // 20-29 + coldTaiga, coldTaigaHills, megaTaiga, megaTaigaHills, extremeHillsPlus, savanna, savannaPlateau, mesa, mesaPlateau_F, mesaPlateau, // 30-39 + /*skyIslandLow, skyIslandMedium, skyIslandHigh, skyIslandBarren,*/ warmOcean, lukewarmOcean, coldOcean, /*warmDeepOcean,*/ lukewarmDeepOcean, coldDeepOcean, // 40-49 + frozenDeepOcean, + // Modified variants... + plains+128, desert+128, extremeHills+128, forest+128, taiga+128, swampland+128, + icePlains+128, jungle+128, jungleEdge+128, birchForest+128, birchForestHills+128, roofedForest+128, + coldTaiga+128, megaTaiga+128, megaTaigaHills+128, extremeHillsPlus+128, savanna+128, savannaPlateau+128, mesa+128, mesaPlateau_F+128, mesaPlateau+128 +}; + + +//============================================================================== +// MC 1.7 Biome Tables +//============================================================================== + +/* L_ADD_MUSHROOM_ISLAND_256 and L_DEEP_OCEAN_256 + * add the mushroomIsland and deepOcean biomes respectively, however the rest of + * the biomes are incomplete and are better described by temperature categories + * with special modifications bits. + */ + +// BIOMES_L_BIOME_256: Changes temperature to weighted biomes: +// Warm -> [desert, desert, desert, savanna, savanna, plains] +// Warm,special -> [mesaPlateau, mesaPlateau_F, mesaPlateau_F] +// Lush -> [forest, roofedForest, extremeHills, plains, birchForest, swampland] +// Lush,special -> [jungle] +// Cold -> [forest, extremeHills, taiga, plains] +// Cold,special -> [megaTaiga] +// Freezing -> [icePlains, icePlains, icePlains, coldTaiga] +static const int BIOMES_L_BIOME_256[] = +{ + ocean, plains, desert, extremeHills, forest, taiga, swampland, /*river, hell, sky,*/ // 0-9 + /*frozenOcean, frozenRiver,*/ icePlains, /*iceMountains,*/ mushroomIsland, /*mushroomIslandShore, beach, desertHills, forestHills, taigaHills,*/ // 10-19 + /*extremeHillsEdge,*/ jungle, /*jungleHills, jungleEdge,*/ deepOcean, /*stoneBeach, coldBeach,*/ birchForest, /*birchForestHills,*/ roofedForest, // 20-29 + coldTaiga, /*coldTaigaHills,*/ megaTaiga, /*megaTaigaHills, extremeHillsPlus,*/ savanna, /*savannaPlateau, mesa,*/ mesaPlateau_F, mesaPlateau, // 30-39 +}; + +// Introduces biomes: jungleEdge, extremeHillsPlus, mesa +static const int BIOMES_L_BIOME_EDGE_64[] = +{ + ocean, plains, desert, extremeHills, forest, taiga, swampland, /*river, hell, sky,*/ // 0-9 + /*frozenOcean, frozenRiver,*/ icePlains, /*iceMountains,*/ mushroomIsland, /*mushroomIslandShore, beach, desertHills, forestHills, taigaHills,*/ // 10-19 + /*extremeHillsEdge,*/ jungle, /*jungleHills,*/ jungleEdge, deepOcean, /*stoneBeach, coldBeach,*/ birchForest, /*birchForestHills,*/ roofedForest, // 20-29 + coldTaiga, /*coldTaigaHills,*/ megaTaiga, /*megaTaigaHills,*/ extremeHillsPlus, savanna, /*savannaPlateau,*/ mesa, mesaPlateau_F, mesaPlateau, // 30-39 +}; + +// Introduces biomes: iceMountains, desertHills, forestHills, taigaHills, +// jungleHills, birchForestHills, coldTaigaHills, megaTaigaHills, savannaPlateau +// and all 21 mutated biomes +static const int BIOMES_L_HILLS_64[] = +{ + ocean, plains, desert, extremeHills, forest, taiga, swampland, /*river, hell, sky,*/ // 0-9 + /*frozenOcean, frozenRiver,*/ icePlains, iceMountains, mushroomIsland, /*mushroomIslandShore, beach,*/ desertHills, forestHills, taigaHills, // 10-19 + /*extremeHillsEdge,*/ jungle, jungleHills, jungleEdge, deepOcean, /*stoneBeach, coldBeach,*/ birchForest, birchForestHills, roofedForest, // 20-29 + coldTaiga, coldTaigaHills, megaTaiga, megaTaigaHills, extremeHillsPlus, savanna, savannaPlateau, mesa, mesaPlateau_F, mesaPlateau, // 30-39 + // Modified variants... + plains+128, desert+128, extremeHills+128, forest+128, taiga+128, swampland+128, + icePlains+128, jungle+128, jungleEdge+128, birchForest+128, birchForestHills+128, roofedForest+128, + coldTaiga+128, megaTaiga+128, megaTaigaHills+128, extremeHillsPlus+128, savanna+128, savannaPlateau+128, mesa+128, mesaPlateau_F+128, mesaPlateau+128 +}; + +// Introduces biomes: mushroomIslandShore, beach, stoneBeach, coldBeach +static const int BIOMES_L_SHORE_16[] = +{ + ocean, plains, desert, extremeHills, forest, taiga, swampland, /*river, hell, sky,*/ // 0-9 + /*frozenOcean, frozenRiver,*/ icePlains, iceMountains, mushroomIsland, mushroomIslandShore, beach, desertHills, forestHills, taigaHills, // 10-19 + /*extremeHillsEdge,*/ jungle, jungleHills, jungleEdge, deepOcean, stoneBeach, coldBeach, birchForest, birchForestHills, roofedForest, // 20-29 + coldTaiga, coldTaigaHills, megaTaiga, megaTaigaHills, extremeHillsPlus, savanna, savannaPlateau, mesa, mesaPlateau_F, mesaPlateau, // 30-39 + // Modified variants... + plains+128, desert+128, extremeHills+128, forest+128, taiga+128, swampland+128, + icePlains+128, jungle+128, jungleEdge+128, birchForest+128, birchForestHills+128, roofedForest+128, + coldTaiga+128, megaTaiga+128, megaTaigaHills+128, extremeHillsPlus+128, savanna+128, savannaPlateau+128, mesa+128, mesaPlateau_F+128, mesaPlateau+128 +}; + +// Merges the river branch and adds frozenRiver biome +static const int BIOMES_L_RIVER_MIX_4[] = +{ + ocean, plains, desert, extremeHills, forest, taiga, swampland, river, /*hell, sky,*/ // 0-9 + /*frozenOcean,*/ frozenRiver, icePlains, iceMountains, mushroomIsland, mushroomIslandShore, beach, desertHills, forestHills, taigaHills, // 10-19 + /*extremeHillsEdge,*/ jungle, jungleHills, jungleEdge, deepOcean, stoneBeach, coldBeach, birchForest, birchForestHills, roofedForest, // 20-29 + coldTaiga, coldTaigaHills, megaTaiga, megaTaigaHills, extremeHillsPlus, savanna, savannaPlateau, mesa, mesaPlateau_F, mesaPlateau, // 30-39 + // Modified variants... + plains+128, desert+128, extremeHills+128, forest+128, taiga+128, swampland+128, + icePlains+128, jungle+128, jungleEdge+128, birchForest+128, birchForestHills+128, roofedForest+128, + coldTaiga+128, megaTaiga+128, megaTaigaHills+128, extremeHillsPlus+128, savanna+128, savannaPlateau+128, mesa+128, mesaPlateau_F+128, mesaPlateau+128 +}; + + +STRUCT(LayerStack) +{ + Layer *layers; + int layerNum; +}; + +/* Initialise an instance of a generator. */ +LayerStack setupGenerator(int mcversion); +LayerStack setupGeneratorMC17(); +LayerStack setupGeneratorMC113(); + +/* Cleans up and frees the generator layers */ +void freeGenerator(LayerStack g); + + +/* Calculates the minimum size of the buffers required to generate an area of + * dimensions 'sizeX' by 'sizeZ' at the specified layer. + */ +int calcRequiredBuf(Layer *layer, long long areaX, long long areaZ); + +/* Allocates an amount of memory required to generate an area of dimensions + * 'sizeX' by 'sizeZ' for the magnification of the current top layer. + */ +int *allocCache(Layer *layer, int sizeX, int sizeZ); + + +/* Set up custom layers. */ +void setupLayer(int scale, Layer *l, Layer *p, int s, void (*getMap)(Layer *layer, int *out, long long x, long long z, int w, int h)); +void setupMultiLayer(int scale, Layer *l, Layer *p1, Layer *p2, int s, void (*getMap)(Layer *layer, int *out, long long x, long long z, int w, int h)); + + +/* Sets the world seed for the generator */ +void applySeed(LayerStack *g, int64_t seed); + +/* Generates the specified area using the current generator settings and stores + * the biomeIDs in 'out'. + * The biomeIDs will be indexed in the form: out[x + z*areaWidth] + * It is recommended that 'out' is allocated using allocCache() for the correct + * buffer size. + */ +void genArea(Layer *layer, int *out, long long areaX, long long areaZ, int areaWidth, int areaHeight); + + +#endif /* GENERATOR_H_ */ + diff --git a/generationByCubitect/javarnd.h b/generationByCubitect/javarnd.h new file mode 100644 index 0000000..f0df426 --- /dev/null +++ b/generationByCubitect/javarnd.h @@ -0,0 +1,117 @@ +#ifndef JAVARND_H_ +#define JAVARND_H_ + +#include + + +/********************** C copy of the Java Random methods ********************** + */ + +static inline void setSeed(int64_t *seed) +{ + *seed = (*seed ^ 0x5deece66d) & ((1LL << 48) - 1); +} + +static inline int next(int64_t *seed, const int bits) +{ + *seed = (*seed * 0x5deece66d + 0xb) & ((1LL << 48) - 1); + return (int) (*seed >> (48 - bits)); +} + +static inline int nextInt(int64_t *seed, const int n) +{ + int bits, val; + const int m = n - 1; + + if(((unsigned int)m & (unsigned int)n) == 0) return (int) ((n * (unsigned long)next(seed, 31)) >> 31u); + + do { + bits = next(seed, 31); + val = bits % n; + } + while(bits - val + m < 0); + return val; +} + +static inline int64_t nextLong(int64_t *seed) +{ + return ((int64_t) next(seed, 32) << 32) + next(seed, 32); +} + +static inline float nextFloat(int64_t *seed) +{ + return next(seed, 24) / (float) (1 << 24); +} + +static inline double nextDouble(int64_t *seed) +{ + return (((int64_t) next(seed, 26) << 27) + next(seed, 27)) / (double) (1LL << 53); +} + + + +// Custom, faster alternative for the first and second call to nextInt(24) + +static inline long long firstInt24(int64_t seed) +{ + seed ^= 0x5deece66d; + seed = (seed * 0x5deece66d) & 0xffffffffffff; + seed >>= 17; + return seed % 24; +} + +static inline long long secondInt24(int64_t seed) +{ + seed ^= 0x5deece66d; + seed = (seed * 0x5deece66d + 0xb) & 0xffffffffffff; + seed = (seed * 0x5deece66d) & 0xffffffffffff; + seed >>= 17; + return seed % 24; +} + +/* skipNextN + * --------- + * Jumps forwards in the random number sequence by simulating 'n' calls to next. + */ +static inline void skipNextN(int64_t *seed, const int n) +{ + for(int i = 0; i < n; i++) *seed = (*seed * 0x5deece66d + 0xb); + *seed &= 0xffffffffffff; +} + +/* invSeed48 + * --------- + * Returns the previous 48-bit seed which will generate 'nseed'. + * The upper 16 bits are ignored, both here and in the generator. + */ +static inline int64_t invSeed48(int64_t nseed) +{ + const int64_t x = 0x5deece66d; + const int64_t xinv = 0xdfe05bcb1365LL; + const int64_t y = 0xbLL; + const int64_t m48 = 0xffffffffffffLL; + + int64_t a = nseed >> 32; + int64_t b = nseed & 0xffffffffLL; + if(b & 0x80000000LL) a++; + + int64_t q = ((b << 16) - y - (a << 16)*x) & m48; + for(int64_t k = 0; k <= 5; k++) + { + int64_t d = (x - (q + (k << 48))) % x; + d = (d + x) % x; // force the modulo and keep it positive + if(d < 65536) + { + int64_t c = ((q + d) * xinv) & m48; + if(c < 65536) + { + return ((((a << 16) + c) - y) * xinv) & m48; + } + } + } + return -1; +} + + + +#endif /* JAVARND_H_ */ diff --git a/generationByCubitect/layers.cpp b/generationByCubitect/layers.cpp new file mode 100644 index 0000000..aff2b22 --- /dev/null +++ b/generationByCubitect/layers.cpp @@ -0,0 +1,1559 @@ +#include "layers.hpp" +#include +#include +#include + + +static void oceanRndInit(OceanRnd *rnd, int64_t seed); + +Biome biomes[256]; + +void initAddBiome(int id, int tempCat, int biometype, double temp, double height) { + if (id & (~0xff)) return; + biomes[id].id = id; + biomes[id].type = biometype; + biomes[id].temp = temp; + biomes[id].height = height; + biomes[id].tempCat = tempCat; +} + +void createMutation(int id) { + biomes[id + 128] = biomes[id]; + biomes[id + 128].id = id + 128; +} + +/* initBiomes() has to be called before any of the generators can be used */ +void initBiomes() { + int i; + for (i = 0; i < 256; i++) biomes[i].id = none; + + const double hDefault = 0.1, hShallowWaters = -0.5, hOceans = -1.0, hDeepOceans = -1.8, hLowPlains = 0.125; + const double hMidPlains = 0.2, hLowHills = 0.45, hHighPlateaus = 1.5, hMidHills = 1.0, hShores = 0.0; + const double hRockyWaters = 0.1, hLowIslands = 0.2, hPartiallySubmerged = -0.2; + + initAddBiome(ocean, Oceanic, Ocean, 0.5, hOceans); + initAddBiome(plains, Lush, Plains, 0.8, hDefault); + initAddBiome(desert, Warm, Desert, 2.0, hLowPlains); + initAddBiome(extremeHills, Lush, Hills, 0.2, hMidHills); + initAddBiome(forest, Lush, Forest, 0.7, hDefault); + initAddBiome(taiga, Lush, Taiga, 0.25, hMidPlains); + initAddBiome(swampland, Lush, Swamp, 0.8, hPartiallySubmerged); + initAddBiome(river, Lush, River, 0.5, hShallowWaters); + initAddBiome(hell, Warm, Hell, 2.0, hDefault); + initAddBiome(sky, Lush, Sky, 0.5, hDefault); + initAddBiome(frozenOcean, Oceanic, Ocean, 0.0, hOceans); + initAddBiome(frozenRiver, Cold, River, 0.0, hShallowWaters); + initAddBiome(icePlains, Cold, Snow, 0.0, hLowPlains); + initAddBiome(iceMountains, Cold, Snow, 0.0, hLowHills); + initAddBiome(mushroomIsland, Lush, MushroomIsland, 0.9, hLowIslands); + initAddBiome(mushroomIslandShore, Lush, MushroomIsland, 0.9, hShores); + initAddBiome(beach, Lush, Beach, 0.8, hShores); + initAddBiome(desertHills, Warm, Desert, 2.0, hLowHills); + initAddBiome(forestHills, Lush, Forest, 0.7, hLowHills); + initAddBiome(taigaHills, Lush, Taiga, 0.25, hLowHills); + initAddBiome(extremeHillsEdge, Lush, Hills, 0.2, hMidHills); + initAddBiome(jungle, Lush, Jungle, 0.95, hDefault); + initAddBiome(jungleHills, Lush, Jungle, 0.95, hLowHills); + initAddBiome(jungleEdge, Lush, Jungle, 0.95, hDefault); + initAddBiome(deepOcean, Oceanic, Ocean, 0.5, hDeepOceans); + initAddBiome(stoneBeach, Lush, StoneBeach, 0.2, hRockyWaters); + initAddBiome(coldBeach, Cold, Beach, 0.05, hShores); + initAddBiome(birchForest, Lush, Forest, 0.6, hDefault); + initAddBiome(birchForestHills, Lush, Forest, 0.6, hLowHills); + initAddBiome(roofedForest, Lush, Forest, 0.7, hDefault); + initAddBiome(coldTaiga, Cold, Taiga, -0.5, hMidPlains); + initAddBiome(coldTaigaHills, Cold, Taiga, -0.5, hLowHills); + initAddBiome(megaTaiga, Lush, Taiga, 0.3, hMidPlains); + initAddBiome(megaTaigaHills, Lush, Taiga, 0.3, hLowHills); + initAddBiome(extremeHillsPlus, Lush, Hills, 0.2, hMidHills); + initAddBiome(savanna, Warm, Savanna, 1.2, hLowPlains); + initAddBiome(savannaPlateau, Warm, Savanna, 1.0, hHighPlateaus); + initAddBiome(mesa, Warm, Mesa, 2.0, hDefault); + initAddBiome(mesaPlateau_F, Warm, Mesa, 2.0, hHighPlateaus); + initAddBiome(mesaPlateau, Warm, Mesa, 2.0, hHighPlateaus); + + // TODO: determine the 1.13 biome properties + initAddBiome(skyIslandLow, Lush, Sky, 0, 0); + initAddBiome(skyIslandMedium, Lush, Sky, 0, 0); + initAddBiome(skyIslandHigh, Lush, Sky, 0, 0); + initAddBiome(skyIslandBarren, Lush, Sky, 0, 0); + initAddBiome(warmOcean, Oceanic, Ocean, 0, 0); + initAddBiome(lukewarmOcean, Oceanic, Ocean, 0, 0); + initAddBiome(coldOcean, Oceanic, Ocean, 0, 0); + initAddBiome(warmDeepOcean, Oceanic, Ocean, 0, 0); + initAddBiome(lukewarmDeepOcean, Oceanic, Ocean, 0, 0); + initAddBiome(coldDeepOcean, Oceanic, Ocean, 0, 0); + initAddBiome(frozenDeepOcean, Oceanic, Ocean, 0, 0); + + createMutation(plains); + createMutation(desert); + createMutation(extremeHills); + createMutation(forest); + createMutation(taiga); + createMutation(swampland); + createMutation(icePlains); + createMutation(jungle); + createMutation(jungleEdge); + createMutation(birchForest); + createMutation(birchForestHills); + createMutation(roofedForest); + createMutation(coldTaiga); + createMutation(megaTaiga); + createMutation(megaTaigaHills); + createMutation(extremeHillsPlus); + createMutation(savanna); + createMutation(savannaPlateau); + createMutation(mesa); + createMutation(mesaPlateau_F); + createMutation(mesaPlateau); +} + + +void setWorldSeed(Layer *layer, int64_t seed) { + if (layer->p2 != nullptr && layer->getMap != mapHills) + setWorldSeed(layer->p2, seed); + + if (layer->p != nullptr) + setWorldSeed(layer->p, seed); + + if (layer->oceanRnd != nullptr) + oceanRndInit(layer->oceanRnd, seed); + + layer->worldSeed = seed; + layer->worldSeed *= layer->worldSeed * 6364136223846793005LL + 1442695040888963407LL; + layer->worldSeed += layer->baseSeed; + layer->worldSeed *= layer->worldSeed * 6364136223846793005LL + 1442695040888963407LL; + layer->worldSeed += layer->baseSeed; + layer->worldSeed *= layer->worldSeed * 6364136223846793005LL + 1442695040888963407LL; + layer->worldSeed += layer->baseSeed; +} + + +void mapNull(Layer *l, int *__restrict out, long long x, long long z, int w, int h) { +} + +void mapSkip(Layer *l, int *__restrict out, long long x, long long z, int w, int h) { + if (l->p == nullptr) { + printf("mapSkip() requires a non-null parent layer.\n"); + exit(1); + } + l->p->getMap(l->p, out, x, z, w, h); +} + + +void mapIsland(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int x, z; + + const int64_t ws = l->worldSeed; + const int64_t ss = ws * (ws * 6364136223846793005LL + 1442695040888963407LL); + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + const auto chunkX = (int64_t) (x + areaX); + const auto chunkZ = (int64_t) (z + areaZ); + int64_t cs = ss; + cs += chunkX; + cs *= cs * 6364136223846793005LL + 1442695040888963407LL; + cs += chunkZ; + cs *= cs * 6364136223846793005LL + 1442695040888963407LL; + cs += chunkX; + cs *= cs * 6364136223846793005LL + 1442695040888963407LL; + cs += chunkZ; + + out[x + z * areaWidth] = (cs >> 24) % 10 == 0; + } + } + + if (areaX > -areaWidth && areaX <= 0 && areaZ > -areaHeight && areaZ <= 0) { + out[-areaX + -areaZ * areaWidth] = 1; + } +} + +#if defined USE_SIMD && defined __AVX2__ + +void mapZoom(Layer *l, int* __restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) +{ + int pWidth = (areaWidth>>1)+2, pHeight = (areaHeight>>1)+1; + + l->p->getMap(l->p, out, areaX>>1, areaZ>>1, pWidth, pHeight+1); + + __m256i (*selectRand)(__m256i* cs, int ws, __m256i a1, __m256i a2, __m256i a3, __m256i a4) = (l->p->getMap == mapIsland) ? select8Random4 : select8ModeOrRandom; + int newWidth = (areaWidth+10)&0xFFFFFFFE;//modified to ignore ends + int x, z; + __m256i cs, a, b, a1, b1, toBuf1, toBuf2, aSuf; + __m256i mask1 = _mm256_setr_epi32(0xFFFFFFFF, 0x0, 0xFFFFFFFF, 0x0, 0xFFFFFFFF, 0x0, 0xFFFFFFFF, 0x0), mask2 = _mm256_setr_epi32(0x0, 0xFFFFFFFF, 0x0, 0xFFFFFFFF, 0x0, 0xFFFFFFFF, 0x0, 0xFFFFFFFF); + __m256i shuffle = _mm256_setr_epi32(0, 4, 1, 5, 2, 6, 3, 7); + int pX = areaX&0xFFFFFFFE; + __m256i xs = _mm256_set_epi32(pX+14, pX+12, pX+10, pX+8, pX+6, pX+4, pX+2, pX), zs; + __m256i v2 = _mm256_set1_epi32(2), v16 = _mm256_set1_epi32(16); + int* buf = malloc((newWidth+1)*((areaHeight+2)|1)*sizeof(*buf)); + int* idx = buf; + int* outIdx = out; + //z first! + for (x = 0; x < pWidth-1; x += 8) + { + a = _mm256_loadu_si256((__m256i*)(outIdx));//0, 0 + b = _mm256_loadu_si256((__m256i*)(outIdx+1));//1, 0 + zs = _mm256_set1_epi32(areaZ&0xFFFFFFFE); + for (z = 0; z < pHeight; z++) + { + cs = set8ChunkSeeds(l->worldSeed, xs, zs); + outIdx += pWidth; + a1 = _mm256_loadu_si256((__m256i*)(outIdx));//0, 1 + b1 = _mm256_loadu_si256((__m256i*)(outIdx+1));//1, 1 + toBuf1 = _mm256_permutevar8x32_epi32(select8Random2(&cs, l->worldSeed, a, a1), shuffle); + toBuf2 = _mm256_permutevar8x32_epi32(select8Random2(&cs, l->worldSeed, a, b), shuffle); + aSuf = _mm256_permutevar8x32_epi32(a, shuffle); + _mm256_maskstore_epi32(idx, mask1, aSuf); + _mm256_maskstore_epi32(idx+1, mask1, toBuf2); + _mm256_maskstore_epi32(idx+7, mask2, aSuf); + _mm256_maskstore_epi32(idx+8, mask2, toBuf2); + idx += newWidth; + toBuf2 = _mm256_permutevar8x32_epi32(selectRand(&cs, l->worldSeed, a, b, a1, b1), shuffle); + _mm256_maskstore_epi32(idx, mask1, toBuf1); + _mm256_maskstore_epi32(idx+1, mask1, toBuf2); + _mm256_maskstore_epi32(idx+7, mask2, toBuf1); + _mm256_maskstore_epi32(idx+8, mask2, toBuf2); + idx += newWidth; + a = a1; + b = b1; + zs = _mm256_add_epi32(zs, v2); + } + outIdx += 8-pHeight*pWidth; + idx += 16-pHeight*2*newWidth; + xs = _mm256_add_epi32(xs, v16); + } + + for (z = 0; z < areaHeight; z++) + { + memcpy(&out[z*areaWidth], &buf[(z + (areaZ & 1))*newWidth + (areaX & 1)], areaWidth*sizeof(int)); + } + + free(buf); +} + +#elif defined USE_SIMD && defined __SSE4_2__ + +void mapZoom(Layer *l, int* __restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) +{ + int pWidth = (areaWidth>>1)+2, pHeight = (areaHeight>>1)+1; + + l->p->getMap(l->p, out, areaX>>1, areaZ>>1, pWidth, pHeight+1); + + __m128i (*selectRand)(__m128i* cs, int ws, __m128i a1, __m128i a2, __m128i a3, __m128i a4) = (l->p->getMap == mapIsland) ? select4Random4 : select4ModeOrRandom; + int newWidth = areaWidth+6&0xFFFFFFFE;//modified to ignore ends + int x, z; + __m128i cs, a, b, a1, b1, toBuf1, toBuf2, aSuf; + __m128i mask1 = _mm_setr_epi32(0xFFFFFFFF, 0x0, 0xFFFFFFFF, 0x0), mask2 = _mm_setr_epi32(0x0, 0xFFFFFFFF, 0x0, 0xFFFFFFFF); + int pX = areaX&0xFFFFFFFE; + __m128i xs = _mm_set_epi32(pX+6, pX+4, pX+2, pX), zs; + __m128i v2 = _mm_set1_epi32(2), v8 = _mm_set1_epi32(8); + int* buf = malloc((newWidth+1)*(areaHeight+2|1)*sizeof(*buf)); + int* idx = buf; + int* outIdx = out; + //z first! + for (x = 0; x < pWidth-1; x += 4) + { + a = _mm_loadu_si128((__m128i_u*)(outIdx));//0, 0 + b = _mm_loadu_si128((__m128i_u*)(outIdx+1));//1, 0 + zs = _mm_set1_epi32(areaZ&0xFFFFFFFE); + for (z = 0; z < pHeight; z++) + { + cs = set4ChunkSeeds(l->worldSeed, xs, zs); + outIdx += pWidth; + a1 = _mm_loadu_si128((__m128i_u*)(outIdx));//0, 1 + b1 = _mm_loadu_si128((__m128i_u*)(outIdx+1));//1, 1 + toBuf1 = _mm_shuffle_epi32(select4Random2(&cs, l->worldSeed, a, a1), 0xD8);//11011000->3120->1324 + toBuf2 = _mm_shuffle_epi32(select4Random2(&cs, l->worldSeed, a, b), 0xD8); + aSuf = _mm_shuffle_epi32(a, 0xD8); + _mm_maskmoveu_si128(aSuf, mask1, (char*)(idx)); + _mm_maskmoveu_si128(toBuf2, mask1, (char*)(idx+1)); + _mm_maskmoveu_si128(aSuf, mask2, (char*)(idx+3)); + _mm_maskmoveu_si128(toBuf2, mask2, (char*)(idx+4)); + idx += newWidth; + toBuf2 = _mm_shuffle_epi32(selectRand(&cs, l->worldSeed, a, b, a1, b1), 0xD8); + _mm_maskmoveu_si128(toBuf1, mask1, (char*)(idx)); + _mm_maskmoveu_si128(toBuf2, mask1, (char*)(idx+1)); + _mm_maskmoveu_si128(toBuf1, mask2, (char*)(idx+3)); + _mm_maskmoveu_si128(toBuf2, mask2, (char*)(idx+4)); + idx += newWidth; + a = a1; + b = b1; + zs = _mm_add_epi32(zs, v2); + } + outIdx += 4-pHeight*pWidth; + idx += 8-pHeight*2*newWidth; + xs = _mm_add_epi32(xs, v8); + } + + for (z = 0; z < areaHeight; z++) + { + memcpy(&out[z*areaWidth], &buf[(z + (areaZ & 1))*newWidth + (areaX & 1)], areaWidth*sizeof(int)); + } + + free(buf); +} + +#else + +void mapZoom(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int pX = (int)areaX >> 1; + int pZ = (int)areaZ >> 1; + int pWidth = (areaWidth >> 1) + 2; + int pHeight = (areaHeight >> 1) + 2; + int x, z; + + l->p->getMap(l->p, out, pX, pZ, pWidth, pHeight); + + int newWidth = (pWidth - 1) << 1; + int newHeight = (pHeight - 1) << 1; + int idx, a, b; + int *buf = (int *) malloc((newWidth + 1) * (newHeight + 1) * sizeof(*buf)); + + const int ws = (int) l->worldSeed; + const long ss = ws * (ws * 1284865837 + 4150755663); + + for (z = 0; z < pHeight - 1; z++) { + idx = (z << 1) * newWidth; + a = out[(z + 0) * pWidth]; + b = out[(z + 1) * pWidth]; + + for (x = 0; x < pWidth - 1; x++) { + int a1 = out[x + 1 + (z + 0) * pWidth]; + int b1 = out[x + 1 + (z + 1) * pWidth]; + + const int chunkX = (x + pX) << 1; + const int chunkZ = (z + pZ) << 1; + + long cs = ss; + cs += chunkX; + cs *= cs * 1284865837 + 4150755663; + cs += chunkZ; + cs *= cs * 1284865837 + 4150755663; + cs += chunkX; + cs *= cs * 1284865837 + 4150755663; + cs += chunkZ; + + buf[idx] = a; + buf[idx + newWidth] = (cs >> 24) & 1 ? b : a; + idx++; + + cs *= cs * 1284865837 + 4150755663; + cs += ws; + buf[idx] = (cs >> 24) & 1 ? a1 : a; + + + if (l->p->getMap == mapIsland) { + //selectRandom4 + cs *= cs * 1284865837 + 4150755663; + cs += ws; + const int i =(int) (cs >> 24) & 3; + buf[idx + newWidth] = i == 0 ? a : i == 1 ? a1 : i == 2 ? b : b1; + } else { + //selectModeOrRandom + if (a1 == b && b == b1) buf[idx + newWidth] = a1; + else if (a == a1 && a == b) buf[idx + newWidth] = a; + else if (a == a1 && a == b1) buf[idx + newWidth] = a; + else if (a == b && a == b1) buf[idx + newWidth] = a; + else if (a == a1 && b != b1) buf[idx + newWidth] = a; + else if (a == b && a1 != b1) buf[idx + newWidth] = a; + else if (a == b1 && a1 != b) buf[idx + newWidth] = a; + else if (a1 == b && a != b1) buf[idx + newWidth] = a1; + else if (a1 == b1 && a != b) buf[idx + newWidth] = a1; + else if (b == b1 && a != a1) buf[idx + newWidth] = b; + else { + cs *= cs * 1284865837 + 4150755663; + cs += ws; + const int i = (int)(cs >> 24) & 3; + buf[idx + newWidth] = i == 0 ? a : i == 1 ? a1 : i == 2 ? b : b1; + } + } + + idx++; + a = a1; + b = b1; + } + } + + for (z = 0; z < areaHeight; z++) { + memcpy(&out[z * areaWidth], &buf[(z + (areaZ & 1)) * newWidth + (areaX & 1)], areaWidth * sizeof(int)); + } + + free(buf); +} + +#endif + +void mapAddIsland(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int pX = (int)areaX - 1; + int pZ = (int)areaZ - 1; + int pWidth = areaWidth + 2; + int pHeight = areaHeight + 2; + int x, z; + + l->p->getMap(l->p, out, pX, pZ, pWidth, pHeight); + + const int64_t ws = l->worldSeed; + const int64_t ss = ws * (ws * 6364136223846793005LL + 1442695040888963407LL); + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + int v00 = out[x + 0 + (z + 0) * pWidth]; + int v20 = out[x + 2 + (z + 0) * pWidth]; + int v02 = out[x + 0 + (z + 2) * pWidth]; + int v22 = out[x + 2 + (z + 2) * pWidth]; + int v11 = out[x + 1 + (z + 1) * pWidth]; + + if (v11 == 0 && (v00 != 0 || v20 != 0 || v02 != 0 || v22 != 0)) { + const auto chunkX = (int64_t) (x + areaX); + const auto chunkZ = (int64_t) (z + areaZ); + int64_t cs = ss; + cs += chunkX; + cs *= cs * 6364136223846793005LL + 1442695040888963407LL; + cs += chunkZ; + cs *= cs * 6364136223846793005LL + 1442695040888963407LL; + cs += chunkX; + cs *= cs * 6364136223846793005LL + 1442695040888963407LL; + cs += chunkZ; + + int v = 1; + int inc = 0; + + if (v00 != 0) { + ++inc; + v = v00; + cs *= cs * 6364136223846793005LL + 1442695040888963407LL; + cs += ws; + } + if (v20 != 0) { + if (++inc == 1 || (cs & (1LL << 24)) == 0) v = v20; + cs *= cs * 6364136223846793005LL + 1442695040888963407LL; + cs += ws; + } + if (v02 != 0) { + switch (++inc) { + case 1: + v = v02; + break; + case 2: + if ((cs & (1LL << 24)) == 0) v = v02; + break; + default: + if (((cs >> 24) % 3) == 0) v = v02; + } + cs *= cs * 6364136223846793005LL + 1442695040888963407LL; + cs += ws; + } + if (v22 != 0) { + switch (++inc) { + case 1: + v = v22; + break; + case 2: + if ((cs & (1LL << 24)) == 0) v = v22; + break; + case 3: + if (((cs >> 24) % 3) == 0) v = v22; + break; + default: + if ((cs & (3LL << 24)) == 0) v = v22; + } + cs *= cs * 6364136223846793005LL + 1442695040888963407LL; + cs += ws; + } + + if ((cs >> 24) % 3 == 0) + out[x + z * areaWidth] = v; + else if (v == 4) + out[x + z * areaWidth] = 4; + else + out[x + z * areaWidth] = 0; + } else if (v11 > 0 && (v00 == 0 || v20 == 0 || v02 == 0 || v22 == 0)) { + //setChunkSeed(l, (int64_t)(x + areaX), (int64_t)(z + areaZ)); + //if (mcNextInt(l, 5) == 0)... + + const auto chunkX = (int64_t) (x + areaX); + const auto chunkZ = (int64_t) (z + areaZ); + + int64_t cs = ss; + cs += chunkX; + cs *= cs * 6364136223846793005LL + 1442695040888963407LL; + cs += chunkZ; + cs *= cs * 6364136223846793005LL + 1442695040888963407LL; + cs += chunkX; + cs *= cs * 6364136223846793005LL + 1442695040888963407LL; + cs += chunkZ; + + if ((cs >> 24) % 5 == 0) + out[x + z * areaWidth] = (v11 == 4) ? 4 : 0; + else + out[x + z * areaWidth] = v11; + } else { + out[x + z * areaWidth] = v11; + } + } + } +} + + +void +mapRemoveTooMuchOcean(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int pX =(int) areaX - 1; + int pZ = (int)areaZ - 1; + int pWidth = areaWidth + 2; + int pHeight = areaHeight + 2; + int x, z; + + l->p->getMap(l->p, out, pX, pZ, pWidth, pHeight); + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + int v11 = out[x + 1 + (z + 1) * pWidth]; + out[x + z * areaWidth] = v11; + + if (out[x + 1 + (z + 0) * pWidth] != 0) continue; + if (out[x + 2 + (z + 1) * pWidth] != 0) continue; + if (out[x + 0 + (z + 1) * pWidth] != 0) continue; + if (out[x + 1 + (z + 2) * pWidth] != 0) continue; + + if (v11 == 0) { + setChunkSeed(l, (int64_t) (x + areaX), (int64_t) (z + areaZ)); + + if (mcNextInt(l, 2) == 0) { + out[x + z * areaWidth] = 1; + } + } + } + } +} + + +void mapAddSnow(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int pX = (int)areaX - 1; + int pZ = (int)areaZ - 1; + int pWidth = areaWidth + 2; + int pHeight = areaHeight + 2; + int x, z; + + l->p->getMap(l->p, out, pX, pZ, pWidth, pHeight); + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + int v11 = out[x + 1 + (z + 1) * pWidth]; + + if (isShallowOcean(v11)) { + out[x + z * areaWidth] = v11; + } else { + setChunkSeed(l, (int64_t) (x + areaX), (int64_t) (z + areaZ)); + int r = mcNextInt(l, 6); + int v; + + if (r == 0) v = 4; + else if (r <= 1) v = 3; + else v = 1; + + out[x + z * areaWidth] = v; + } + } + } +} + + +void mapCoolWarm(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int pX = (int)areaX - 1; + int pZ = (int)areaZ - 1; + int pWidth = areaWidth + 2; + int pHeight = areaHeight + 2; + int x, z; + + l->p->getMap(l->p, out, pX, pZ, pWidth, pHeight); + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + int v11 = out[x + 1 + (z + 1) * pWidth]; + + if (v11 == 1) { + int v10 = out[x + 1 + (z + 0) * pWidth]; + int v21 = out[x + 2 + (z + 1) * pWidth]; + int v01 = out[x + 0 + (z + 1) * pWidth]; + int v12 = out[x + 1 + (z + 2) * pWidth]; + + if (v10 == 3 || v10 == 4 || v21 == 3 || v21 == 4 || v01 == 3 || v01 == 4 || v12 == 3 || v12 == 4) { + v11 = 2; + } + } + + out[x + z * areaWidth] = v11; + } + } +} + + +void mapHeatIce(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int pX = (int) areaX - 1; + int pZ = (int) areaZ - 1; + int pWidth = areaWidth + 2; + int pHeight = areaHeight + 2; + int x, z; + + l->p->getMap(l->p, out, pX, pZ, pWidth, pHeight); + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + int v11 = out[x + 1 + (z + 1) * pWidth]; + + if (v11 == 4) { + int v10 = out[x + 1 + (z + 0) * pWidth]; + int v21 = out[x + 2 + (z + 1) * pWidth]; + int v01 = out[x + 0 + (z + 1) * pWidth]; + int v12 = out[x + 1 + (z + 2) * pWidth]; + + if (v10 == 1 || v10 == 2 || v21 == 1 || v21 == 2 || v01 == 1 || v01 == 2 || v12 == 1 || v12 == 2) { + v11 = 3; + } + } + + out[x + z * areaWidth] = v11; + } + } +} + + +void mapSpecial(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + l->p->getMap(l->p, out, areaX, areaZ, areaWidth, areaHeight); + + int x, z; + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + int v = out[x + z * areaWidth]; + if (v == 0) continue; + + setChunkSeed(l, (int64_t) (x + areaX), (int64_t) (z + areaZ)); + + if (mcNextInt(l, 13) == 0) { + v |= (1 + mcNextInt(l, 15)) << 8 & 0xf00; + // 1 to 1 mapping so 'out' can be overwritten immediately + out[x + z * areaWidth] = v; + } + } + } +} + + +void +mapAddMushroomIsland(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int pX = (int) areaX - 1; + int pZ = (int) areaZ - 1; + int pWidth = areaWidth + 2; + int pHeight = areaHeight + 2; + int x, z; + + l->p->getMap(l->p, out, pX, pZ, pWidth, pHeight); + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + int v11 = out[x + 1 + (z + 1) * pWidth]; + + // surrounded by ocean? + if (v11 == 0 && !out[x + 0 + (z + 0) * pWidth] && !out[x + 2 + (z + 0) * pWidth] && + !out[x + 0 + (z + 2) * pWidth] && !out[x + 2 + (z + 2) * pWidth]) { + setChunkSeed(l, (int64_t) (x + areaX), (int64_t) (z + areaZ)); + if (mcNextInt(l, 100) == 0) { + out[x + z * areaWidth] = mushroomIsland; + continue; + } + } + + out[x + z * areaWidth] = v11; + } + } +} + + +void mapDeepOcean(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int pX = (int) areaX - 1; + int pZ = (int) areaZ - 1; + int pWidth = areaWidth + 2; + int pHeight = areaHeight + 2; + int x, z; + + l->p->getMap(l->p, out, pX, pZ, pWidth, pHeight); + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + int v11 = out[(x + 1) + (z + 1) * pWidth]; + + if (isShallowOcean(v11)) { + // count adjacent oceans + int oceans = 0; + if (isShallowOcean(out[(x + 1) + (z + 0) * pWidth])) oceans++; + if (isShallowOcean(out[(x + 2) + (z + 1) * pWidth])) oceans++; + if (isShallowOcean(out[(x + 0) + (z + 1) * pWidth])) oceans++; + if (isShallowOcean(out[(x + 1) + (z + 2) * pWidth])) oceans++; + + if (oceans > 3) { + if (v11 == warmOcean) + v11 = warmDeepOcean; + else if (v11 == lukewarmOcean) + v11 = lukewarmDeepOcean; + else if (v11 == ocean) + v11 = deepOcean; + else if (v11 == coldOcean) + v11 = coldDeepOcean; + else if (v11 == frozenOcean) + v11 = frozenDeepOcean; + else + v11 = deepOcean; + } + } + + out[x + z * areaWidth] = v11; + } + } +} + + +const int warmBiomes[] = {desert, desert, desert, savanna, savanna, plains}; +const int lushBiomes[] = {forest, roofedForest, extremeHills, plains, birchForest, swampland}; +const int coldBiomes[] = {forest, extremeHills, taiga, plains}; +const int snowBiomes[] = {icePlains, icePlains, icePlains, coldTaiga}; + +void mapBiome(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + l->p->getMap(l->p, out, areaX, areaZ, areaWidth, areaHeight); + + int x, z; + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + int idx = x + z * areaWidth; + int id = out[idx]; + int hasHighBit = (id & 0xf00) >> 8; + id &= -0xf01; + + if (getBiomeType(id) == Ocean || id == mushroomIsland) { + out[idx] = id; + continue; + } + + setChunkSeed(l, (int64_t) (x + areaX), (int64_t) (z + areaZ)); + + switch (id) { + case Warm: + if (hasHighBit) out[idx] = (mcNextInt(l, 3) == 0) ? mesaPlateau : mesaPlateau_F; + else out[idx] = warmBiomes[mcNextInt(l, 6)]; + break; + case Lush: + if (hasHighBit) out[idx] = jungle; + else out[idx] = lushBiomes[mcNextInt(l, 6)]; + break; + case Cold: + if (hasHighBit) out[idx] = megaTaiga; + else out[idx] = coldBiomes[mcNextInt(l, 4)]; + break; + case Freezing: + out[idx] = snowBiomes[mcNextInt(l, 4)]; + break; + default: + out[idx] = mushroomIsland; + } + } + } +} + + +void mapRiverInit(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + l->p->getMap(l->p, out, areaX, areaZ, areaWidth, areaHeight); + + int x, z; + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + if (out[x + z * areaWidth] > 0) { + setChunkSeed(l, (int64_t) (x + areaX), (int64_t) (z + areaZ)); + out[x + z * areaWidth] = mcNextInt(l, 299999) + 2; + } else { + out[x + z * areaWidth] = 0; + } + } + } +} + + +// replaceEdgeIfNecessary() always returns 0 in the only place it is used in +// Minecraft, making it redundant. +/* +static inline int replaceEdgeIfNecessary(int *out, int idx, int v10, int v21, int v01, int v12, int id, int baseID, int edgeID) +{ + if (!equalOrPlateau(id, baseID)) return 0; + + if (canBeNeighbors(v10, baseID) && canBeNeighbors(v21, baseID) && canBeNeighbors(v01, baseID) && canBeNeighbors(v12, baseID)) + out[idx] = id; + else + out[idx] = edgeID; + + return 1; +} +*/ + +static inline int replaceEdge(int *out, int idx, int v10, int v21, int v01, int v12, int id, int baseID, int edgeID) { + if (id != baseID) return 0; + + if (equalOrPlateau(v10, baseID) && equalOrPlateau(v21, baseID) && equalOrPlateau(v01, baseID) && + equalOrPlateau(v12, baseID)) + out[idx] = id; + else + out[idx] = edgeID; + + return 1; +} + +void mapBiomeEdge(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int pX = (int) areaX - 1; + int pZ = (int) areaZ - 1; + int pWidth = areaWidth + 2; + int pHeight = areaHeight + 2; + int x, z; + + l->p->getMap(l->p, out, pX, pZ, pWidth, pHeight); + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + int v11 = out[x + 1 + (z + 1) * pWidth]; + + int v10 = out[x + 1 + (z + 0) * pWidth]; + int v21 = out[x + 2 + (z + 1) * pWidth]; + int v01 = out[x + 0 + (z + 1) * pWidth]; + int v12 = out[x + 1 + (z + 2) * pWidth]; + + if (/*!replaceEdgeIfNecessary(out, x + z*areaWidth, v10, v21, v01, v12, v11, extremeHills, extremeHillsEdge) &&*/ + !replaceEdge(out, x + z * areaWidth, v10, v21, v01, v12, v11, mesaPlateau_F, mesa) && + !replaceEdge(out, x + z * areaWidth, v10, v21, v01, v12, v11, mesaPlateau, mesa) && + !replaceEdge(out, x + z * areaWidth, v10, v21, v01, v12, v11, megaTaiga, taiga)) { + if (v11 == desert) { + if (v10 != icePlains && v21 != icePlains && v01 != icePlains && v12 != icePlains) { + out[x + z * areaWidth] = v11; + } else { + out[x + z * areaWidth] = extremeHillsPlus; + } + } else if (v11 == swampland) { + if (v10 != desert && v21 != desert && v01 != desert && v12 != desert && + v10 != coldTaiga && v21 != coldTaiga && v01 != coldTaiga && v12 != coldTaiga && + v10 != icePlains && v21 != icePlains && v01 != icePlains && v12 != icePlains) { + if (v10 != jungle && v12 != jungle && v21 != jungle && v01 != jungle) + out[x + z * areaWidth] = v11; + else + out[x + z * areaWidth] = jungleEdge; + } else { + out[x + z * areaWidth] = plains; + } + } else { + out[x + z * areaWidth] = v11; + } + } + } + } +} + + +void mapHills(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int pX = (int) areaX - 1; + int pZ = (int) areaZ - 1; + int pWidth = areaWidth + 2; + int pHeight = areaHeight + 2; + int x, z; + int *buf = nullptr; + + if (l->p2 == nullptr) { + printf("mapHills() requires two parents! Use setupMultiLayer()\n"); + exit(1); + } + + buf = (int *) malloc(pWidth * pHeight * sizeof(int)); + + l->p->getMap(l->p, out, pX, pZ, pWidth, pHeight); + memcpy(buf, out, pWidth * pHeight * sizeof(int)); + + l->p2->getMap(l->p2, out, pX, pZ, pWidth, pHeight); + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + setChunkSeed(l, (int64_t) (x + areaX), (int64_t) (z + areaZ)); + int a11 = buf[x + 1 + (z + 1) * pWidth]; // biome branch + int b11 = out[x + 1 + (z + 1) * pWidth]; // river branch + int idx = x + z * areaWidth; + + int var12 = (b11 - 2) % 29 == 0; + + if (a11 != 0 && b11 >= 2 && (b11 - 2) % 29 == 1 && a11 < 128) { + out[idx] = (biomeExists(a11 + 128)) ? a11 + 128 : a11; + } else if (mcNextInt(l, 3) != 0 && !var12) { + out[idx] = a11; + } else { + int hillID = a11; + + switch (a11) { + case desert: + hillID = desertHills; + break; + case forest: + hillID = forestHills; + break; + case birchForest: + hillID = birchForestHills; + break; + case roofedForest: + hillID = plains; + break; + case taiga: + hillID = taigaHills; + break; + case megaTaiga: + hillID = megaTaigaHills; + break; + case coldTaiga: + hillID = coldTaigaHills; + break; + case plains: + hillID = (mcNextInt(l, 3) == 0) ? forestHills : forest; + break; + case icePlains: + hillID = iceMountains; + break; + case jungle: + hillID = jungleHills; + break; + case ocean: + hillID = deepOcean; + break; + case extremeHills: + hillID = extremeHillsPlus; + break; + case savanna: + hillID = savannaPlateau; + break; + default: + if (equalOrPlateau(a11, mesaPlateau_F)) + hillID = mesa; + else if (a11 == deepOcean && mcNextInt(l, 3) == 0) + hillID = (mcNextInt(l, 2) == 0) ? plains : forest; + break; + } + + if (var12 && hillID != a11) { + if (biomeExists(hillID + 128)) + hillID += 128; + else + hillID = a11; + } + + if (hillID == a11) { + out[idx] = a11; + } else { + int a10 = buf[x + 1 + (z + 0) * pWidth]; + int a21 = buf[x + 2 + (z + 1) * pWidth]; + int a01 = buf[x + 0 + (z + 1) * pWidth]; + int a12 = buf[x + 1 + (z + 2) * pWidth]; + int equals = 0; + + if (equalOrPlateau(a10, a11)) equals++; + if (equalOrPlateau(a21, a11)) equals++; + if (equalOrPlateau(a01, a11)) equals++; + if (equalOrPlateau(a12, a11)) equals++; + + if (equals >= 3) + out[idx] = hillID; + else + out[idx] = a11; + } + } + } + } + + free(buf); +} + + +void mapHills113(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int pX = (int) areaX - 1; + int pZ = (int) areaZ - 1; + int pWidth = areaWidth + 2; + int pHeight = areaHeight + 2; + int x, z; + int *buf = nullptr; + + if (l->p2 == nullptr) { + printf("mapHills() requires two parents! Use setupMultiLayer()\n"); + exit(1); + } + + buf = (int *) malloc(pWidth * pHeight * sizeof(int)); + + l->p->getMap(l->p, out, pX, pZ, pWidth, pHeight); + memcpy(buf, out, pWidth * pHeight * sizeof(int)); + + l->p2->getMap(l->p2, out, pX, pZ, pWidth, pHeight); + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + setChunkSeed(l, (int64_t) (x + areaX), (int64_t) (z + areaZ)); + int a11 = buf[x + 1 + (z + 1) * pWidth]; // biome branch + int b11 = out[x + 1 + (z + 1) * pWidth]; // river branch + int idx = x + z * areaWidth; + + int bn = (b11 - 2) % 29; + + if (!(isOceanic(a11) || b11 < 2 || bn != 1 || a11 >= 128)) { + out[idx] = (biomeExists(a11 + 128)) ? a11 + 128 : a11; + } else if (mcNextInt(l, 3) == 0 || bn == 0) { + int hillID = a11; + + switch (a11) { + case desert: + hillID = desertHills; + break; + case forest: + hillID = forestHills; + break; + case birchForest: + hillID = birchForestHills; + break; + case roofedForest: + hillID = plains; + break; + case taiga: + hillID = taigaHills; + break; + case megaTaiga: + hillID = megaTaigaHills; + break; + case coldTaiga: + hillID = coldTaigaHills; + break; + case plains: + hillID = (mcNextInt(l, 3) == 0) ? forestHills : forest; + break; + case icePlains: + hillID = iceMountains; + break; + case jungle: + hillID = jungleHills; + break; + case ocean: + hillID = deepOcean; + break; + case extremeHills: + hillID = extremeHillsPlus; + break; + case savanna: + hillID = savannaPlateau; + break; + default: + if (equalOrPlateau(a11, mesaPlateau_F)) + hillID = mesa; + else if ((a11 == deepOcean || a11 == lukewarmDeepOcean || + a11 == coldDeepOcean || a11 == frozenDeepOcean) && + mcNextInt(l, 3) == 0) + hillID = (mcNextInt(l, 2) == 0) ? plains : forest; + break; + } + + if (bn == 0 && hillID != a11) { + if (biomeExists(hillID + 128)) + hillID += 128; + else + hillID = a11; + } + + if (hillID != a11) { + int a10 = buf[x + 1 + (z + 0) * pWidth]; + int a21 = buf[x + 2 + (z + 1) * pWidth]; + int a01 = buf[x + 0 + (z + 1) * pWidth]; + int a12 = buf[x + 1 + (z + 2) * pWidth]; + int equals = 0; + + if (equalOrPlateau(a10, a11)) equals++; + if (equalOrPlateau(a21, a11)) equals++; + if (equalOrPlateau(a01, a11)) equals++; + if (equalOrPlateau(a12, a11)) equals++; + + if (equals >= 3) + out[idx] = hillID; + else + out[idx] = a11; + } else { + out[idx] = a11; + } + } else { + out[idx] = a11; + } + } + } + + free(buf); +} + + +static inline int reduceID(int id) { + return id >= 2 ? 2 + (id & 1) : id; +} + +void mapRiver(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int pX = (int) areaX - 1; + int pZ = (int) areaZ - 1; + int pWidth = areaWidth + 2; + int pHeight = areaHeight + 2; + int x, z; + + l->p->getMap(l->p, out, pX, pZ, pWidth, pHeight); + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + int v01 = reduceID(out[x + 0 + (z + 1) * pWidth]); + int v21 = reduceID(out[x + 2 + (z + 1) * pWidth]); + int v10 = reduceID(out[x + 1 + (z + 0) * pWidth]); + int v12 = reduceID(out[x + 1 + (z + 2) * pWidth]); + int v11 = reduceID(out[x + 1 + (z + 1) * pWidth]); + + if (v11 == v01 && v11 == v10 && v11 == v21 && v11 == v12) { + out[x + z * areaWidth] = -1; + } else { + out[x + z * areaWidth] = river; + } + } + } +} + + +void mapSmooth(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int pX = (int) areaX - 1; + int pZ = (int) areaZ - 1; + int pWidth = areaWidth + 2; + int pHeight = areaHeight + 2; + int x, z; + + l->p->getMap(l->p, out, pX, pZ, pWidth, pHeight); + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + int v11 = out[x + 1 + (z + 1) * pWidth]; + int v10 = out[x + 1 + (z + 0) * pWidth]; + int v21 = out[x + 2 + (z + 1) * pWidth]; + int v01 = out[x + 0 + (z + 1) * pWidth]; + int v12 = out[x + 1 + (z + 2) * pWidth]; + + if (v01 == v21 && v10 == v12) { + setChunkSeed(l, (int64_t) (x + areaX), (int64_t) (z + areaZ)); + + if (mcNextInt(l, 2) == 0) + v11 = v01; + else + v11 = v10; + } else { + if (v01 == v21) v11 = v01; + if (v10 == v12) v11 = v10; + } + + out[x + z * areaWidth] = v11; + } + } +} + + +void mapRareBiome(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int pX = (int) areaX - 1; + int pZ = (int) areaZ - 1; + int pWidth = areaWidth + 2; + int pHeight = areaHeight + 2; + int x, z; + + l->p->getMap(l->p, out, pX, pZ, pWidth, pHeight); + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + setChunkSeed(l, (int64_t) (x + areaX), (int64_t) (z + areaZ)); + int v11 = out[x + 1 + (z + 1) * pWidth]; + + if (mcNextInt(l, 57) == 0 && v11 == plains) { + // Sunflower Plains + out[x + z * areaWidth] = plains + 128; + } else { + out[x + z * areaWidth] = v11; + } + } + } +} + + +inline static int replaceOcean(int *out, int idx, int v10, int v21, int v01, int v12, int id, int replaceID) { + if (isOceanic(id)) return 0; + + if (!isOceanic(v10) && !isOceanic(v21) && !isOceanic(v01) && !isOceanic(v12)) + out[idx] = id; + else + out[idx] = replaceID; + + return 1; +} + +inline static int isBiomeJFTO(int id) { + return biomeExists(id) && (getBiomeType(id) == Jungle || id == forest || id == taiga || isOceanic(id)); +} + +void mapShore(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int pX = (int) areaX - 1; + int pZ = (int) areaZ - 1; + int pWidth = areaWidth + 2; + int pHeight = areaHeight + 2; + int x, z; + + l->p->getMap(l->p, out, pX, pZ, pWidth, pHeight); + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + int v11 = out[x + 1 + (z + 1) * pWidth]; + int v10 = out[x + 1 + (z + 0) * pWidth]; + int v21 = out[x + 2 + (z + 1) * pWidth]; + int v01 = out[x + 0 + (z + 1) * pWidth]; + int v12 = out[x + 1 + (z + 2) * pWidth]; + + int biome = biomeExists(v11) ? v11 : 0; + + if (v11 == mushroomIsland) { + if (v10 != ocean && v21 != ocean && v01 != ocean && v12 != ocean) + out[x + z * areaWidth] = v11; + else + out[x + z * areaWidth] = mushroomIslandShore; + } else if (/*biome < 128 &&*/ getBiomeType(biome) == Jungle) { + if (isBiomeJFTO(v10) && isBiomeJFTO(v21) && isBiomeJFTO(v01) && isBiomeJFTO(v12)) { + if (!isOceanic(v10) && !isOceanic(v21) && !isOceanic(v01) && !isOceanic(v12)) + out[x + z * areaWidth] = v11; + else + out[x + z * areaWidth] = beach; + } else { + out[x + z * areaWidth] = jungleEdge; + } + } else if (v11 != extremeHills && v11 != extremeHillsPlus && v11 != extremeHillsEdge) { + if (isBiomeSnowy(biome)) { + replaceOcean(out, x + z * areaWidth, v10, v21, v01, v12, v11, coldBeach); + } else if (v11 != mesa && v11 != mesaPlateau_F) { + if (v11 != ocean && v11 != deepOcean && v11 != river && v11 != swampland) { + if (!isOceanic(v10) && !isOceanic(v21) && !isOceanic(v01) && !isOceanic(v12)) + out[x + z * areaWidth] = v11; + else + out[x + z * areaWidth] = beach; + } else { + out[x + z * areaWidth] = v11; + } + } else { + if (!isOceanic(v10) && !isOceanic(v21) && !isOceanic(v01) && !isOceanic(v12)) { + if (getBiomeType(v10) == Mesa && getBiomeType(v21) == Mesa && getBiomeType(v01) == Mesa && + getBiomeType(v12) == Mesa) + out[x + z * areaWidth] = v11; + else + out[x + z * areaWidth] = desert; + } else { + out[x + z * areaWidth] = v11; + } + } + } else { + replaceOcean(out, x + z * areaWidth, v10, v21, v01, v12, v11, stoneBeach); + } + } + } +} + + +void mapRiverMix(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int idx; + int len; + int *buf; + + if (l->p2 == nullptr) { + printf("mapRiverMix() requires two parents! Use setupMultiLayer()\n"); + exit(1); + } + + len = areaWidth * areaHeight; + buf = (int *) malloc(len * sizeof(int)); + + l->p->getMap(l->p, out, areaX, areaZ, areaWidth, areaHeight); // biome chain + memcpy(buf, out, len * sizeof(int)); + + l->p2->getMap(l->p2, out, areaX, areaZ, areaWidth, areaHeight); // rivers + + for (idx = 0; idx < len; idx++) { + if (isOceanic(buf[idx])) { + out[idx] = buf[idx]; + } else { + if (out[idx] == river) { + if (buf[idx] == icePlains) + out[idx] = frozenRiver; + else if (buf[idx] == mushroomIsland || buf[idx] == mushroomIslandShore) + out[idx] = mushroomIslandShore; + else + out[idx] = out[idx] & 255; + } else { + out[idx] = buf[idx]; + } + } + } + + free(buf); +} + + +/* Initialises data for the ocean temperature types using the world seed. + * This function is called when the world seed is applied in setWorldSeed(). + */ +static void oceanRndInit(OceanRnd *rnd, int64_t seed) { + int i = 0; + memset(rnd, 0, sizeof(*rnd)); + setSeed(&seed); + rnd->a = nextDouble(&seed) * 256.0; + rnd->b = nextDouble(&seed) * 256.0; + rnd->c = nextDouble(&seed) * 256.0; + + for (i = 0; i < 256; i++) { + rnd->d[i] = i; + } + for (i = 0; i < 256; i++) { + int n3 = nextInt(&seed, 256 - i) + i; + int n4 = rnd->d[i]; + rnd->d[i] = rnd->d[n3]; + rnd->d[n3] = n4; + rnd->d[i + 256] = rnd->d[i]; + } +} + +static double lerp(const double part, const double from, const double to) { + return from + part * (to - from); +} + +/* Table of vectors to cube edge centres (12 + 4 extra), used for ocean PRNG */ +const double cEdgeX[] = {1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0}; +const double cEdgeY[] = {1.0, 1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0}; +const double cEdgeZ[] = {0.0, 0.0, 0.0, 0.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 0.0, 1.0, 0.0, -1.0}; + +static double indexedLerp(int idx, const double d1, const double d2, const double d3) { + idx &= 0xf; + return cEdgeX[idx] * d1 + cEdgeY[idx] * d2 + cEdgeZ[idx] * d3; +} + + +static double getOceanTemp(const OceanRnd *rnd, double d1, double d2, double d3) { + d1 += rnd->a; + d2 += rnd->b; + d3 += rnd->c; + int i1 = (int) d1 - (int) (d1 < 0); + int i2 = (int) d2 - (int) (d2 < 0); + int i3 = (int) d3 - (int) (d3 < 0); + d1 -= i1; + d2 -= i2; + d3 -= i3; + double t1 = d1 * d1 * d1 * (d1 * (d1 * 6.0 - 15.0) + 10.0); + double t2 = d2 * d2 * d2 * (d2 * (d2 * 6.0 - 15.0) + 10.0); + double t3 = d3 * d3 * d3 * (d3 * (d3 * 6.0 - 15.0) + 10.0); + + i1 &= 0xff; + i2 &= 0xff; + i3 &= 0xff; + + int a1 = rnd->d[i1] + i2; + int a2 = rnd->d[a1] + i3; + int a3 = rnd->d[a1 + 1] + i3; + int b1 = rnd->d[i1 + 1] + i2; + int b2 = rnd->d[b1] + i3; + int b3 = rnd->d[b1 + 1] + i3; + + double l1 = indexedLerp(rnd->d[a2], d1, d2, d3); + double l2 = indexedLerp(rnd->d[b2], d1 - 1, d2, d3); + double l3 = indexedLerp(rnd->d[a3], d1, d2 - 1, d3); + double l4 = indexedLerp(rnd->d[b3], d1 - 1, d2 - 1, d3); + double l5 = indexedLerp(rnd->d[a2 + 1], d1, d2, d3 - 1); + double l6 = indexedLerp(rnd->d[b2 + 1], d1 - 1, d2, d3 - 1); + double l7 = indexedLerp(rnd->d[a3 + 1], d1, d2 - 1, d3 - 1); + double l8 = indexedLerp(rnd->d[b3 + 1], d1 - 1, d2 - 1, d3 - 1); + + l1 = lerp(t1, l1, l2); + l3 = lerp(t1, l3, l4); + l5 = lerp(t1, l5, l6); + l7 = lerp(t1, l7, l8); + + l1 = lerp(t2, l1, l3); + l5 = lerp(t2, l5, l7); + + return lerp(t3, l1, l5); +} + +void mapOceanTemp(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int x, z; + OceanRnd *rnd = l->oceanRnd; + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + double tmp = getOceanTemp(rnd, (x + areaX) / 8.0, (z + areaZ) / 8.0, 0); + + if (tmp > 0.4) + out[x + z * areaWidth] = warmOcean; + else if (tmp > 0.2) + out[x + z * areaWidth] = lukewarmOcean; + else if (tmp < -0.4) + out[x + z * areaWidth] = frozenOcean; + else if (tmp < -0.2) + out[x + z * areaWidth] = coldOcean; + else + out[x + z * areaWidth] = ocean; + } + } +} + +/* Warning: this function is horribly slow compared to other layers! */ +void mapOceanMix(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + int landX = (int) areaX - 8, landZ = (int) areaZ - 8; + int landWidth = areaWidth + 17, landHeight = areaHeight + 17; + int *map1, *map2; + + if (l->p2 == nullptr) { + printf("mapOceanMix() requires two parents! Use setupMultiLayer()\n"); + exit(1); + } + + l->p->getMap(l->p, out, landX, landZ, landWidth, landHeight); + map1 = (int *) malloc(landWidth * landHeight * sizeof(int)); + memcpy(map1, out, landWidth * landHeight * sizeof(int)); + + l->p2->getMap(l->p2, out, areaX, areaZ, areaWidth, areaHeight); + map2 = (int *) malloc(areaWidth * areaHeight * sizeof(int)); + memcpy(map2, out, areaWidth * areaHeight * sizeof(int)); + + + int x, z; + + for (z = 0; z < areaHeight; z++) { + for (x = 0; x < areaWidth; x++) { + int landID = map1[(x + 8) + (z + 8) * landWidth]; + int oceanID = map2[x + z * areaWidth]; + + if (!isOceanic(landID)) { + out[x + z * areaWidth] = landID; + continue; + } + + for (int i = -8; i <= 8; i += 4) { + for (int j = -8; j <= 8; j += 4) { + int nearbyID = map1[(x + i + 8) + (z + j + 8) * landWidth]; + + if (isOceanic(nearbyID)) continue; + + if (oceanID == warmOcean) { + out[x + z * areaWidth] = lukewarmOcean; + goto loop_x; + } + + if (oceanID == frozenOcean) { + out[x + z * areaWidth] = coldOcean; + goto loop_x; + } + } + } + + if (landID == deepOcean) { + if (oceanID == lukewarmOcean) { + out[x + z * areaWidth] = lukewarmDeepOcean; + continue; + } + if (oceanID == ocean) { + out[x + z * areaWidth] = deepOcean; + continue; + } + if (oceanID == coldOcean) { + out[x + z * areaWidth] = coldDeepOcean; + continue; + } + if (oceanID == frozenOcean) { + out[x + z * areaWidth] = frozenDeepOcean; + continue; + } + } + + out[x + z * areaWidth] = oceanID; + + loop_x:; + } + } + + free(map1); + free(map2); +} + + +void mapVoronoiZoom(Layer *l, int *__restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight) { + areaX -= 2; + areaZ -= 2; + int pX = (int) areaX >> 2; + int pZ = (int) areaZ >> 2; + int pWidth = (areaWidth >> 2) + 2; + int pHeight = (areaHeight >> 2) + 2; + int newWidth = (pWidth - 1) << 2; + int newHeight = (pHeight - 1) << 2; + int x, z, i, j; + int *buf = (int *) malloc((newWidth + 1) * (newHeight + 1) * sizeof(*buf)); + + l->p->getMap(l->p, out, pX, pZ, pWidth, pHeight); + + for (z = 0; z < pHeight - 1; z++) { + int v00 = out[(z + 0) * pWidth]; + int v01 = out[(z + 1) * pWidth]; + + for (x = 0; x < pWidth - 1; x++) { + setChunkSeed(l, (x + pX) << 2, (z + pZ) << 2); + double da1 = (mcNextInt(l, 1024) / 1024.0 - 0.5) * 3.6; + double da2 = (mcNextInt(l, 1024) / 1024.0 - 0.5) * 3.6; + + setChunkSeed(l, (x + pX + 1) << 2, (z + pZ) << 2); + double db1 = (mcNextInt(l, 1024) / 1024.0 - 0.5) * 3.6 + 4.0; + double db2 = (mcNextInt(l, 1024) / 1024.0 - 0.5) * 3.6; + + setChunkSeed(l, (x + pX) << 2, (z + pZ + 1) << 2); + double dc1 = (mcNextInt(l, 1024) / 1024.0 - 0.5) * 3.6; + double dc2 = (mcNextInt(l, 1024) / 1024.0 - 0.5) * 3.6 + 4.0; + + setChunkSeed(l, (x + pX + 1) << 2, (z + pZ + 1) << 2); + double dd1 = (mcNextInt(l, 1024) / 1024.0 - 0.5) * 3.6 + 4.0; + double dd2 = (mcNextInt(l, 1024) / 1024.0 - 0.5) * 3.6 + 4.0; + + int v10 = out[x + 1 + (z + 0) * pWidth] & 255; + int v11 = out[x + 1 + (z + 1) * pWidth] & 255; + + for (j = 0; j < 4; j++) { + int idx = ((z << 2) + j) * newWidth + (x << 2); + + for (i = 0; i < 4; i++) { + double da = (j - da2) * (j - da2) + (i - da1) * (i - da1); + double db = (j - db2) * (j - db2) + (i - db1) * (i - db1); + double dc = (j - dc2) * (j - dc2) + (i - dc1) * (i - dc1); + double dd = (j - dd2) * (j - dd2) + (i - dd1) * (i - dd1); + + if (da < db && da < dc && da < dd) { + buf[idx++] = v00; + } else if (db < da && db < dc && db < dd) { + buf[idx++] = v10; + } else if (dc < da && dc < db && dc < dd) { + buf[idx++] = v01; + } else { + buf[idx++] = v11; + } + } + } + + v00 = v10; + v01 = v11; + } + } + + for (z = 0; z < areaHeight; z++) { + memcpy(&out[z * areaWidth], &buf[(z + (areaZ & 3)) * newWidth + (areaX & 3)], areaWidth * sizeof(int)); + } + + free(buf); +} + + diff --git a/generationByCubitect/layers.hpp b/generationByCubitect/layers.hpp new file mode 100644 index 0000000..f3cda3a --- /dev/null +++ b/generationByCubitect/layers.hpp @@ -0,0 +1,473 @@ +#ifndef LAYER_H_ +#define LAYER_H_ + +#include "javarnd.h" + +#include +#include +#include + + +#if defined USE_SIMD && __AVX2__ +#include +#include +#include +#warning "Using AVX2 extensions." +#elif defined USE_SIMD && defined __SSE4_2__ +#include +#include +#warning "Using SSE4.2 extensions." +#else +//#warning "Using no SIMD extensions." +#endif + +#define STRUCT(S) typedef struct S S; struct S + +#define OPT_O2 __attribute__((optimize("O2"))) + + +enum BiomeID +{ + none = -1, + ocean = 0, plains, desert, extremeHills, forest, taiga, swampland, river, hell, sky, // 0-9 + frozenOcean, frozenRiver, icePlains, iceMountains, mushroomIsland, mushroomIslandShore, beach, desertHills, forestHills, taigaHills, // 10-19 + extremeHillsEdge, jungle, jungleHills, jungleEdge, deepOcean, stoneBeach, coldBeach, birchForest, birchForestHills, roofedForest, // 20-29 + coldTaiga, coldTaigaHills, megaTaiga, megaTaigaHills, extremeHillsPlus, savanna, savannaPlateau, mesa, mesaPlateau_F, mesaPlateau, // 30-39 + // 1.13 + skyIslandLow, skyIslandMedium, skyIslandHigh, skyIslandBarren, warmOcean, lukewarmOcean, coldOcean, warmDeepOcean, lukewarmDeepOcean, coldDeepOcean, // 40-49 + frozenDeepOcean, + BIOME_NUM +}; + +enum BiomeType +{ + Ocean, Plains, Desert, Hills, Forest, Taiga, Swamp, River, Hell, Sky, Snow, MushroomIsland, Beach, Jungle, StoneBeach, Savanna, Mesa, BTYPE_NUM +}; + +enum BiomeTempCategory +{ + Oceanic, Warm, Lush, Cold, Freezing, Special +}; + + +STRUCT(Biome) +{ + int id; + int type; + double height; + double temp; + int tempCat; +}; + +STRUCT(OceanRnd) +{ + int d[512]; + double a, b, c; +}; + +STRUCT(Layer) +{ + int64_t baseSeed; // Generator seed (depends only on layer hierarchy) + int64_t worldSeed; // based on the seed of the world + + int64_t chunkSeed; // randomiser seed + + OceanRnd *oceanRnd; // world seed dependent data for ocean temperatures + + int scale; // map scale of this layer (map entry = scale x scale blocks) + + void (*getMap)(Layer *layer, int *out, long long x, long long z, int w, int h); + + Layer *p, *p2; // parent layers +}; + + +//============================================================================== +// Essentials +//============================================================================== + +extern Biome biomes[256]; + + +/* initBiomes() has to be called before any of the generators can be used */ +void initBiomes(); + +/* Applies the given world seed to the layer and all dependent layers. */ +void setWorldSeed(Layer *layer, int64_t seed); + + +//============================================================================== +// Static Helpers +//============================================================================== + +static inline int getBiomeType(int id) +{ + return biomes[id & 0xff].type; +} + +static inline int biomeExists(int id) +{ + return !(biomes[id & 0xff].id & (~0xff)); +} + +static inline int getTempCategory(int id) +{ + return biomes[id & 0xff].tempCat; +} + + +static inline int equalOrPlateau(int id1, int id2) +{ + if (id1 == id2) return 1; + if (id1 == mesaPlateau_F || id1 == mesaPlateau) return id2 == mesaPlateau_F || id2 == mesaPlateau; + if (!biomeExists(id1) || !biomeExists(id2)) return 0; + // adjust for asymmetric equality (workaround to simulate a bug in the MC java code) + if (id1 >= 128 || id2 >= 128) { + // skip biomes that did not overload the isEqualTo() method + if (id2 == 130 || id2 == 133 || id2 == 134 || id2 == 149 || id2 == 151 || id2 == 155 || + id2 == 156 || id2 == 157 || id2 == 158 || id2 == 163 || id2 == 164) return 0; + } + return getBiomeType(id1) == getBiomeType(id2); +} + +static inline int canBeNeighbors(int id1, int id2) +{ + if (equalOrPlateau(id1, id2)) return 1; + if (!biomeExists(id1) || !biomeExists(id2)) return 0; + int tempCat1 = getTempCategory(id1); if (tempCat1 == Lush) return 1; + int tempCat2 = getTempCategory(id2); if (tempCat2 == Lush) return 1; + return tempCat1 == tempCat2; +} + +static inline int isShallowOcean(int id) +{ + return id == ocean || id == frozenOcean || + id == warmOcean || id == lukewarmOcean || id == coldOcean; +} + +static inline int isOceanic(int id) +{ + switch(id) + { + case ocean: + case deepOcean: + case warmOcean: + case warmDeepOcean: + case lukewarmOcean: + case lukewarmDeepOcean: + case coldOcean: + case coldDeepOcean: + case frozenOcean: + case frozenDeepOcean: + return 1; + default: + return 0; + } +} + + +static inline int isBiomeSnowy(int id) +{ + return biomeExists(id) && biomes[id&0xff].temp < 0.1; +} + +static inline int mcNextInt(Layer *layer, int mod) +{ + int ret = (int)((layer->chunkSeed >> 24) % (int64_t)mod); + + if (ret < 0) + { + ret += mod; + } + + layer->chunkSeed *= layer->chunkSeed * 6364136223846793005LL + 1442695040888963407LL; + layer->chunkSeed += layer->worldSeed; + return ret; +} + + + +static inline int64_t processWorldSeed( int64_t ws, const int64_t bs) +{ + ws *= ws * 6364136223846793005LL + 1442695040888963407LL; + ws += bs; + ws *= ws * 6364136223846793005LL + 1442695040888963407LL; + ws += bs; + ws *= ws * 6364136223846793005LL + 1442695040888963407LL; + ws += bs; + ws *= ws * 6364136223846793005LL + 1442695040888963407LL; + return ws; +} + +static inline int64_t getChunkSeed(int64_t ss, const int64_t x, const int64_t z) +{ + ss += x; + ss *= ss * 6364136223846793005LL + 1442695040888963407LL; + ss += z; + ss *= ss * 6364136223846793005LL + 1442695040888963407LL; + ss += x; + ss *= ss * 6364136223846793005LL + 1442695040888963407LL; + ss += z; + return ss; +} + + + +static inline void setChunkSeed(Layer *layer, int64_t chunkX, int64_t chunkZ) +{ + layer->chunkSeed = layer->worldSeed; + layer->chunkSeed *= layer->chunkSeed * 6364136223846793005LL + 1442695040888963407LL; + layer->chunkSeed += chunkX; + layer->chunkSeed *= layer->chunkSeed * 6364136223846793005LL + 1442695040888963407LL; + layer->chunkSeed += chunkZ; + layer->chunkSeed *= layer->chunkSeed * 6364136223846793005LL + 1442695040888963407LL; + layer->chunkSeed += chunkX; + layer->chunkSeed *= layer->chunkSeed * 6364136223846793005LL + 1442695040888963407LL; + layer->chunkSeed += chunkZ; +} + +static inline void setBaseSeed(Layer *layer, int64_t seed) +{ + layer->baseSeed = seed; + layer->baseSeed *= layer->baseSeed * 6364136223846793005LL + 1442695040888963407LL; + layer->baseSeed += seed; + layer->baseSeed *= layer->baseSeed * 6364136223846793005LL + 1442695040888963407LL; + layer->baseSeed += seed; + layer->baseSeed *= layer->baseSeed * 6364136223846793005LL + 1442695040888963407LL; + layer->baseSeed += seed; + + layer->p = nullptr; + layer->worldSeed = 0; + layer->chunkSeed = 0; +} + +#if defined USE_SIMD && __AVX2__ + +static inline __m256i set8ChunkSeeds(int ws, __m256i xs, __m256i zs) +{ + __m256i out = _mm256_set1_epi32(ws); + __m256i mul = _mm256_set1_epi32(1284865837); + __m256i add = _mm256_set1_epi32(4150755663); + out = _mm256_add_epi32(xs, _mm256_mullo_epi32(out, _mm256_add_epi32(add, _mm256_mullo_epi32(out, mul)))); + out = _mm256_add_epi32(zs, _mm256_mullo_epi32(out, _mm256_add_epi32(add, _mm256_mullo_epi32(out, mul)))); + out = _mm256_add_epi32(xs, _mm256_mullo_epi32(out, _mm256_add_epi32(add, _mm256_mullo_epi32(out, mul)))); + return _mm256_add_epi32(zs, _mm256_mullo_epi32(out, _mm256_add_epi32(add, _mm256_mullo_epi32(out, mul)))); +} + +static inline __m256i mc8NextInt(__m256i* cs, int ws, int mask) +{ + __m256i and = _mm256_set1_epi32(mask); + __m256i ret = _mm256_and_si256(and, _mm256_srli_epi32(*cs, 24)); + *cs = _mm256_add_epi32(_mm256_set1_epi32(ws), _mm256_mullo_epi32(*cs, _mm256_add_epi32(_mm256_set1_epi32(4150755663), _mm256_mullo_epi32(*cs, _mm256_set1_epi32(1284865837))))); + return _mm256_add_epi32(ret, _mm256_and_si256(and, _mm256_cmpgt_epi32(_mm256_set1_epi32(0), ret)));; +} + +static inline __m256i select8Random2(__m256i* cs, int ws, __m256i a1, __m256i a2) +{ + __m256i cmp = _mm256_cmpeq_epi32(_mm256_set1_epi32(0), mc8NextInt(cs, ws, 0x1)); + return _mm256_or_si256(_mm256_and_si256(cmp, a1), _mm256_andnot_si256(cmp, a2)); +} + +static inline __m256i select8Random4(__m256i* cs, int ws, __m256i a1, __m256i a2, __m256i a3, __m256i a4) +{ + __m256i val = mc8NextInt(cs, ws, 0x3); + __m256i v2 = _mm256_set1_epi32(2); + __m256i cmp1 = _mm256_cmpeq_epi32(val, _mm256_set1_epi32(0)); + __m256i cmp2 = _mm256_cmpeq_epi32(v2, val); + __m256i cmp3 = _mm256_cmpgt_epi32(v2, val); + return _mm256_or_si256( + _mm256_and_si256(cmp3, _mm256_or_si256(_mm256_and_si256(cmp1, a1), _mm256_andnot_si256(cmp1, a2))), + _mm256_andnot_si256(cmp3, _mm256_or_si256(_mm256_and_si256(cmp2, a3), _mm256_andnot_si256(cmp2, a4))) + ); +} + +static inline __m256i select8ModeOrRandom(__m256i* cs, int ws, __m256i a1, __m256i a2, __m256i a3, __m256i a4) +{ + __m256i cmp1 = _mm256_cmpeq_epi32(a1, a2); + __m256i cmp2 = _mm256_cmpeq_epi32(a1, a3); + __m256i cmp3 = _mm256_cmpeq_epi32(a1, a4); + __m256i cmp4 = _mm256_cmpeq_epi32(a2, a3); + __m256i cmp5 = _mm256_cmpeq_epi32(a2, a4); + __m256i cmp6 = _mm256_cmpeq_epi32(a3, a4); + __m256i isa1 = _mm256_or_si256( + _mm256_andnot_si256(cmp6, cmp1), + _mm256_or_si256 ( + _mm256_andnot_si256(cmp5, cmp2), + _mm256_andnot_si256(cmp4, cmp3) + ) + ); + __m256i isa2 = _mm256_or_si256( + _mm256_andnot_si256(cmp3, cmp4), + _mm256_andnot_si256(cmp2, cmp5) + ); + __m256i isa3 = _mm256_andnot_si256(cmp1, cmp6); + + return _mm256_or_si256( + _mm256_andnot_si256( + _mm256_or_si256( + isa1, + _mm256_or_si256(isa2, isa3) + ), + select8Random4(cs, ws, a1, a2, a3, a4) + ), + _mm256_or_si256( + _mm256_and_si256(isa1, a1), + _mm256_or_si256( + _mm256_and_si256(isa2, a2), + _mm256_and_si256(isa3, a3) + ) + ) + ); +} + +#elif defined USE_SIMD && defined __SSE4_2__ + +static inline __m128i set4ChunkSeeds(int ws, __m128i xs, __m128i zs) +{ + __m128i out = _mm_set1_epi32(ws); + __m128i mul = _mm_set1_epi32(1284865837); + __m128i add = _mm_set1_epi32(4150755663); + out = _mm_add_epi32(xs, _mm_mullo_epi32(out, _mm_add_epi32(add, _mm_mullo_epi32(out, mul)))); + out = _mm_add_epi32(zs, _mm_mullo_epi32(out, _mm_add_epi32(add, _mm_mullo_epi32(out, mul)))); + out = _mm_add_epi32(xs, _mm_mullo_epi32(out, _mm_add_epi32(add, _mm_mullo_epi32(out, mul)))); + return _mm_add_epi32(zs, _mm_mullo_epi32(out, _mm_add_epi32(add, _mm_mullo_epi32(out, mul)))); +} + +static inline __m128i mc4NextInt(__m128i* cs, int ws, int mask) +{ + __m128i and = _mm_set1_epi32(mask); + __m128i ret = _mm_and_si128(and, _mm_srli_epi32(*cs, 24)); + *cs = _mm_add_epi32( _mm_set1_epi32(ws), _mm_mullo_epi32(*cs, _mm_add_epi32(_mm_set1_epi32(4150755663), _mm_mullo_epi32(*cs, _mm_set1_epi32(1284865837))))); + return _mm_add_epi32(ret, _mm_and_si128(and, _mm_cmplt_epi32(ret, _mm_set1_epi32(0))));; +} + +static inline __m128i select4Random2(__m128i* cs, int ws, __m128i a1, __m128i a2) +{ + __m128i cmp = _mm_cmpeq_epi32(_mm_set1_epi32(0), mc4NextInt(cs, ws, 0x1)); + return _mm_or_si128(_mm_and_si128(cmp, a1), _mm_andnot_si128(cmp, a2)); +} + +static inline __m128i select4Random4(__m128i* cs, int ws, __m128i a1, __m128i a2, __m128i a3, __m128i a4) +{ + __m128i val = mc4NextInt(cs, ws, 0x3); + __m128i v2 = _mm_set1_epi32(2); + __m128i cmp1 = _mm_cmpeq_epi32(val, _mm_set1_epi32(0)); + __m128i cmp2 = _mm_cmpeq_epi32(val, v2); + __m128i cmp3 = _mm_cmplt_epi32(val, v2); + return _mm_or_si128( + _mm_and_si128(cmp3, _mm_or_si128(_mm_and_si128(cmp1, a1), _mm_andnot_si128(cmp1, a2))), + _mm_andnot_si128(cmp3, _mm_or_si128(_mm_and_si128(cmp2, a3), _mm_andnot_si128(cmp2, a4))) + ); +} + +static inline __m128i select4ModeOrRandom(__m128i* cs, int ws, __m128i a1, __m128i a2, __m128i a3, __m128i a4) +{ + //((a == b)&(c != d) | (a == c)&(b != d) | (a == d)&(b != c))&a | ((b == c)&(a != d) | (b == d)&(a != c))&b | ((c == d)&(a != b))&c + __m128i cmp1 = _mm_cmpeq_epi32(a1, a2); + __m128i cmp2 = _mm_cmpeq_epi32(a1, a3); + __m128i cmp3 = _mm_cmpeq_epi32(a1, a4); + __m128i cmp4 = _mm_cmpeq_epi32(a2, a3); + __m128i cmp5 = _mm_cmpeq_epi32(a2, a4); + __m128i cmp6 = _mm_cmpeq_epi32(a3, a4); + __m128i isa1 = _mm_or_si128( + _mm_andnot_si128(cmp6, cmp1), + _mm_or_si128 ( + _mm_andnot_si128(cmp5, cmp2), + _mm_andnot_si128(cmp4, cmp3) + ) + ); + __m128i isa2 = _mm_or_si128( + _mm_andnot_si128(cmp3, cmp4), + _mm_andnot_si128(cmp2, cmp5) + ); + __m128i isa3 = _mm_andnot_si128(cmp1, cmp6); + return _mm_or_si128( + _mm_andnot_si128( + _mm_or_si128( + isa1, + _mm_or_si128(isa2, isa3) + ), + select4Random4(cs, ws, a1, a2, a3, a4) + ), + _mm_or_si128( + _mm_and_si128(isa1, a1), + _mm_or_si128( + _mm_and_si128(isa2, a2), + _mm_and_si128(isa3, a3) + ) + ) + ); +} + +#else + +static inline int selectRandom2(Layer *l, int a1, int a2) +{ + int i = mcNextInt(l, 2); + return i == 0 ? a1 : a2; +} + +static inline int selectRandom4(Layer *l, int a1, int a2, int a3, int a4) +{ + int i = mcNextInt(l, 4); + return i == 0 ? a1 : i == 1 ? a2 : i == 2 ? a3 : a4; +} + +static inline int selectModeOrRandom(Layer *l, int a1, int a2, int a3, int a4) +{ + int rndarg = selectRandom4(l, a1, a2, a3, a4); + + if (a2 == a3 && a3 == a4) return a2; + if (a1 == a2 && a1 == a3) return a1; + if (a1 == a2 && a1 == a4) return a1; + if (a1 == a3 && a1 == a4) return a1; + if (a1 == a2 && a3 != a4) return a1; + if (a1 == a3 && a2 != a4) return a1; + if (a1 == a4 && a2 != a3) return a1; + if (a2 == a3 && a1 != a4) return a2; + if (a2 == a4 && a1 != a3) return a2; + if (a3 == a4 && a1 != a2) return a3; + + return rndarg; +} + +#endif + +//============================================================================== +// Layers +//============================================================================== + +// A null layer does nothing, and can be used to apply a layer to existing data. +void mapNull(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +// A skip layer simply calls its first parent without modification. +// This can be used as an easy way to skip a layer in a generator. +void mapSkip(Layer *l, int * __restrict out, long long x, long long z, int w, int h); + +void mapIsland(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapZoom(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapAddIsland(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapRemoveTooMuchOcean(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapAddSnow(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapCoolWarm(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapHeatIce(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapSpecial(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapAddMushroomIsland(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapDeepOcean(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapBiome(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapRiverInit(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapBiomeEdge(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapHills(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapRiver(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapSmooth(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapRareBiome(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapShore(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapRiverMix(Layer *l, int * __restrict out, long long x, long long z, int w, int h); + +// 1.13 layers +void mapHills113(Layer *l, int * __restrict out, long long x, long long z, int w, int h); +void mapOceanTemp(Layer *l, int * __restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight); +void mapOceanMix(Layer *l, int * __restrict out, long long areaX, long long areaZ, int areaWidth, int areaHeight); + +void mapVoronoiZoom(Layer *l, int * __restrict out, long long x, long long z, int w, int h); + +#endif /* LAYER_H_ */ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..fe67e6c --- /dev/null +++ b/main.cpp @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include "generationByCubitect/generator.hpp" +#include "xmap.h" + + +constexpr unsigned int str2int(const char *str, int h = 0) { + return !str[h] ? 5381 : (str2int(str, h + 1) * 33) ^ (unsigned int) (str[h]); +} + +versions parse_version(std::string &s) { + versions v; + switch (str2int(s.c_str())) { + case str2int("1.7"): + v = MC_1_7; + break; + case str2int("1.8"): + v = MC_1_8; + break; + case str2int("1.9"): + v = MC_1_9; + break; + case str2int("1.10"): + v = MC_1_10; + break; + case str2int("1.11"): + v = MC_1_11; + break; + case str2int("1.12"): + v = MC_1_12; + break; + case str2int("1.13"): + v = MC_1_13; + break; + case str2int("1.13.2"): + v = MC_1_13_2; + break; + case str2int("1.14"): + v = MC_1_14; + break; + default: + v = MC_LEG; + break; + } + return v; +} + +int main(int argc, char *argv[]) { + int sizeX = 2048; + int sizeZ = 2048; + int x = -1024; + int z = -1024; + initBiomes(); + + + if (argc <= 1) { + std::cout << "Program should be called with at least the version number, possible ones are 1.7, 1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.13.2, 1.14 ." << std::endl; + exit(1); + } + std::string arg1(argv[1]); + versions v = parse_version(arg1); + if (v == MC_LEG) { + std::cout << "Program should be called with a valid version number, possible ones are 1.7, 1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.13.2, 1.14 ." << std::endl; + exit(1); + } + + LayerStack g = setupGenerator(v); + unsigned char biomeColours[256][3]; + initBiomeColours(biomeColours); + if (argc == 2) { + + std::cout << "Using the in.txt to produce high quality images" << std::endl; + std::string line; + std::ifstream datafile("in.txt", std::ios::in); + if (datafile.fail() || !datafile) { + printf("file was not loaded\n"); + throw std::runtime_error("file was not loaded"); + } + + while (std::getline(datafile, line)) { + int64_t seed = stoll(line, nullptr, 10); + std::string name = "img/" + std::to_string(seed) + ".png"; + int n = name.length(); + char char_array[n + 1]; + strcpy(char_array, name.c_str()); + applySeed(&g, (int64_t) seed); + + viewmap(&g.layers[g.layerNum - 1], biomeColours, x, z, sizeX, sizeZ, 1, char_array); + } + } else { + int64_t seed = 1; + std::string name = "img/" + std::to_string(seed) + ".png"; + int n = name.length(); + char char_array[n + 1]; + strcpy(char_array, name.c_str()); + applySeed(&g, (int64_t) seed); + viewmap(&g.layers[g.layerNum - 1], biomeColours, x, z, sizeX, sizeZ, 1, char_array); + + } + +} + + +int genPerLayer(int64_t seed, versions version, long x, long z, int sizeX, int sizeZ) { + + /*viewmap(&g.layers[42],biomeColours,x,z,sizeX,sizeZ,1,"rivermix.png"); + viewmap(&g.layers[24],biomeColours,x,z,sizeX,sizeZ,1,"hills.png"); + viewmap(&g.layers[18],biomeColours,x,z,sizeX,sizeZ,1,"biome.png"); + viewmap(&g.layers[21],biomeColours,x,z,sizeX,sizeZ,1,"biomeedge.png"); + viewmap(&g.layers[22],biomeColours,x,z,sizeX,sizeZ,1,"riverinit.png"); + viewmap(&g.layers[41],biomeColours,x,z,sizeX,sizeZ,1,"smooth.png"); + viewmap(&g.layers[40],biomeColours,x,z,sizeX,sizeZ,1,"river.png"); + viewmap(&g.layers[27],biomeColours,x,z,sizeX,sizeZ,1,"rare.png"); + viewmap(&g.layers[30],biomeColours,x,z,sizeX,sizeZ,1,"shore.png"); + viewmap(&g.layers[0],biomeColours,x,z,sizeX,sizeZ,1,"smoothbiome.png");*/ + + /*viewmap(&g.layers[0],biomeColours,x,z,sizeX,sizeZ,1,"1island.png"); + viewmap(&g.layers[1],biomeColours,x,z,sizeX,sizeZ,1,"2zoom.png"); + viewmap(&g.layers[2],biomeColours,x,z,sizeX,sizeZ,1,"3island1.png"); + viewmap(&g.layers[3],biomeColours,x,z,sizeX,sizeZ,1,"4zoom1.png"); + viewmap(&g.layers[4],biomeColours,x,z,sizeX,sizeZ,1,"5island2.png"); + viewmap(&g.layers[5],biomeColours,x,z,sizeX,sizeZ,1,"6island3.png"); + viewmap(&g.layers[6],biomeColours,x,z,sizeX,sizeZ,1,"7island4.png"); + viewmap(&g.layers[7],biomeColours,x,z,sizeX,sizeZ,1,"8toomuch.png"); + viewmap(&g.layers[8],biomeColours,x,z,sizeX,sizeZ,1,"9snow.png"); + viewmap(&g.layers[9],biomeColours,x,z,sizeX,sizeZ,1,"9zisland5.png"); + viewmap(&g.layers[10],biomeColours,x,z,sizeX,sizeZ,1,"9zzcoolwarm.png"); + viewmap(&g.layers[11],biomeColours,x,z,sizeX,sizeZ,1,"9zzzheatice.png"); + viewmap(&g.layers[12],biomeColours,x,z,sizeX,sizeZ,1,"9zzzzSpecial.png"); + viewmap(&g.layers[13],biomeColours,x,z,sizeX,sizeZ,1,"9zzzzzoom2.png"); + viewmap(&g.layers[14],biomeColours,x,z,sizeX,sizeZ,1,"9zzzzzzoom3.png"); + viewmap(&g.layers[15],biomeColours,x,z,sizeX,sizeZ,1,"9zzzzzzzisland6.png"); + viewmap(&g.layers[16],biomeColours,x,z,sizeX,sizeZ,1,"9zzzzzzzzmoosh.png"); + viewmap(&g.layers[17],biomeColours,x,z,sizeX,sizeZ,1,"9zzzzzzzzzdeepocean.png"); + */ + return 0; +} \ No newline at end of file diff --git a/xmap.cpp b/xmap.cpp new file mode 100644 index 0000000..43fca21 --- /dev/null +++ b/xmap.cpp @@ -0,0 +1,325 @@ +#include "xmap.h" +#include +#include + +#include +#include +#include +#include + + +// This takes the float value 'val', converts it to red, green & blue values, then +// sets those values into the image memory buffer location pointed to by 'ptr' +inline void setRGB(png_byte *ptr, float val); + +// This function actually writes out the PNG image file. The string 'title' is +// also written into the image file +int writeImage(const char* filename, int width, int height, unsigned int *buffer, char* title); +/* Global biome colour table. */ + + +void setBiomeColour(unsigned char biomeColour[256][3], int biome, + unsigned char r, unsigned char g, unsigned char b) +{ + biomeColour[biome][0] = r; + biomeColour[biome][1] = g; + biomeColour[biome][2] = b; +} + +void initBiomeColours(unsigned char biomeColours[256][3]) +{ + // This colouring scheme is taken from the AMIDST program: + // https://github.com/toolbox4minecraft/amidst + // https://sourceforge.net/projects/amidst.mirror/ + + memset(biomeColours, 0, 256*3); + + setBiomeColour(biomeColours, ocean, 0, 0, 112); + setBiomeColour(biomeColours, plains,141, 179, 96); + setBiomeColour(biomeColours, desert, 250, 148, 24); + setBiomeColour(biomeColours, extremeHills, 96, 96, 96); + setBiomeColour(biomeColours, forest, 5, 102, 33); + setBiomeColour(biomeColours, taiga, 11, 102, 89); + setBiomeColour(biomeColours, swampland, 7, 249, 178); + setBiomeColour(biomeColours, river, 0, 0, 255); + setBiomeColour(biomeColours, hell, 255, 0, 0); + setBiomeColour(biomeColours, sky, 128, 128, 255); + setBiomeColour(biomeColours, frozenOcean, 112, 112, 214); + setBiomeColour(biomeColours, frozenRiver, 160, 160, 255); + setBiomeColour(biomeColours, icePlains, 255, 255, 255); + setBiomeColour(biomeColours, iceMountains, 160, 160, 160); + setBiomeColour(biomeColours, mushroomIsland, 255, 0, 255); + setBiomeColour(biomeColours, mushroomIslandShore, 160, 0, 255); + setBiomeColour(biomeColours, beach, 250, 222, 85); + setBiomeColour(biomeColours, desertHills, 210, 95, 18); + setBiomeColour(biomeColours, forestHills, 34, 85, 28); + setBiomeColour(biomeColours, taigaHills, 22, 57, 51); + setBiomeColour(biomeColours, extremeHillsEdge, 114, 120, 154); + setBiomeColour(biomeColours, jungle, 83, 123, 9); + setBiomeColour(biomeColours, jungleHills, 44, 66, 5); + setBiomeColour(biomeColours, jungleEdge, 98, 139, 23); + setBiomeColour(biomeColours, deepOcean, 0, 0, 48); + setBiomeColour(biomeColours, stoneBeach, 162, 162, 132); + setBiomeColour(biomeColours, coldBeach, 250, 240, 192); + setBiomeColour(biomeColours, birchForest, 48, 116, 68); + setBiomeColour(biomeColours, birchForestHills, 31, 95, 50); + setBiomeColour(biomeColours, roofedForest, 64, 81, 26); + setBiomeColour(biomeColours, coldTaiga, 49, 85, 74); + setBiomeColour(biomeColours, coldTaigaHills, 36, 63, 54); + setBiomeColour(biomeColours, megaTaiga, 89, 102, 81); + setBiomeColour(biomeColours, megaTaigaHills, 69, 79, 62); + setBiomeColour(biomeColours, extremeHillsPlus, 80, 112, 80); + setBiomeColour(biomeColours, savanna, 189, 178, 95); + setBiomeColour(biomeColours, savannaPlateau, 167, 157, 100); + setBiomeColour(biomeColours, mesa, 217, 69, 21); + setBiomeColour(biomeColours, mesaPlateau_F, 176, 151, 101); + setBiomeColour(biomeColours, mesaPlateau, 202, 140, 101); + + setBiomeColour(biomeColours, warmOcean, 0, 0, 172); + setBiomeColour(biomeColours, lukewarmOcean, 0, 0, 144); + setBiomeColour(biomeColours, coldOcean, 32, 32, 112); + setBiomeColour(biomeColours, warmDeepOcean, 0, 0, 80); + setBiomeColour(biomeColours, lukewarmDeepOcean, 0, 0, 64); + setBiomeColour(biomeColours, coldDeepOcean, 32, 32, 56); + setBiomeColour(biomeColours, frozenDeepOcean, 64, 64, 144); + + setBiomeColour(biomeColours, ocean+128, 0, 0, 112); + setBiomeColour(biomeColours, plains+128, 141, 179, 96); + setBiomeColour(biomeColours, desert+128, 250, 148, 24); + setBiomeColour(biomeColours, extremeHills+128, 96, 96, 96); + setBiomeColour(biomeColours, forest+128, 5, 102, 33); + setBiomeColour(biomeColours, taiga+128, 11, 102, 89); + setBiomeColour(biomeColours, swampland+128, 7, 249, 178); + setBiomeColour(biomeColours, river+128, 0, 0, 255); + setBiomeColour(biomeColours, hell+128, 255, 0, 0); + setBiomeColour(biomeColours, sky+128, 128, 128, 255); + setBiomeColour(biomeColours, frozenOcean+128, 144, 144, 160); + setBiomeColour(biomeColours, frozenRiver+128, 160, 160, 255); + setBiomeColour(biomeColours, icePlains+128, 140, 180, 180); + setBiomeColour(biomeColours, iceMountains+128, 160, 160, 160); + setBiomeColour(biomeColours, mushroomIsland+128, 255, 0, 255); + setBiomeColour(biomeColours, mushroomIslandShore+128, 160, 0, 255); + setBiomeColour(biomeColours, beach+128, 250, 222, 85); + setBiomeColour(biomeColours, desertHills+128, 210, 95, 18); + setBiomeColour(biomeColours, forestHills+128, 34, 85, 28); + setBiomeColour(biomeColours, taigaHills+128, 22, 57, 51); + setBiomeColour(biomeColours, extremeHillsEdge+128, 114, 120, 154); + setBiomeColour(biomeColours, jungle+128, 83, 123, 9); + setBiomeColour(biomeColours, jungleHills+128, 44, 66, 5); + setBiomeColour(biomeColours, jungleEdge+128, 98, 139, 23); + setBiomeColour(biomeColours, deepOcean+128, 0, 0, 48); + setBiomeColour(biomeColours, stoneBeach+128, 162, 162, 132); + setBiomeColour(biomeColours, coldBeach+128, 250, 240, 192); + setBiomeColour(biomeColours, birchForest+128, 48, 116, 68); + setBiomeColour(biomeColours, birchForestHills+128, 31, 95, 50); + setBiomeColour(biomeColours, roofedForest+128, 64, 81, 26); + setBiomeColour(biomeColours, coldTaiga+128, 49, 85, 74); + setBiomeColour(biomeColours, coldTaigaHills+128, 36, 63, 54); + setBiomeColour(biomeColours, megaTaiga+128, 89, 102, 81); + setBiomeColour(biomeColours, megaTaigaHills+128, 69, 79, 62); + setBiomeColour(biomeColours, extremeHillsPlus+128, 80, 112, 80); + setBiomeColour(biomeColours, savanna+128, 189, 178, 95); + setBiomeColour(biomeColours, savannaPlateau+128, 167, 157, 100); + setBiomeColour(biomeColours, mesa+128, 217, 69, 21); + setBiomeColour(biomeColours, mesaPlateau_F+128, 176, 151, 101); + setBiomeColour(biomeColours, mesaPlateau+128, 202, 140, 101); +} + +void initBiomeTypeColours(unsigned char biomeColours[256][3]) +{ + setBiomeColour(biomeColours, Oceanic+44, 0x00, 0x00, 0xa0); + setBiomeColour(biomeColours, Warm+44, 0xff, 0xc0, 0x00); + setBiomeColour(biomeColours, Lush+44, 0x00, 0xa0, 0x00); + setBiomeColour(biomeColours, Cold+44, 0x60, 0x60, 0x60); + setBiomeColour(biomeColours, Freezing+44, 0xff, 0xff, 0xff); +} + + + + + +void getBiomeColourMap(unsigned int *colbuf, const unsigned char biomeColour[256][3], + const int *ints, const unsigned int sx, const unsigned int sy, const unsigned int pixscale) +{ + unsigned int i, j; + int containsInvalidBiomes = 0; + + for(j = 0; j < sy; j++) + { + for(i = 0; i < sx; i++) + { + int id = ints[i*sx+j]; + + unsigned int r, g, b; + if(id < 0 || id >= 256) + { + // This may happen for some intermediate layers + containsInvalidBiomes = 1; + r = biomeColour[id&0x7f][0]-40; r = (r>0xff) ? 0x00 : r&0xff; + g = biomeColour[id&0x7f][1]-40; g = (g>0xff) ? 0x00 : g&0xff; + b = biomeColour[id&0x7f][2]-40; b = (b>0xff) ? 0x00 : b&0xff; + } + else + { + if(id < 128) { + r = biomeColour[id][0]; + g = biomeColour[id][1]; + b = biomeColour[id][2]; + } else { + r = biomeColour[id][0]+40; r = (r>0xff) ? 0xff : r&0xff; + g = biomeColour[id][1]+40; g = (g>0xff) ? 0xff : g&0xff; + b = biomeColour[id][2]+40; b = (b>0xff) ? 0xff : b&0xff; + } + } + + unsigned int m, n; + for(m = 0; m < pixscale; m++){ + for(n = 0; n < pixscale; n++){ + colbuf[(j*pixscale+n) + sy*pixscale*(i*pixscale+m)] = + ((r&0xff) << 16) + ((g&0xff) << 8) + (b&0xff); + } + } + } + } + + if(containsInvalidBiomes) + { + printf("Warning: Ints contain invalid Biome IDs (Is this an intermediate layer?)\n"); + } +} + + +void viewmap(Layer *layer, unsigned char biomeColour[256][3], int areaX, int areaZ, int areaWidth, int areaHeight, unsigned int pixscale,const char* nom) +{ + int *ints = allocCache(layer, areaWidth, areaHeight); + // generate the biome ints + + genArea(layer, ints, areaX, areaZ, areaWidth, areaHeight); + + // Calculate a hash for the area (useful to verify the accuracy of the map) + unsigned int i, hash = 0; + for(i = 0; i < areaWidth*areaHeight; i++) hash = hash ^ (i*(ints[i]+1)); + printf("Hash:%3X\n", hash&0xfff); + + // construct the X11 window + + + + + // convert the biome ints to a colour image + auto *colbuf = (unsigned int *) malloc(sizeof(unsigned int) * + areaWidth*areaHeight*pixscale*pixscale); + + getBiomeColourMap(colbuf, biomeColour, ints, areaWidth, areaHeight, pixscale); + int width = areaWidth; + int height = areaHeight; + + // Create a test image - in this case a Mandelbrot Set fractal + // The output is a 1D array of floats, length: width * height + printf("Creating Image\n"); + + + printf("Saving PNG\n"); + writeImage(nom, width, height, colbuf, "This is my test image"); + +} + + + + + + +inline void setRGB(png_byte *ptr, unsigned int val) +{ + ptr[0] = (val&0xFF0000u)>>16u; ptr[1] = (val&0xFF00u)>>8u; ptr[2] = val&0xFFu; +} + +int writeImage(const char* filename, int width, int height, unsigned int *buffer, char* title) +{ + int code = 0; + FILE *fp = NULL; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_bytep row = NULL; + + // Open file for writing (binary mode) + fp = fopen(filename, "wb"); + if (fp == NULL) { + fprintf(stderr, "Could not open file %s for writing\n", filename); + code = 1; + goto finalise; + } + + // Initialize write structure + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL) { + fprintf(stderr, "Could not allocate write struct\n"); + code = 1; + goto finalise; + } + + // Initialize info structure + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) { + fprintf(stderr, "Could not allocate info struct\n"); + code = 1; + goto finalise; + } + + // Setup Exception handling + if (setjmp(png_jmpbuf(png_ptr))) { + fprintf(stderr, "Error during png creation\n"); + code = 1; + goto finalise; + } + + png_init_io(png_ptr, fp); + + // Write header (8 bit colour depth) + png_set_IHDR(png_ptr, info_ptr, width, height, + 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + // Set title + if (title != NULL) { + png_text title_text; + title_text.compression = PNG_TEXT_COMPRESSION_NONE; + title_text.key = "Title"; + title_text.text = title; + png_set_text(png_ptr, info_ptr, &title_text, 1); + } + + png_write_info(png_ptr, info_ptr); + + // Allocate memory for one row (3 bytes per pixel - RGB) + row = (png_bytep) malloc(3 * width * sizeof(png_byte)); + + // Write image data + int x, y; + for (y=0 ; y +#include +#include +#include + +typedef struct xwin_t +{ + Display *dis; + int screen; + Window win; + GC gc; + + unsigned int *colbuf; + unsigned int sx, sy; + +} xwin_t; + + +void initBiomeColours(unsigned char biomeColours[256][3]); +void initBiomeTypeColours(unsigned char biomeColours[256][3]); + + +xwin_t init_x(unsigned int sx, unsigned int sy, const char *titel); +void close_x(xwin_t w); + +void viewmap(Layer *layer, unsigned char biomeColour[256][3], + int areaX, int areaZ, int areaWidth, int areaHeight, unsigned int pixscale,const char* nom); + + + +#endif \ No newline at end of file