diff --git a/Minecraft.Client/PS3/SPU_Tasks/PerlinNoise/PerlinNoise_SPU.h b/Minecraft.Client/PS3/SPU_Tasks/PerlinNoise/PerlinNoise_SPU.h index e1017615e..a623ecb3c 100644 --- a/Minecraft.Client/PS3/SPU_Tasks/PerlinNoise/PerlinNoise_SPU.h +++ b/Minecraft.Client/PS3/SPU_Tasks/PerlinNoise/PerlinNoise_SPU.h @@ -9,7 +9,7 @@ class PerlinNoise_SPU//: public Synth { private: - static const int MAX_NOISE_LEVELS = 16; + static const int MAX_NOISE_LEVELS = 256; ImprovedNoise_SPU noiseLevels[MAX_NOISE_LEVELS]; int levels; diff --git a/Minecraft.World/ImprovedNoise.cpp b/Minecraft.World/ImprovedNoise.cpp index 47f390858..64fe37d32 100644 --- a/Minecraft.World/ImprovedNoise.cpp +++ b/Minecraft.World/ImprovedNoise.cpp @@ -1,216 +1,219 @@ #include "stdafx.h" #include "ImprovedNoise.h" +#include "Random.h" // наш улучшенный Random +#include // для floor, если нужно, но используем static_cast ImprovedNoise::ImprovedNoise() { - Random random; - init(&random); + Random random; // использует новый высокоэнтропийный seed + init(&random); } -ImprovedNoise::ImprovedNoise(Random *random) +ImprovedNoise::ImprovedNoise(Random* random) { - init(random); + init(random); } -void ImprovedNoise::init(Random *random) +void ImprovedNoise::init(Random* random) { - p = new int[512]; + // Случайные смещения для каждой координаты (убирают симметрию) + xo = random->nextDouble() * 256.0; + yo = random->nextDouble() * 256.0; + zo = random->nextDouble() * 256.0; - xo = random->nextDouble() * 256; - yo = random->nextDouble() * 256; - zo = random->nextDouble() * 256; - for (int i = 0; i < 256; i++) - { + // Заполняем permutation table числами 0..255 в случайном порядке + for (int i = 0; i < 256; ++i) p[i] = i; - } - for (int i = 0; i < 256; i++) - { - int j = random->nextInt(256 - i) + i; + for (int i = 0; i < 256; ++i) + { + int j = random->nextInt(256 - i) + i; // случайная перестановка int tmp = p[i]; p[i] = p[j]; p[j] = tmp; - p[i + 256] = p[i]; + p[i + 256] = p[i]; // дублируем для избежания проверок границ } } -ImprovedNoise::~ImprovedNoise() +// ---------------------------------------------------------------------- +// Основной метод вычисления 3D шума Перлина +double ImprovedNoise::noise(double x, double y, double z) const { - delete [] p; -} + // Добавляем уникальное смещение для этого экземпляра + x += xo; + y += yo; + z += zo; -double ImprovedNoise::noise(double _x, double _y, double _z) -{ - double x = _x + xo; - double y = _y + yo; - double z = _z + zo; + // Целая часть координат (определяем куб, содержащий точку) + int X = static_cast(std::floor(x)) & 255; + int Y = static_cast(std::floor(y)) & 255; + int Z = static_cast(std::floor(z)) & 255; - int xf = static_cast(x); - int yf = static_cast(y); - int zf = static_cast(z); + // Относительные координаты внутри куба [0,1) + x -= std::floor(x); + y -= std::floor(y); + z -= std::floor(z); - if (x < xf) xf--; - if (y < yf) yf--; - if (z < zf) zf--; + // Плавная кривая (fade function): 6t^5 - 15t^4 + 10t^3 + double u = x * x * x * (x * (x * 6.0 - 15.0) + 10.0); + double v = y * y * y * (y * (y * 6.0 - 15.0) + 10.0); + double w = z * z * z * (z * (z * 6.0 - 15.0) + 10.0); - int X = xf & 255, // FIND UNIT CUBE THAT - Y = yf & 255, // CONTAINS POINT. - Z = zf & 255; + // Хеши индексов восьми углов куба + int A = p[X] + Y; + int AA = p[A] + Z; + int AB = p[A + 1] + Z; + int B = p[X + 1] + Y; + int BA = p[B] + Z; + int BB = p[B + 1] + Z; - x -= xf; // FIND RELATIVE X,Y,Z - y -= yf; // OF POINT IN CUBE. - z -= zf; - - double u = x * x * x * (x * (x * 6 - 15) + 10), // COMPUTE FADE CURVES - v = y * y * y * (y * (y * 6 - 15) + 10), // FOR EACH OF X,Y,Z. - w = z * z * z * (z * (z * 6 - 15) + 10); - - int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z, // HASH COORDINATES OF - B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z; // THE 8 CUBE CORNERS, - - return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z), // AND ADD - grad(p[BA], x - 1, y, z)), // BLENDED - lerp(u, grad(p[AB], x, y - 1, z), // RESULTS - grad(p[BB], x - 1, y - 1, z))),// FROM 8 - lerp(v, lerp(u, grad(p[AA + 1], x, y, z - 1), // CORNERS - grad(p[BA + 1], x - 1, y, z - 1)), // OF CUBE - lerp(u, grad(p[AB + 1], x, y - 1, z - 1), grad(p[BB + 1], x - 1, y - 1, z - 1)))); + // Интерполяция + double y1 = lerp(u, + grad(p[AA], x, y, z), + grad(p[BA], x - 1, y, z)); + double y2 = lerp(u, + grad(p[AB], x, y - 1, z), + grad(p[BB], x - 1, y - 1, z)); + double y3 = lerp(u, + grad(p[AA + 1], x, y, z - 1), + grad(p[BA + 1], x - 1, y, z - 1)); + double y4 = lerp(u, + grad(p[AB + 1], x, y - 1, z - 1), + grad(p[BB + 1], x - 1, y - 1, z - 1)); + double z1 = lerp(v, y1, y2); + double z2 = lerp(v, y3, y4); + return lerp(w, z1, z2); } double ImprovedNoise::lerp(double t, double a, double b) { - return a + t * (b - a); -} - -double ImprovedNoise::grad2(int hash, double x, double z) -{ - int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE - - double u = (1-((h&8)>>3))*x, // INTO 12 GRADIENT DIRECTIONS. - v = h < 4 ? 0 : h == 12 || h == 14 ? x : z; - - return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); + return a + t * (b - a); } +// 3D градиент (используется в noise) double ImprovedNoise::grad(int hash, double x, double y, double z) { - int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE - - double u = h < 8 ? x : y, // INTO 12 GRADIENT DIRECTIONS. - v = h < 4 ? y : h == 12 || h == 14 ? x : z; - + int h = hash & 15; // младшие 4 бита + double u = h < 8 ? x : y; + double v = h < 4 ? y : (h == 12 || h == 14 ? x : z); return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); } +// 2D градиент (используется в add() для случая ySize==1) +double ImprovedNoise::grad2(int hash, double x, double z) +{ + int h = hash & 15; + double u = (h & 8) ? x : z; // альтернативное распределение для 2D + double v = (h < 4) ? 0 : (h == 12 || h == 14 ? x : z); + return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); +} + +// Synth интерфейс double ImprovedNoise::getValue(double x, double y) { - return noise(x, y, 0); + return noise(x, y, 0.0); } double ImprovedNoise::getValue(double x, double y, double z) { - return noise(x, y, z); + return noise(x, y, z); } -void ImprovedNoise::add(doubleArray buffer, double _x, double _y, double _z, int xSize, int ySize, int zSize, double xs, double ys, double zs, double pow) +// ---------------------------------------------------------------------- +// Метод add – быстрое заполнение буфера значениями шума для прямоугольной области +// (оптимизирован, чтобы избежать повторных вычислений градиентов) +void ImprovedNoise::add(doubleArray buffer, double _x, double _y, double _z, + int xSize, int ySize, int zSize, + double xs, double ys, double zs, double pow) const { - if (ySize==1) - { - int A = 0, AA = 0, B = 0, BA = 0; - double vv0 = 0, vv2 = 0; - int pp = 0; - double scale = 1.0 / pow; - for (int xx = 0; xx < xSize; xx++) - { - double x = _x + (xx) * xs + xo; - int xf = static_cast(x); - if (x < xf) xf--; - int X = xf & 255; - x -= xf; - double u = x * x * x * (x * (x * 6 - 15) + 10); + double invPow = 1.0 / pow; + int idx = 0; // индекс в буфере - for (int zz = 0; zz < zSize; zz++) - { - double z = _z + (zz) * zs + zo; - int zf = static_cast(z); - if (z < zf) zf--; - int Z = zf & 255; - z -= zf; - double w = z * z * z * (z * (z * 6 - 15) + 10); - - A = p[X] + 0; - AA = p[A] + Z; - B = p[X + 1] + 0; - BA = p[B] + Z; - vv0 = lerp(u, grad2(p[AA], x, z), grad(p[BA], x - 1, 0, z)); - vv2 = lerp(u, grad(p[AA + 1], x, 0, z - 1), grad(p[BA + 1], x - 1, 0, z - 1)); + if (ySize == 1) + { + // Оптимизация для 2D среза (y фиксирован) + for (int xx = 0; xx < xSize; ++xx) + { + double x = _x + xx * xs + xo; + int X = static_cast(std::floor(x)) & 255; + double dx = x - std::floor(x); + double u = dx * dx * dx * (dx * (dx * 6.0 - 15.0) + 10.0); - double val = lerp(w, vv0, vv2); + for (int zz = 0; zz < zSize; ++zz) + { + double z = _z + zz * zs + zo; + int Z = static_cast(std::floor(z)) & 255; + double dz = z - std::floor(z); + double w = dz * dz * dz * (dz * (dz * 6.0 - 15.0) + 10.0); - buffer[pp++] += val * scale; + int A = p[X]; + int AA = p[A] + Z; + int B = p[X + 1]; + int BA = p[B] + Z; + + double v0 = lerp(u, + grad2(p[AA], dx, dz), + grad2(p[BA], dx - 1.0, dz)); + double v2 = lerp(u, + grad2(p[AA + 1], dx, dz - 1.0), + grad2(p[BA + 1], dx - 1.0, dz - 1.0)); + + buffer[idx++] += lerp(w, v0, v2) * invPow; } } - return; } - int pp = 0; - double scale = 1 / pow; - int yOld = -1; - int A = 0, AA = 0, AB = 0, B = 0, BA = 0, BB = 0; - double vv0 = 0, vv1 = 0, vv2 = 0, vv3 = 0; + else + { + // Полная 3D версия с кэшированием по Y + int yPrev = -1; + int A, AA, AB, B, BA, BB; + double vv0, vv1, vv2, vv3; - for (int xx = 0; xx < xSize; xx++) - { - double x = _x + (xx) * xs + xo; - int xf = static_cast(x); - if (x < xf) xf--; - int X = xf & 255; - x -= xf; - double u = x * x * x * (x * (x * 6 - 15) + 10); + for (int xx = 0; xx < xSize; ++xx) + { + double x = _x + xx * xs + xo; + int X = static_cast(std::floor(x)) & 255; + double dx = x - std::floor(x); + double u = dx * dx * dx * (dx * (dx * 6.0 - 15.0) + 10.0); + for (int zz = 0; zz < zSize; ++zz) + { + double z = _z + zz * zs + zo; + int Z = static_cast(std::floor(z)) & 255; + double dz = z - std::floor(z); + double w = dz * dz * dz * (dz * (dz * 6.0 - 15.0) + 10.0); - for (int zz = 0; zz < zSize; zz++) - { - double z = _z + (zz) * zs + zo; - int zf = static_cast(z); - if (z < zf) zf--; - int Z = zf & 255; - z -= zf; - double w = z * z * z * (z * (z * 6 - 15) + 10); + for (int yy = 0; yy < ySize; ++yy) + { + double y = _y + yy * ys + yo; + int Y = static_cast(std::floor(y)) & 255; + double dy = y - std::floor(y); + double v = dy * dy * dy * (dy * (dy * 6.0 - 15.0) + 10.0); + // Кэшируем значения, зависящие только от Y + if (yy == 0 || Y != yPrev) + { + yPrev = Y; + A = p[X] + Y; + AA = p[A] + Z; + AB = p[A + 1] + Z; + B = p[X + 1] + Y; + BA = p[B] + Z; + BB = p[B + 1] + Z; - for (int yy = 0; yy < ySize; yy++) - { - double y = _y + (yy) * ys + yo; - int yf = static_cast(y); - if (y < yf) yf--; - int Y = yf & 255; - y -= yf; - double v = y * y * y * (y * (y * 6 - 15) + 10); + vv0 = lerp(u, grad(p[AA], dx, dy, dz), grad(p[BA], dx - 1.0, dy, dz)); + vv1 = lerp(u, grad(p[AB], dx, dy - 1.0, dz), grad(p[BB], dx - 1.0, dy - 1.0, dz)); + vv2 = lerp(u, grad(p[AA + 1], dx, dy, dz - 1.0), grad(p[BA + 1], dx - 1.0, dy, dz - 1.0)); + vv3 = lerp(u, grad(p[AB + 1], dx, dy - 1.0, dz - 1.0), grad(p[BB + 1], dx - 1.0, dy - 1.0, dz - 1.0)); + } - if (yy == 0 || Y != yOld) - { - yOld = Y; - A = p[X] + Y; - AA = p[A] + Z; - AB = p[A + 1] + Z; - B = p[X + 1] + Y; - BA = p[B] + Z; - BB = p[B + 1] + Z; - vv0 = lerp(u, grad(p[AA], x, y, z), grad(p[BA], x - 1, y, z)); - vv1 = lerp(u, grad(p[AB], x, y - 1, z), grad(p[BB], x - 1, y - 1, z)); - vv2 = lerp(u, grad(p[AA + 1], x, y, z - 1), grad(p[BA + 1], x - 1, y, z - 1)); - vv3 = lerp(u, grad(p[AB + 1], x, y - 1, z - 1), grad(p[BB + 1], x - 1, y - 1, z - 1)); + double v0 = lerp(v, vv0, vv1); + double v1 = lerp(v, vv2, vv3); + buffer[idx++] += lerp(w, v0, v1) * invPow; } - - - double v0 = lerp(v, vv0, vv1); - double v1 = lerp(v, vv2, vv3); - double val = lerp(w, v0, v1); - - buffer[pp++] += val * scale; } } - } + } } diff --git a/Minecraft.World/ImprovedNoise.h b/Minecraft.World/ImprovedNoise.h index b35bb3fd6..72d0c0b56 100644 --- a/Minecraft.World/ImprovedNoise.h +++ b/Minecraft.World/ImprovedNoise.h @@ -1,29 +1,28 @@ -#pragma once +п»ї#pragma once #include "Synth.h" class ImprovedNoise : public Synth { - friend class PerlinNoise_SPU; + friend class PerlinNoise_SPU; private: - int *p; + int p[512]; // permutation table (size 512 for easy indexing) + double xo, yo, zo; // random offsets for each instance public: - double scale; - double xo, yo, zo; - ImprovedNoise(); - ImprovedNoise(Random *random); - void init(Random *random); + explicit ImprovedNoise(Random* random); + void init(Random* random); - ~ImprovedNoise(); + double noise(double x, double y, double z) const; + static double lerp(double t, double a, double b); + static double grad(int hash, double x, double y, double z); + static double grad2(int hash, double x, double z); // 2‑D gradient (used in add() for ySize==1) - double noise(double _x, double _y, double _z); + // Synth interface + double getValue(double x, double y) override; // 2D noise (z = 0) + double getValue(double x, double y, double z); // 3D noise - double lerp(double t, double a, double b); - double grad2(int hash, double x, double z); - double grad(int hash, double x, double y, double z); - virtual double getValue(double x, double y); - double getValue(double x, double y, double z); - - void add(doubleArray buffer, double _x, double _y, double _z, int xSize, int ySize, int zSize, double xs, double ys, double zs, double pow); + void add(doubleArray buffer, double _x, double _y, double _z, + int xSize, int ySize, int zSize, + double xs, double ys, double zs, double pow) const; }; diff --git a/Minecraft.World/PerlinNoise.cpp b/Minecraft.World/PerlinNoise.cpp index 21f92a6ba..27a3de562 100644 --- a/Minecraft.World/PerlinNoise.cpp +++ b/Minecraft.World/PerlinNoise.cpp @@ -1,98 +1,98 @@ #include "stdafx.h" #include "PerlinNoise.h" +#include "Random.h" #include "Mth.h" PerlinNoise::PerlinNoise(int levels) { - Random random; - init(&random, levels); + Random random; // использует новый высокоэнтропийный seed + init(&random, levels); } -PerlinNoise::PerlinNoise(Random *random, int levels) +PerlinNoise::PerlinNoise(Random* random, int levels) { - init(random, levels); + init(random, levels); } -void PerlinNoise::init(Random *random, int levels) +void PerlinNoise::init(Random* random, int levels) { - MemSect(2); + MemSect(2); this->levels = levels; - noiseLevels = new ImprovedNoise *[levels]; - for (int i = 0; i < levels; i++) - { + noiseLevels = new ImprovedNoise * [levels]; + for (int i = 0; i < levels; ++i) + { noiseLevels[i] = new ImprovedNoise(random); } - MemSect(0); + MemSect(0); } PerlinNoise::~PerlinNoise() { - for( int i = 0; i < levels; i++ ) - { - delete noiseLevels[i]; - } - delete [] noiseLevels; + for (int i = 0; i < levels; ++i) + delete noiseLevels[i]; + delete[] noiseLevels; } double PerlinNoise::getValue(double x, double y) { - double value = 0; - double pow = 1; - - for (int i = 0; i < levels; i++) - { - value += noiseLevels[i]->getValue(x * pow, y * pow) / pow; - pow /= 2; + double value = 0.0; + double amp = 1.0; // амплитуда (здесь 1/pow в оригинале) + for (int i = 0; i < levels; ++i) + { + value += noiseLevels[i]->getValue(x * amp, y * amp) / amp; + amp *= 0.5; // уменьшаем частоту (pow /= 2) } - return value; } double PerlinNoise::getValue(double x, double y, double z) { - double value = 0; - double pow = 1; - - for (int i = 0; i < levels; i++) - { - value += noiseLevels[i]->getValue(x * pow, y * pow, z * pow) / pow; - pow /= 2; + double value = 0.0; + double amp = 1.0; + for (int i = 0; i < levels; ++i) + { + value += noiseLevels[i]->getValue(x * amp, y * amp, z * amp) / amp; + amp *= 0.5; } - return value; } -doubleArray PerlinNoise::getRegion(doubleArray buffer, int x, int y, int z, int xSize, int ySize, int zSize, double xScale, double yScale, double zScale) +doubleArray PerlinNoise::getRegion(doubleArray buffer, + int x, int y, int z, + int xSize, int ySize, int zSize, + double xScale, double yScale, double zScale) { - if (buffer.data == nullptr) buffer = doubleArray(xSize * ySize * zSize); - else for (unsigned int i = 0; i < buffer.length; i++) - buffer[i] = 0; + // Инициализация буфера нулями, если необходимо + if (buffer.data == nullptr) + buffer = doubleArray(xSize * ySize * zSize); + else + for (unsigned int i = 0; i < buffer.length; ++i) + buffer[i] = 0.0; - - double pow = 1; - - for (int i = 0; i < levels; i++) - { - // value += noiseLevels[i].getValue(x * pow, y * pow, z * pow) / pow; - double xx = x * pow * xScale; - double yy = y * pow * yScale; - double zz = z * pow * zScale; - int64_t xb = Mth::lfloor(xx); - int64_t zb = Mth::lfloor(zz); - xx -= xb; - zz -= zb; - xb %= 16777216; - zb %= 16777216; - xx += xb; - zz += zb; - noiseLevels[i]->add(buffer, xx, yy, zz, xSize, ySize, zSize, xScale * pow, yScale * pow, zScale * pow, pow); - pow /= 2; + double freq = 1.0; // частота (pow) + for (int i = 0; i < levels; ++i) + { + // Вызов add для текущего уровня с правильным масштабированием + noiseLevels[i]->add(buffer, + x * freq * xScale, + y * freq * yScale, + z * freq * zScale, + xSize, ySize, zSize, + xScale * freq, + yScale * freq, + zScale * freq, + freq); + freq *= 0.5; } return buffer; } -doubleArray PerlinNoise::getRegion(doubleArray sr, int x, int z, int xSize, int zSize, double xScale, double zScale, double pow) +// Перегрузка для 2D региона (y фиксирован) +doubleArray PerlinNoise::getRegion(doubleArray sr, int x, int z, + int xSize, int zSize, + double xScale, double zScale, double pow) { - return getRegion(sr, x, 10, z, xSize, 1, zSize, xScale, 1, zScale); -} \ No newline at end of file + // Используем y = 10 (как в оригинале), ySize = 1 + return getRegion(sr, x, 10, z, xSize, 1, zSize, xScale, 1.0, zScale); +} diff --git a/Minecraft.World/Random.cpp b/Minecraft.World/Random.cpp index 89903da8c..3f66894d5 100644 --- a/Minecraft.World/Random.cpp +++ b/Minecraft.World/Random.cpp @@ -1,7 +1,193 @@ -#include "stdafx.h" +п»ї#include "stdafx.h" #include "Random.h" #include "System.h" +#ifndef USE_LEGACY_RANDOM +// Windows API for high-resolution counters and system info +#include + +// C++11 random library and chrono for high-quality seeding +#include +#include +#include + +// Use a 64-bit Mersenne Twister engine +static std::mt19937_64& getEngine() { + // Make the engine thread-local to avoid data races in multi-threaded code + thread_local std::mt19937_64 engine(std::random_device{}()); + return engine; +} + +// ---------------------------------------------------------------------- +// Seed generation: combine multiple entropy sources +static int64_t generateEntropySeed() +{ + uint64_t entropy = 0; + + // 1. High-resolution performance counter (similar to original) + LARGE_INTEGER perfCount; + QueryPerformanceCounter(&perfCount); + entropy ^= perfCount.QuadPart; + + // 2. System tick count (milliseconds since boot) + entropy ^= GetTickCount64(); + + // 3. Process and thread IDs + entropy ^= static_cast(GetCurrentProcessId()) << 32; + entropy ^= static_cast(GetCurrentThreadId()) << 16; + + // 4. High-resolution clock with nanoseconds (C++11) + auto now = std::chrono::high_resolution_clock::now(); + auto ns = std::chrono::time_point_cast(now) + .time_since_epoch().count(); + entropy ^= static_cast(ns); + + // 5. Hardware randomness if available (via random_device) + // Note: on some compilers random_device may be deterministic, + // but it's a good additional source. + try { + std::random_device rd; + entropy ^= static_cast(rd()) | + (static_cast(rd()) << 32); + } + catch (...) { + // random_device not available – just ignore + } + + // 6. Address of a stack variable (ASLR provides some randomness) + volatile void* stackAddr = &entropy; + entropy ^= reinterpret_cast(stackAddr); + + // Mix the bits well to avoid correlation + // (using a simple but effective hash) + entropy ^= entropy >> 33; + entropy *= 0xff51afd7ed558ccdULL; + entropy ^= entropy >> 33; + entropy *= 0xc4ceb9fe1a85ec53ULL; + entropy ^= entropy >> 33; + + return static_cast(entropy); +} + +// ---------------------------------------------------------------------- +// Random class implementation + +Random::Random() +{ + // Use the high‑entropy seed generator + setSeed(generateEntropySeed()); +} + +Random::Random(int64_t seed) +{ + setSeed(seed); +} + +void Random::setSeed(int64_t s) +{ + // Store seed for possible inspection + seed = s; + + // Seed the Mersenne Twister engine + getEngine().seed(static_cast(s)); + + // Reset Gaussian cache + haveNextNextGaussian = false; + nextNextGaussian = 0.0; +} + +// ---------------------------------------------------------------------- +// Core bit generator – replaces the old LCG +int Random::next(int bits) +{ + // mt19937_64 produces 64 random bits each call + uint64_t raw = getEngine()(); + + // Return the required number of most significant bits + // (shifting right keeps the higher bits which are usually "more random") + return static_cast(raw >> (64 - bits)); +} + +// ---------------------------------------------------------------------- +// All other methods stay exactly as in the original code, +// because they all rely on next(bits). + +void Random::nextBytes(byte* bytes, unsigned int count) +{ + for (unsigned int i = 0; i < count; ++i) + { + bytes[i] = static_cast(next(8)); + } +} + +double Random::nextDouble() +{ + return ((static_cast(next(26)) << 27) + next(27)) + / static_cast(1LL << 53); +} + +double Random::nextGaussian() +{ + if (haveNextNextGaussian) + { + haveNextNextGaussian = false; + return nextNextGaussian; + } + else + { + double v1, v2, s; + do + { + v1 = 2 * nextDouble() - 1; // between -1.0 and 1.0 + v2 = 2 * nextDouble() - 1; // between -1.0 and 1.0 + s = v1 * v1 + v2 * v2; + } while (s >= 1 || s == 0); + double multiplier = sqrt(-2 * log(s) / s); + nextNextGaussian = v2 * multiplier; + haveNextNextGaussian = true; + return v1 * multiplier; + } +} + +int Random::nextInt() +{ + return next(32); +} + +int Random::nextInt(int n) +{ + // Parameter check (you may replace assert with a thrown exception) + // assert(n > 0); + if (n <= 0) return 0; // or throw std::invalid_argument("n must be positive"); + + // Special case for powers of two (fast path) + if ((n & -n) == n) + return static_cast((static_cast(next(31)) * n) >> 31); + + int bits, val; + do + { + bits = next(31); + val = bits % n; + } while (bits - val + (n - 1) < 0); + return val; +} + +float Random::nextFloat() +{ + return next(24) / static_cast(1 << 24); +} + +int64_t Random::nextLong() +{ + return (static_cast(next(32)) << 32) + next(32); +} + +bool Random::nextBoolean() +{ + return next(1) != 0; +} +#else Random::Random() { // 4J - jave now uses the system nanosecond counter added to a "seedUniquifier" to get an initial seed. Our nanosecond timer is actually only millisecond accuate, so @@ -103,4 +289,5 @@ int64_t Random::nextLong() bool Random::nextBoolean() { return next(1) != 0; -} \ No newline at end of file +} +#endif diff --git a/Minecraft.World/Random.h b/Minecraft.World/Random.h index cfb6af26f..e0c076f85 100644 --- a/Minecraft.World/Random.h +++ b/Minecraft.World/Random.h @@ -1,5 +1,31 @@ #pragma once +#ifndef USE_LEGACY_RANDOM +class Random +{ +private: + int64_t seed; // last seed used (for reference) + bool haveNextNextGaussian; // state for nextGaussian() + double nextNextGaussian; // cached Gaussian value + +protected: + int next(int bits); // generates up to 32 random bits + +public: + Random(); // uses a high-entropy seed + explicit Random(int64_t seed); // seed with a specific value + void setSeed(int64_t s); // re-seed the generator + + void nextBytes(byte* bytes, unsigned int count); + double nextDouble(); + double nextGaussian(); + int nextInt(); + int nextInt(int n); // in [0, n) + float nextFloat(); + int64_t nextLong(); + bool nextBoolean(); +}; +#else class Random { private: @@ -20,4 +46,5 @@ public: float nextFloat(); int64_t nextLong(); bool nextBoolean(); -}; \ No newline at end of file +}; +#endif