forked from Mirrorlandia_minetest/minetest
Add support for the PCG32 PRNG algo (and associated script APIs)
This commit is contained in:
parent
7679396ebb
commit
3993093f51
@ -2515,7 +2515,8 @@ an itemstring, a table or `nil`.
|
|||||||
Returns taken `ItemStack`.
|
Returns taken `ItemStack`.
|
||||||
|
|
||||||
### `PseudoRandom`
|
### `PseudoRandom`
|
||||||
A pseudorandom number generator.
|
A 16-bit pseudorandom number generator.
|
||||||
|
Uses a well-known LCG algorithm introduced by K&R.
|
||||||
|
|
||||||
It can be created via `PseudoRandom(seed)`.
|
It can be created via `PseudoRandom(seed)`.
|
||||||
|
|
||||||
@ -2525,6 +2526,19 @@ It can be created via `PseudoRandom(seed)`.
|
|||||||
* `((max - min) == 32767) or ((max-min) <= 6553))` must be true
|
* `((max - min) == 32767) or ((max-min) <= 6553))` must be true
|
||||||
due to the simple implementation making bad distribution otherwise.
|
due to the simple implementation making bad distribution otherwise.
|
||||||
|
|
||||||
|
### `PcgRandom`
|
||||||
|
A 32-bit pseudorandom number generator.
|
||||||
|
Uses PCG32, an algorithm of the permuted congruential generator family, offering very strong randomness.
|
||||||
|
|
||||||
|
It can be created via `PcgRandom(seed)` or `PcgRandom(seed, sequence)`.
|
||||||
|
|
||||||
|
#### Methods
|
||||||
|
* `next()`: return next integer random number [`-2147483648`...`2147483647`]
|
||||||
|
* `next(min, max)`: return next integer random number [`min`...`max`]
|
||||||
|
* `rand_normal_dist(min, max, num_trials=6)`: return normally distributed random number [`min`...`max`]
|
||||||
|
* This is only a rough approximation of a normal distribution with mean=(max-min)/2 and variance=1
|
||||||
|
* Increasing num_trials improves accuracy of the approximation
|
||||||
|
|
||||||
### `PerlinNoise`
|
### `PerlinNoise`
|
||||||
A perlin noise generator.
|
A perlin noise generator.
|
||||||
It can be created via `PerlinNoise(seed, octaves, persistence, scale)`
|
It can be created via `PerlinNoise(seed, octaves, persistence, scale)`
|
||||||
|
@ -515,14 +515,10 @@ void MapgenParams::load(const Settings &settings)
|
|||||||
std::string seed_str;
|
std::string seed_str;
|
||||||
const char *seed_name = (&settings == g_settings) ? "fixed_map_seed" : "seed";
|
const char *seed_name = (&settings == g_settings) ? "fixed_map_seed" : "seed";
|
||||||
|
|
||||||
if (settings.getNoEx(seed_name, seed_str) && !seed_str.empty()) {
|
if (settings.getNoEx(seed_name, seed_str) && !seed_str.empty())
|
||||||
seed = read_seed(seed_str.c_str());
|
seed = read_seed(seed_str.c_str());
|
||||||
} else {
|
else
|
||||||
seed = ((u64)(myrand() & 0xFFFF) << 0) |
|
myrand_bytes(&seed, sizeof(seed));
|
||||||
((u64)(myrand() & 0xFFFF) << 16) |
|
|
||||||
((u64)(myrand() & 0xFFFF) << 32) |
|
|
||||||
((u64)(myrand() & 0xFFFF) << 48);
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.getNoEx("mg_name", mg_name);
|
settings.getNoEx("mg_name", mg_name);
|
||||||
settings.getS16NoEx("water_level", water_level);
|
settings.getS16NoEx("water_level", water_level);
|
||||||
|
@ -308,7 +308,7 @@ void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// randval ranges from -1..1
|
// randval ranges from -1..1
|
||||||
float randval = (float)pr.next() / (PSEUDORANDOM_MAX / 2) - 1.f;
|
float randval = (float)pr.next() / (pr.RANDOM_RANGE / 2) - 1.f;
|
||||||
float noiseval = contour(noise->result[index]);
|
float noiseval = contour(noise->result[index]);
|
||||||
float noiseval2 = contour(noise2->result[index]);
|
float noiseval2 = contour(noise2->result[index]);
|
||||||
if (noiseval * noiseval2 + randval * random_factor < nthresh)
|
if (noiseval * noiseval2 + randval * random_factor < nthresh)
|
||||||
|
101
src/noise.cpp
101
src/noise.cpp
@ -62,6 +62,107 @@ FlagDesc flagdesc_noiseparams[] = {
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
PcgRandom::PcgRandom(u64 state, u64 seq)
|
||||||
|
{
|
||||||
|
seed(state, seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PcgRandom::seed(u64 state, u64 seq)
|
||||||
|
{
|
||||||
|
m_state = 0U;
|
||||||
|
m_inc = (seq << 1u) | 1u;
|
||||||
|
next();
|
||||||
|
m_state += state;
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u32 PcgRandom::next()
|
||||||
|
{
|
||||||
|
u64 oldstate = m_state;
|
||||||
|
m_state = oldstate * 6364136223846793005ULL + m_inc;
|
||||||
|
|
||||||
|
u32 xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
|
||||||
|
u32 rot = oldstate >> 59u;
|
||||||
|
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u32 PcgRandom::range(u32 bound)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
If the bound is not a multiple of the RNG's range, it may cause bias,
|
||||||
|
e.g. a RNG has a range from 0 to 3 and we take want a number 0 to 2.
|
||||||
|
Using rand() % 3, the number 0 would be twice as likely to appear.
|
||||||
|
With a very large RNG range, the effect becomes less prevalent but
|
||||||
|
still present. This can be solved by modifying the range of the RNG
|
||||||
|
to become a multiple of bound by dropping values above the a threshhold.
|
||||||
|
In our example, threshhold == 4 - 3 = 1 % 3 == 1, so reject 0, thus
|
||||||
|
making the range 3 with no bias.
|
||||||
|
|
||||||
|
This loop looks dangerous, but will always terminate due to the
|
||||||
|
RNG's property of uniformity.
|
||||||
|
*/
|
||||||
|
u32 threshhold = -bound % bound;
|
||||||
|
u32 r;
|
||||||
|
|
||||||
|
while ((r = next()) < threshhold);
|
||||||
|
|
||||||
|
return r % bound;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
s32 PcgRandom::range(s32 min, s32 max)
|
||||||
|
{
|
||||||
|
assert(max >= min);
|
||||||
|
u32 bound = max - min + 1;
|
||||||
|
return range(bound) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PcgRandom::bytes(void *out, size_t len)
|
||||||
|
{
|
||||||
|
u32 r;
|
||||||
|
u8 *outb = (u8 *)out;
|
||||||
|
|
||||||
|
size_t len_alignment = (uintptr_t)out % sizeof(u32);
|
||||||
|
if (len_alignment) {
|
||||||
|
r = next();
|
||||||
|
while (len_alignment--) {
|
||||||
|
*outb = r & 0xFF;
|
||||||
|
outb++;
|
||||||
|
r >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len_dwords = len / sizeof(u32);
|
||||||
|
while (len_dwords--) {
|
||||||
|
r = next();
|
||||||
|
*(u32 *)outb = next();
|
||||||
|
outb += sizeof(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len_remaining = len % sizeof(u32);
|
||||||
|
if (len_remaining) {
|
||||||
|
r = next();
|
||||||
|
while (len_remaining--) {
|
||||||
|
*outb = r & 0xFF;
|
||||||
|
outb++;
|
||||||
|
r >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
s32 PcgRandom::randNormalDist(s32 min, s32 max, int num_trials)
|
||||||
|
{
|
||||||
|
u32 accum = 0;
|
||||||
|
for (int i = 0; i != num_trials; i++)
|
||||||
|
accum += range(min, max);
|
||||||
|
return ((float)accum / num_trials) + 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
float noise2d(int x, int y, int seed)
|
float noise2d(int x, int y, int seed)
|
||||||
{
|
{
|
||||||
|
71
src/noise.h
71
src/noise.h
@ -30,47 +30,67 @@
|
|||||||
#include "irr_v3d.h"
|
#include "irr_v3d.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
|
|
||||||
#define PSEUDORANDOM_MAX 32767
|
|
||||||
|
|
||||||
extern FlagDesc flagdesc_noiseparams[];
|
extern FlagDesc flagdesc_noiseparams[];
|
||||||
|
|
||||||
class PseudoRandom
|
// Note: this class is not polymorphic so that its high level of
|
||||||
{
|
// optimizability may be preserved in the common use case
|
||||||
|
class PseudoRandom {
|
||||||
public:
|
public:
|
||||||
PseudoRandom(): m_next(0)
|
const static u32 RANDOM_RANGE = 32767;
|
||||||
|
|
||||||
|
inline PseudoRandom(int seed=0):
|
||||||
|
m_next(seed)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
PseudoRandom(int seed): m_next(seed)
|
|
||||||
{
|
inline void seed(int seed)
|
||||||
}
|
|
||||||
void seed(int seed)
|
|
||||||
{
|
{
|
||||||
m_next = seed;
|
m_next = seed;
|
||||||
}
|
}
|
||||||
// Returns 0...PSEUDORANDOM_MAX
|
|
||||||
int next()
|
inline int next()
|
||||||
{
|
{
|
||||||
m_next = m_next * 1103515245 + 12345;
|
m_next = m_next * 1103515245 + 12345;
|
||||||
return((unsigned)(m_next/65536) % (PSEUDORANDOM_MAX + 1));
|
return (unsigned)(m_next / 65536) % (RANDOM_RANGE + 1);
|
||||||
}
|
}
|
||||||
int range(int min, int max)
|
|
||||||
|
inline int range(int min, int max)
|
||||||
{
|
{
|
||||||
if (max-min > (PSEUDORANDOM_MAX + 1) / 10)
|
assert(max >= min);
|
||||||
{
|
/*
|
||||||
//dstream<<"WARNING: PseudoRandom::range: max > 32767"<<std::endl;
|
Here, we ensure the range is not too large relative to RANDOM_MAX,
|
||||||
assert("Something wrong with random number" == NULL);
|
as otherwise the effects of bias would become noticable. Unlike
|
||||||
}
|
PcgRandom, we cannot modify this RNG's range as it would change the
|
||||||
if(min > max)
|
output of this RNG for reverse compatibility.
|
||||||
{
|
*/
|
||||||
assert("Something wrong with random number" == NULL);
|
assert((u32)(max - min) <= (RANDOM_RANGE + 1) / 10);
|
||||||
//return max;
|
|
||||||
}
|
return (next() % (max - min + 1)) + min;
|
||||||
return (next()%(max-min+1))+min;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_next;
|
int m_next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PcgRandom {
|
||||||
|
public:
|
||||||
|
const static s32 RANDOM_MIN = -0x7fffffff - 1;
|
||||||
|
const static s32 RANDOM_MAX = 0x7fffffff;
|
||||||
|
const static u32 RANDOM_RANGE = 0xffffffff;
|
||||||
|
|
||||||
|
PcgRandom(u64 state=0x853c49e6748fea9bULL, u64 seq=0xda3e39cb94b95bdbULL);
|
||||||
|
void seed(u64 state, u64 seq=0xda3e39cb94b95bdbULL);
|
||||||
|
u32 next();
|
||||||
|
u32 range(u32 bound);
|
||||||
|
s32 range(s32 min, s32 max);
|
||||||
|
void bytes(void *out, size_t len);
|
||||||
|
s32 randNormalDist(s32 min, s32 max, int num_trials=6);
|
||||||
|
|
||||||
|
private:
|
||||||
|
u64 m_state;
|
||||||
|
u64 m_inc;
|
||||||
|
};
|
||||||
|
|
||||||
#define NOISE_FLAG_DEFAULTS 0x01
|
#define NOISE_FLAG_DEFAULTS 0x01
|
||||||
#define NOISE_FLAG_EASED 0x02
|
#define NOISE_FLAG_EASED 0x02
|
||||||
#define NOISE_FLAG_ABSVALUE 0x04
|
#define NOISE_FLAG_ABSVALUE 0x04
|
||||||
@ -89,7 +109,8 @@ struct NoiseParams {
|
|||||||
float lacunarity;
|
float lacunarity;
|
||||||
u32 flags;
|
u32 flags;
|
||||||
|
|
||||||
NoiseParams() {
|
NoiseParams()
|
||||||
|
{
|
||||||
offset = 0.0f;
|
offset = 0.0f;
|
||||||
scale = 1.0f;
|
scale = 1.0f;
|
||||||
spread = v3f(250, 250, 250);
|
spread = v3f(250, 250, 250);
|
||||||
|
@ -23,12 +23,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "common/c_content.h"
|
#include "common/c_content.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
// garbage collector
|
///////////////////////////////////////
|
||||||
int LuaPerlinNoise::gc_object(lua_State *L)
|
/*
|
||||||
|
LuaPerlinNoise
|
||||||
|
*/
|
||||||
|
|
||||||
|
LuaPerlinNoise::LuaPerlinNoise(NoiseParams *params) :
|
||||||
|
np(*params)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LuaPerlinNoise::~LuaPerlinNoise()
|
||||||
{
|
{
|
||||||
LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1));
|
|
||||||
delete o;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -54,19 +61,6 @@ int LuaPerlinNoise::l_get3d(lua_State *L)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LuaPerlinNoise::LuaPerlinNoise(NoiseParams *params) :
|
|
||||||
np(*params)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LuaPerlinNoise::~LuaPerlinNoise()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// LuaPerlinNoise(seed, octaves, persistence, scale)
|
|
||||||
// Creates an LuaPerlinNoise and leaves it on top of stack
|
|
||||||
int LuaPerlinNoise::create_object(lua_State *L)
|
int LuaPerlinNoise::create_object(lua_State *L)
|
||||||
{
|
{
|
||||||
NO_MAP_LOCK_REQUIRED;
|
NO_MAP_LOCK_REQUIRED;
|
||||||
@ -91,14 +85,22 @@ int LuaPerlinNoise::create_object(lua_State *L)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LuaPerlinNoise* LuaPerlinNoise::checkobject(lua_State *L, int narg)
|
int LuaPerlinNoise::gc_object(lua_State *L)
|
||||||
|
{
|
||||||
|
LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1));
|
||||||
|
delete o;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LuaPerlinNoise *LuaPerlinNoise::checkobject(lua_State *L, int narg)
|
||||||
{
|
{
|
||||||
NO_MAP_LOCK_REQUIRED;
|
NO_MAP_LOCK_REQUIRED;
|
||||||
luaL_checktype(L, narg, LUA_TUSERDATA);
|
luaL_checktype(L, narg, LUA_TUSERDATA);
|
||||||
void *ud = luaL_checkudata(L, narg, className);
|
void *ud = luaL_checkudata(L, narg, className);
|
||||||
if (!ud)
|
if (!ud)
|
||||||
luaL_typerror(L, narg, className);
|
luaL_typerror(L, narg, className);
|
||||||
return *(LuaPerlinNoise**)ud; // unbox pointer
|
return *(LuaPerlinNoise **)ud;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -111,7 +113,7 @@ void LuaPerlinNoise::Register(lua_State *L)
|
|||||||
|
|
||||||
lua_pushliteral(L, "__metatable");
|
lua_pushliteral(L, "__metatable");
|
||||||
lua_pushvalue(L, methodtable);
|
lua_pushvalue(L, methodtable);
|
||||||
lua_settable(L, metatable); // hide metatable from Lua getmetatable()
|
lua_settable(L, metatable);
|
||||||
|
|
||||||
lua_pushliteral(L, "__index");
|
lua_pushliteral(L, "__index");
|
||||||
lua_pushvalue(L, methodtable);
|
lua_pushvalue(L, methodtable);
|
||||||
@ -121,12 +123,11 @@ void LuaPerlinNoise::Register(lua_State *L)
|
|||||||
lua_pushcfunction(L, gc_object);
|
lua_pushcfunction(L, gc_object);
|
||||||
lua_settable(L, metatable);
|
lua_settable(L, metatable);
|
||||||
|
|
||||||
lua_pop(L, 1); // drop metatable
|
lua_pop(L, 1);
|
||||||
|
|
||||||
luaL_openlib(L, 0, methods, 0); // fill methodtable
|
luaL_openlib(L, 0, methods, 0);
|
||||||
lua_pop(L, 1); // drop methodtable
|
lua_pop(L, 1);
|
||||||
|
|
||||||
// Can be created from Lua (PerlinNoise(seed, octaves, persistence)
|
|
||||||
lua_register(L, className, create_object);
|
lua_register(L, className, create_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,16 +139,26 @@ const luaL_reg LuaPerlinNoise::methods[] = {
|
|||||||
{0,0}
|
{0,0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////
|
||||||
/*
|
/*
|
||||||
PerlinNoiseMap
|
LuaPerlinNoiseMap
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int LuaPerlinNoiseMap::gc_object(lua_State *L)
|
LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *params, int seed, v3s16 size)
|
||||||
{
|
{
|
||||||
LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1));
|
m_is3d = size.Z > 1;
|
||||||
delete o;
|
np = *params;
|
||||||
return 0;
|
try {
|
||||||
|
noise = new Noise(&np, seed, size.X, size.Y, size.Z);
|
||||||
|
} catch (InvalidNoiseParamsException &e) {
|
||||||
|
throw LuaError(e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LuaPerlinNoiseMap::~LuaPerlinNoiseMap()
|
||||||
|
{
|
||||||
|
delete noise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -251,26 +262,6 @@ int LuaPerlinNoiseMap::l_get3dMap_flat(lua_State *L)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *params, int seed, v3s16 size)
|
|
||||||
{
|
|
||||||
m_is3d = size.Z > 1;
|
|
||||||
np = *params;
|
|
||||||
try {
|
|
||||||
noise = new Noise(&np, seed, size.X, size.Y, size.Z);
|
|
||||||
} catch (InvalidNoiseParamsException &e) {
|
|
||||||
throw LuaError(e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LuaPerlinNoiseMap::~LuaPerlinNoiseMap()
|
|
||||||
{
|
|
||||||
delete noise;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// LuaPerlinNoiseMap(np, size)
|
|
||||||
// Creates an LuaPerlinNoiseMap and leaves it on top of stack
|
|
||||||
int LuaPerlinNoiseMap::create_object(lua_State *L)
|
int LuaPerlinNoiseMap::create_object(lua_State *L)
|
||||||
{
|
{
|
||||||
NoiseParams np;
|
NoiseParams np;
|
||||||
@ -286,6 +277,14 @@ int LuaPerlinNoiseMap::create_object(lua_State *L)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int LuaPerlinNoiseMap::gc_object(lua_State *L)
|
||||||
|
{
|
||||||
|
LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1));
|
||||||
|
delete o;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
LuaPerlinNoiseMap *LuaPerlinNoiseMap::checkobject(lua_State *L, int narg)
|
LuaPerlinNoiseMap *LuaPerlinNoiseMap::checkobject(lua_State *L, int narg)
|
||||||
{
|
{
|
||||||
luaL_checktype(L, narg, LUA_TUSERDATA);
|
luaL_checktype(L, narg, LUA_TUSERDATA);
|
||||||
@ -294,7 +293,7 @@ LuaPerlinNoiseMap *LuaPerlinNoiseMap::checkobject(lua_State *L, int narg)
|
|||||||
if (!ud)
|
if (!ud)
|
||||||
luaL_typerror(L, narg, className);
|
luaL_typerror(L, narg, className);
|
||||||
|
|
||||||
return *(LuaPerlinNoiseMap **)ud; // unbox pointer
|
return *(LuaPerlinNoiseMap **)ud;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -307,7 +306,7 @@ void LuaPerlinNoiseMap::Register(lua_State *L)
|
|||||||
|
|
||||||
lua_pushliteral(L, "__metatable");
|
lua_pushliteral(L, "__metatable");
|
||||||
lua_pushvalue(L, methodtable);
|
lua_pushvalue(L, methodtable);
|
||||||
lua_settable(L, metatable); // hide metatable from Lua getmetatable()
|
lua_settable(L, metatable);
|
||||||
|
|
||||||
lua_pushliteral(L, "__index");
|
lua_pushliteral(L, "__index");
|
||||||
lua_pushvalue(L, methodtable);
|
lua_pushvalue(L, methodtable);
|
||||||
@ -317,12 +316,11 @@ void LuaPerlinNoiseMap::Register(lua_State *L)
|
|||||||
lua_pushcfunction(L, gc_object);
|
lua_pushcfunction(L, gc_object);
|
||||||
lua_settable(L, metatable);
|
lua_settable(L, metatable);
|
||||||
|
|
||||||
lua_pop(L, 1); // drop metatable
|
lua_pop(L, 1);
|
||||||
|
|
||||||
luaL_openlib(L, 0, methods, 0); // fill methodtable
|
luaL_openlib(L, 0, methods, 0);
|
||||||
lua_pop(L, 1); // drop methodtable
|
lua_pop(L, 1);
|
||||||
|
|
||||||
// Can be created from Lua (PerlinNoiseMap(np, size)
|
|
||||||
lua_register(L, className, create_object);
|
lua_register(L, className, create_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,32 +334,23 @@ const luaL_reg LuaPerlinNoiseMap::methods[] = {
|
|||||||
{0,0}
|
{0,0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////
|
||||||
/*
|
/*
|
||||||
LuaPseudoRandom
|
LuaPseudoRandom
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// garbage collector
|
|
||||||
int LuaPseudoRandom::gc_object(lua_State *L)
|
|
||||||
{
|
|
||||||
LuaPseudoRandom *o = *(LuaPseudoRandom **)(lua_touserdata(L, 1));
|
|
||||||
delete o;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// next(self, min=0, max=32767) -> get next value
|
|
||||||
int LuaPseudoRandom::l_next(lua_State *L)
|
int LuaPseudoRandom::l_next(lua_State *L)
|
||||||
{
|
{
|
||||||
NO_MAP_LOCK_REQUIRED;
|
NO_MAP_LOCK_REQUIRED;
|
||||||
LuaPseudoRandom *o = checkobject(L, 1);
|
LuaPseudoRandom *o = checkobject(L, 1);
|
||||||
int min = 0;
|
int min = 0;
|
||||||
int max = 32767;
|
int max = 32767;
|
||||||
lua_settop(L, 3); // Fill 2 and 3 with nil if they don't exist
|
lua_settop(L, 3);
|
||||||
if(!lua_isnil(L, 2))
|
if (lua_isnumber(L, 2))
|
||||||
min = luaL_checkinteger(L, 2);
|
min = luaL_checkinteger(L, 2);
|
||||||
if(!lua_isnil(L, 3))
|
if (lua_isnumber(L, 3))
|
||||||
max = luaL_checkinteger(L, 3);
|
max = luaL_checkinteger(L, 3);
|
||||||
if(max < min){
|
if (max < min) {
|
||||||
errorstream<<"PseudoRandom.next(): max="<<max<<" min="<<min<<std::endl;
|
errorstream<<"PseudoRandom.next(): max="<<max<<" min="<<min<<std::endl;
|
||||||
throw LuaError("PseudoRandom.next(): max < min");
|
throw LuaError("PseudoRandom.next(): max < min");
|
||||||
}
|
}
|
||||||
@ -378,30 +367,6 @@ int LuaPseudoRandom::l_next(lua_State *L)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LuaPseudoRandom::LuaPseudoRandom(int seed):
|
|
||||||
m_pseudo(seed)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LuaPseudoRandom::~LuaPseudoRandom()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const PseudoRandom& LuaPseudoRandom::getItem() const
|
|
||||||
{
|
|
||||||
return m_pseudo;
|
|
||||||
}
|
|
||||||
|
|
||||||
PseudoRandom& LuaPseudoRandom::getItem()
|
|
||||||
{
|
|
||||||
return m_pseudo;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// LuaPseudoRandom(seed)
|
|
||||||
// Creates an LuaPseudoRandom and leaves it on top of stack
|
|
||||||
int LuaPseudoRandom::create_object(lua_State *L)
|
int LuaPseudoRandom::create_object(lua_State *L)
|
||||||
{
|
{
|
||||||
int seed = luaL_checknumber(L, 1);
|
int seed = luaL_checknumber(L, 1);
|
||||||
@ -413,13 +378,21 @@ int LuaPseudoRandom::create_object(lua_State *L)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LuaPseudoRandom* LuaPseudoRandom::checkobject(lua_State *L, int narg)
|
int LuaPseudoRandom::gc_object(lua_State *L)
|
||||||
|
{
|
||||||
|
LuaPseudoRandom *o = *(LuaPseudoRandom **)(lua_touserdata(L, 1));
|
||||||
|
delete o;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LuaPseudoRandom *LuaPseudoRandom::checkobject(lua_State *L, int narg)
|
||||||
{
|
{
|
||||||
luaL_checktype(L, narg, LUA_TUSERDATA);
|
luaL_checktype(L, narg, LUA_TUSERDATA);
|
||||||
void *ud = luaL_checkudata(L, narg, className);
|
void *ud = luaL_checkudata(L, narg, className);
|
||||||
if (!ud)
|
if (!ud)
|
||||||
luaL_typerror(L, narg, className);
|
luaL_typerror(L, narg, className);
|
||||||
return *(LuaPseudoRandom**)ud; // unbox pointer
|
return *(LuaPseudoRandom **)ud;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -432,7 +405,7 @@ void LuaPseudoRandom::Register(lua_State *L)
|
|||||||
|
|
||||||
lua_pushliteral(L, "__metatable");
|
lua_pushliteral(L, "__metatable");
|
||||||
lua_pushvalue(L, methodtable);
|
lua_pushvalue(L, methodtable);
|
||||||
lua_settable(L, metatable); // hide metatable from Lua getmetatable()
|
lua_settable(L, metatable);
|
||||||
|
|
||||||
lua_pushliteral(L, "__index");
|
lua_pushliteral(L, "__index");
|
||||||
lua_pushvalue(L, methodtable);
|
lua_pushvalue(L, methodtable);
|
||||||
@ -442,12 +415,11 @@ void LuaPseudoRandom::Register(lua_State *L)
|
|||||||
lua_pushcfunction(L, gc_object);
|
lua_pushcfunction(L, gc_object);
|
||||||
lua_settable(L, metatable);
|
lua_settable(L, metatable);
|
||||||
|
|
||||||
lua_pop(L, 1); // drop metatable
|
lua_pop(L, 1);
|
||||||
|
|
||||||
luaL_openlib(L, 0, methods, 0); // fill methodtable
|
luaL_openlib(L, 0, methods, 0);
|
||||||
lua_pop(L, 1); // drop methodtable
|
lua_pop(L, 1);
|
||||||
|
|
||||||
// Can be created from Lua (LuaPseudoRandom(seed))
|
|
||||||
lua_register(L, className, create_object);
|
lua_register(L, className, create_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,3 +429,101 @@ const luaL_reg LuaPseudoRandom::methods[] = {
|
|||||||
luamethod(LuaPseudoRandom, next),
|
luamethod(LuaPseudoRandom, next),
|
||||||
{0,0}
|
{0,0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////
|
||||||
|
/*
|
||||||
|
LuaPcgRandom
|
||||||
|
*/
|
||||||
|
|
||||||
|
int LuaPcgRandom::l_next(lua_State *L)
|
||||||
|
{
|
||||||
|
NO_MAP_LOCK_REQUIRED;
|
||||||
|
|
||||||
|
LuaPcgRandom *o = checkobject(L, 1);
|
||||||
|
u32 min = lua_isnumber(L, 2) ? lua_tointeger(L, 2) : o->m_rnd.RANDOM_MIN;
|
||||||
|
u32 max = lua_isnumber(L, 3) ? lua_tointeger(L, 3) : o->m_rnd.RANDOM_MAX;
|
||||||
|
|
||||||
|
lua_pushinteger(L, o->m_rnd.range(min, max));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int LuaPcgRandom::l_rand_normal_dist(lua_State *L)
|
||||||
|
{
|
||||||
|
NO_MAP_LOCK_REQUIRED;
|
||||||
|
|
||||||
|
LuaPcgRandom *o = checkobject(L, 1);
|
||||||
|
u32 min = lua_isnumber(L, 2) ? lua_tointeger(L, 2) : o->m_rnd.RANDOM_MIN;
|
||||||
|
u32 max = lua_isnumber(L, 3) ? lua_tointeger(L, 3) : o->m_rnd.RANDOM_MAX;
|
||||||
|
int num_trials = lua_isnumber(L, 4) ? lua_tointeger(L, 4) : 6;
|
||||||
|
|
||||||
|
lua_pushinteger(L, o->m_rnd.randNormalDist(min, max, num_trials));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int LuaPcgRandom::create_object(lua_State *L)
|
||||||
|
{
|
||||||
|
lua_Integer seed = luaL_checknumber(L, 1);
|
||||||
|
LuaPcgRandom *o = lua_isnumber(L, 2) ?
|
||||||
|
new LuaPcgRandom(seed, lua_tointeger(L, 2)) :
|
||||||
|
new LuaPcgRandom(seed);
|
||||||
|
*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
|
||||||
|
luaL_getmetatable(L, className);
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int LuaPcgRandom::gc_object(lua_State *L)
|
||||||
|
{
|
||||||
|
LuaPcgRandom *o = *(LuaPcgRandom **)(lua_touserdata(L, 1));
|
||||||
|
delete o;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LuaPcgRandom *LuaPcgRandom::checkobject(lua_State *L, int narg)
|
||||||
|
{
|
||||||
|
luaL_checktype(L, narg, LUA_TUSERDATA);
|
||||||
|
void *ud = luaL_checkudata(L, narg, className);
|
||||||
|
if (!ud)
|
||||||
|
luaL_typerror(L, narg, className);
|
||||||
|
return *(LuaPcgRandom **)ud;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LuaPcgRandom::Register(lua_State *L)
|
||||||
|
{
|
||||||
|
lua_newtable(L);
|
||||||
|
int methodtable = lua_gettop(L);
|
||||||
|
luaL_newmetatable(L, className);
|
||||||
|
int metatable = lua_gettop(L);
|
||||||
|
|
||||||
|
lua_pushliteral(L, "__metatable");
|
||||||
|
lua_pushvalue(L, methodtable);
|
||||||
|
lua_settable(L, metatable);
|
||||||
|
|
||||||
|
lua_pushliteral(L, "__index");
|
||||||
|
lua_pushvalue(L, methodtable);
|
||||||
|
lua_settable(L, metatable);
|
||||||
|
|
||||||
|
lua_pushliteral(L, "__gc");
|
||||||
|
lua_pushcfunction(L, gc_object);
|
||||||
|
lua_settable(L, metatable);
|
||||||
|
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
luaL_openlib(L, 0, methods, 0);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_register(L, className, create_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char LuaPcgRandom::className[] = "PcgRandom";
|
||||||
|
const luaL_reg LuaPcgRandom::methods[] = {
|
||||||
|
luamethod(LuaPcgRandom, next),
|
||||||
|
luamethod(LuaPcgRandom, rand_normal_dist),
|
||||||
|
{0,0}
|
||||||
|
};
|
||||||
|
@ -64,6 +64,9 @@ class LuaPerlinNoiseMap : public ModApiBase {
|
|||||||
static const char className[];
|
static const char className[];
|
||||||
static const luaL_reg methods[];
|
static const luaL_reg methods[];
|
||||||
|
|
||||||
|
// Exported functions
|
||||||
|
|
||||||
|
// garbage collector
|
||||||
static int gc_object(lua_State *L);
|
static int gc_object(lua_State *L);
|
||||||
|
|
||||||
static int l_get2dMap(lua_State *L);
|
static int l_get2dMap(lua_State *L);
|
||||||
@ -104,18 +107,51 @@ private:
|
|||||||
static int l_next(lua_State *L);
|
static int l_next(lua_State *L);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LuaPseudoRandom(int seed);
|
LuaPseudoRandom(int seed) :
|
||||||
|
m_pseudo(seed) {}
|
||||||
~LuaPseudoRandom();
|
|
||||||
|
|
||||||
const PseudoRandom& getItem() const;
|
|
||||||
PseudoRandom& getItem();
|
|
||||||
|
|
||||||
// LuaPseudoRandom(seed)
|
// LuaPseudoRandom(seed)
|
||||||
// Creates an LuaPseudoRandom and leaves it on top of stack
|
// Creates an LuaPseudoRandom and leaves it on top of stack
|
||||||
static int create_object(lua_State *L);
|
static int create_object(lua_State *L);
|
||||||
|
|
||||||
static LuaPseudoRandom* checkobject(lua_State *L, int narg);
|
static LuaPseudoRandom *checkobject(lua_State *L, int narg);
|
||||||
|
|
||||||
|
static void Register(lua_State *L);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
LuaPcgRandom
|
||||||
|
*/
|
||||||
|
class LuaPcgRandom : public ModApiBase {
|
||||||
|
private:
|
||||||
|
PcgRandom m_rnd;
|
||||||
|
|
||||||
|
static const char className[];
|
||||||
|
static const luaL_reg methods[];
|
||||||
|
|
||||||
|
// Exported functions
|
||||||
|
|
||||||
|
// garbage collector
|
||||||
|
static int gc_object(lua_State *L);
|
||||||
|
|
||||||
|
// next(self, min=-2147483648, max=2147483647) -> get next value
|
||||||
|
static int l_next(lua_State *L);
|
||||||
|
|
||||||
|
// rand_normal_dist(self, min=-2147483648, max=2147483647, num_trials=6) ->
|
||||||
|
// get next normally distributed random value
|
||||||
|
static int l_rand_normal_dist(lua_State *L);
|
||||||
|
|
||||||
|
public:
|
||||||
|
LuaPcgRandom(u64 seed) :
|
||||||
|
m_rnd(seed) {}
|
||||||
|
LuaPcgRandom(u64 seed, u64 seq) :
|
||||||
|
m_rnd(seed, seq) {}
|
||||||
|
|
||||||
|
// LuaPcgRandom(seed)
|
||||||
|
// Creates an LuaPcgRandom and leaves it on top of stack
|
||||||
|
static int create_object(lua_State *L);
|
||||||
|
|
||||||
|
static LuaPcgRandom *checkobject(lua_State *L, int narg);
|
||||||
|
|
||||||
static void Register(lua_State *L);
|
static void Register(lua_State *L);
|
||||||
};
|
};
|
||||||
|
@ -92,6 +92,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top)
|
|||||||
LuaPerlinNoise::Register(L);
|
LuaPerlinNoise::Register(L);
|
||||||
LuaPerlinNoiseMap::Register(L);
|
LuaPerlinNoiseMap::Register(L);
|
||||||
LuaPseudoRandom::Register(L);
|
LuaPseudoRandom::Register(L);
|
||||||
|
LuaPcgRandom::Register(L);
|
||||||
LuaVoxelManip::Register(L);
|
LuaVoxelManip::Register(L);
|
||||||
NodeMetaRef::Register(L);
|
NodeMetaRef::Register(L);
|
||||||
NodeTimerRef::Register(L);
|
NodeTimerRef::Register(L);
|
||||||
|
@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "../constants.h" // BS, MAP_BLOCKSIZE
|
#include "../constants.h" // BS, MAP_BLOCKSIZE
|
||||||
|
#include "../noise.h" // PseudoRandom, PcgRandom
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
@ -115,36 +116,32 @@ void FacePositionCache::generateFacePosition(u16 d)
|
|||||||
myrand
|
myrand
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static unsigned long next = 1;
|
PcgRandom g_pcgrand;
|
||||||
|
|
||||||
/* RAND_MAX assumed to be 32767 */
|
u32 myrand()
|
||||||
int myrand(void)
|
|
||||||
{
|
{
|
||||||
next = next * 1103515245 + 12345;
|
return g_pcgrand.next();
|
||||||
return((unsigned)(next/65536) % 32768);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void mysrand(unsigned seed)
|
void mysrand(unsigned int seed)
|
||||||
{
|
{
|
||||||
next = seed;
|
g_pcgrand.seed(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void myrand_bytes(void *out, size_t len)
|
||||||
|
{
|
||||||
|
g_pcgrand.bytes(out, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
int myrand_range(int min, int max)
|
int myrand_range(int min, int max)
|
||||||
{
|
{
|
||||||
if(max-min > MYRAND_MAX)
|
return g_pcgrand.range(min, max);
|
||||||
{
|
|
||||||
errorstream<<"WARNING: myrand_range: max-min > MYRAND_MAX"<<std::endl;
|
|
||||||
max = min + MYRAND_MAX;
|
|
||||||
}
|
|
||||||
if(min > max)
|
|
||||||
{
|
|
||||||
errorstream<<"WARNING: myrand_range: min > max"<<std::endl;
|
|
||||||
return max;
|
|
||||||
}
|
|
||||||
return (myrand()%(max-min+1))+min;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 64-bit unaligned version of MurmurHash
|
|
||||||
|
/*
|
||||||
|
64-bit unaligned version of MurmurHash
|
||||||
|
*/
|
||||||
u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed)
|
u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed)
|
||||||
{
|
{
|
||||||
const u64 m = 0xc6a4a7935bd1e995ULL;
|
const u64 m = 0xc6a4a7935bd1e995ULL;
|
||||||
@ -159,12 +156,12 @@ u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed)
|
|||||||
memcpy(&k, data, sizeof(u64));
|
memcpy(&k, data, sizeof(u64));
|
||||||
data++;
|
data++;
|
||||||
|
|
||||||
k *= m;
|
k *= m;
|
||||||
k ^= k >> r;
|
k ^= k >> r;
|
||||||
k *= m;
|
k *= m;
|
||||||
|
|
||||||
h ^= k;
|
h ^= k;
|
||||||
h *= m;
|
h *= m;
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned char *data2 = (const unsigned char *)data;
|
const unsigned char *data2 = (const unsigned char *)data;
|
||||||
@ -178,13 +175,13 @@ u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed)
|
|||||||
case 1: h ^= (u64)data2[0];
|
case 1: h ^= (u64)data2[0];
|
||||||
h *= m;
|
h *= m;
|
||||||
}
|
}
|
||||||
|
|
||||||
h ^= h >> r;
|
h ^= h >> r;
|
||||||
h *= m;
|
h *= m;
|
||||||
h ^= h >> r;
|
h ^= h >> r;
|
||||||
|
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -197,7 +194,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
|
|||||||
f32 camera_fov, f32 range, f32 *distance_ptr)
|
f32 camera_fov, f32 range, f32 *distance_ptr)
|
||||||
{
|
{
|
||||||
v3s16 blockpos_nodes = blockpos_b * MAP_BLOCKSIZE;
|
v3s16 blockpos_nodes = blockpos_b * MAP_BLOCKSIZE;
|
||||||
|
|
||||||
// Block center position
|
// Block center position
|
||||||
v3f blockpos(
|
v3f blockpos(
|
||||||
((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
|
((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
|
||||||
@ -213,7 +210,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
|
|||||||
|
|
||||||
if(distance_ptr)
|
if(distance_ptr)
|
||||||
*distance_ptr = d;
|
*distance_ptr = d;
|
||||||
|
|
||||||
// If block is far away, it's not in sight
|
// If block is far away, it's not in sight
|
||||||
if(d > range)
|
if(d > range)
|
||||||
return false;
|
return false;
|
||||||
@ -221,7 +218,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
|
|||||||
// Maximum radius of a block. The magic number is
|
// Maximum radius of a block. The magic number is
|
||||||
// sqrt(3.0) / 2.0 in literal form.
|
// sqrt(3.0) / 2.0 in literal form.
|
||||||
f32 block_max_radius = 0.866025403784 * MAP_BLOCKSIZE * BS;
|
f32 block_max_radius = 0.866025403784 * MAP_BLOCKSIZE * BS;
|
||||||
|
|
||||||
// If block is (nearly) touching the camera, don't
|
// If block is (nearly) touching the camera, don't
|
||||||
// bother validating further (that is, render it anyway)
|
// bother validating further (that is, render it anyway)
|
||||||
if(d < block_max_radius)
|
if(d < block_max_radius)
|
||||||
@ -242,7 +239,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
|
|||||||
// Cosine of the angle between the camera direction
|
// Cosine of the angle between the camera direction
|
||||||
// and the block direction (camera_dir is an unit vector)
|
// and the block direction (camera_dir is an unit vector)
|
||||||
f32 cosangle = dforward / blockpos_adj.getLength();
|
f32 cosangle = dforward / blockpos_adj.getLength();
|
||||||
|
|
||||||
// If block is not in the field of view, skip it
|
// If block is not in the field of view, skip it
|
||||||
if(cosangle < cos(camera_fov / 2))
|
if(cosangle < cos(camera_fov / 2))
|
||||||
return false;
|
return false;
|
||||||
|
@ -239,10 +239,10 @@ inline float wrapDegrees_180(float f)
|
|||||||
/*
|
/*
|
||||||
Pseudo-random (VC++ rand() sucks)
|
Pseudo-random (VC++ rand() sucks)
|
||||||
*/
|
*/
|
||||||
int myrand(void);
|
#define MYRAND_RANGE 0xffffffff
|
||||||
void mysrand(unsigned seed);
|
u32 myrand();
|
||||||
#define MYRAND_MAX 32767
|
void mysrand(unsigned int seed);
|
||||||
|
void myrand_bytes(void *out, size_t len);
|
||||||
int myrand_range(int min, int max);
|
int myrand_range(int min, int max);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user