mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2024-11-21 12:01:05 +00:00
New M_Random implementation
This commit is contained in:
parent
a752e6c8e4
commit
9e5a828508
8 changed files with 259 additions and 26 deletions
|
@ -278,4 +278,26 @@ char *I_ClipboardPaste(void)
|
||||||
|
|
||||||
void I_RegisterSysCommands(void) {}
|
void I_RegisterSysCommands(void) {}
|
||||||
|
|
||||||
|
// This is identical to the SDL implementation.
|
||||||
|
size_t I_GetRandomBytes(char *destination, size_t count)
|
||||||
|
{
|
||||||
|
FILE *rndsource;
|
||||||
|
size_t actual_bytes;
|
||||||
|
|
||||||
|
if (!(rndsource = fopen("/dev/urandom", "r")))
|
||||||
|
if (!(rndsource = fopen("/dev/random", "r")))
|
||||||
|
actual_bytes = 0;
|
||||||
|
|
||||||
|
if (rndsource)
|
||||||
|
{
|
||||||
|
actual_bytes = fread(destination, 1, count, rndsource);
|
||||||
|
fclose(rndsource);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actual_bytes == 0)
|
||||||
|
I_OutputMsg("I_GetRandomBytes(): couldn't get any random bytes");
|
||||||
|
|
||||||
|
return actual_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
#include "../sdl/dosstr.c"
|
#include "../sdl/dosstr.c"
|
||||||
|
|
12
src/d_main.c
12
src/d_main.c
|
@ -70,6 +70,7 @@
|
||||||
#include "filesrch.h" // refreshdirmenu
|
#include "filesrch.h" // refreshdirmenu
|
||||||
#include "g_input.h" // tutorial mode control scheming
|
#include "g_input.h" // tutorial mode control scheming
|
||||||
#include "m_perfstats.h"
|
#include "m_perfstats.h"
|
||||||
|
#include "m_random.h"
|
||||||
|
|
||||||
#ifdef CMAKECONFIG
|
#ifdef CMAKECONFIG
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
@ -1334,11 +1335,12 @@ void D_SRB2Main(void)
|
||||||
snprintf(addonsdir, sizeof addonsdir, "%s%s%s", srb2home, PATHSEP, "addons");
|
snprintf(addonsdir, sizeof addonsdir, "%s%s%s", srb2home, PATHSEP, "addons");
|
||||||
I_mkdir(addonsdir, 0755);
|
I_mkdir(addonsdir, 0755);
|
||||||
|
|
||||||
// rand() needs seeded regardless of password
|
// seed M_Random because it is necessary; seed P_Random for scripts that
|
||||||
srand((unsigned int)time(NULL));
|
// might want to use random numbers immediately at start
|
||||||
rand();
|
if (!M_RandomSeedFromOS())
|
||||||
rand();
|
M_RandomSeed((UINT32)time(NULL)); // less good but serviceable
|
||||||
rand();
|
|
||||||
|
P_SetRandSeed(M_RandomizedSeed());
|
||||||
|
|
||||||
if (M_CheckParm("-password") && M_IsNextParm())
|
if (M_CheckParm("-password") && M_IsNextParm())
|
||||||
D_SetPassword(M_GetNextParm());
|
D_SetPassword(M_GetNextParm());
|
||||||
|
|
|
@ -180,6 +180,11 @@ const char *I_ClipboardPaste(void)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t I_GetRandomBytes(char *destination, size_t amount)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void I_RegisterSysCommands(void) {}
|
void I_RegisterSysCommands(void) {}
|
||||||
|
|
||||||
void I_GetCursorPosition(INT32 *x, INT32 *y)
|
void I_GetCursorPosition(INT32 *x, INT32 *y)
|
||||||
|
|
|
@ -49,6 +49,10 @@ size_t I_GetFreeMem(size_t *total);
|
||||||
*/
|
*/
|
||||||
precise_t I_GetPreciseTime(void);
|
precise_t I_GetPreciseTime(void);
|
||||||
|
|
||||||
|
/** \brief Fills a buffer with random data, returns amount of data obtained.
|
||||||
|
*/
|
||||||
|
size_t I_GetRandomBytes(char *destination, size_t count);
|
||||||
|
|
||||||
/** \brief Get the precision of precise_t in units per second. Invocations of
|
/** \brief Get the precision of precise_t in units per second. Invocations of
|
||||||
this function for the program's duration MUST return the same value.
|
this function for the program's duration MUST return the same value.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1256,8 +1256,6 @@ static int libd_RandomKey(lua_State *L)
|
||||||
INT32 a = (INT32)luaL_checkinteger(L, 1);
|
INT32 a = (INT32)luaL_checkinteger(L, 1);
|
||||||
|
|
||||||
HUDONLY
|
HUDONLY
|
||||||
if (a > 65536)
|
|
||||||
LUA_UsageWarning(L, "v.RandomKey: range > 65536 is undefined behavior");
|
|
||||||
lua_pushinteger(L, M_RandomKey(a));
|
lua_pushinteger(L, M_RandomKey(a));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -1268,13 +1266,6 @@ static int libd_RandomRange(lua_State *L)
|
||||||
INT32 b = (INT32)luaL_checkinteger(L, 2);
|
INT32 b = (INT32)luaL_checkinteger(L, 2);
|
||||||
|
|
||||||
HUDONLY
|
HUDONLY
|
||||||
if (b < a) {
|
|
||||||
INT32 c = a;
|
|
||||||
a = b;
|
|
||||||
b = c;
|
|
||||||
}
|
|
||||||
if ((b-a+1) > 65536)
|
|
||||||
LUA_UsageWarning(L, "v.RandomRange: range > 65536 is undefined behavior");
|
|
||||||
lua_pushinteger(L, M_RandomRange(a, b));
|
lua_pushinteger(L, M_RandomRange(a, b));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
192
src/m_random.c
192
src/m_random.c
|
@ -3,6 +3,7 @@
|
||||||
// Copyright (C) 1993-1996 by id Software, Inc.
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
||||||
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
||||||
// Copyright (C) 2012-2016 by Matthew "Kaito Sinclaire" Walsh.
|
// Copyright (C) 2012-2016 by Matthew "Kaito Sinclaire" Walsh.
|
||||||
|
// Copyright (C) 2022-2023 by tertu marybig.
|
||||||
// Copyright (C) 1999-2023 by Sonic Team Junior.
|
// Copyright (C) 1999-2023 by Sonic Team Junior.
|
||||||
//
|
//
|
||||||
// This program is free software distributed under the
|
// This program is free software distributed under the
|
||||||
|
@ -14,11 +15,122 @@
|
||||||
|
|
||||||
#include "doomdef.h"
|
#include "doomdef.h"
|
||||||
#include "doomtype.h"
|
#include "doomtype.h"
|
||||||
|
#include "i_system.h" // I_GetRandomBytes
|
||||||
|
|
||||||
#include "m_random.h"
|
#include "m_random.h"
|
||||||
#include "m_fixed.h"
|
#include "m_fixed.h"
|
||||||
|
|
||||||
#include "m_cond.h" // totalplaytime
|
// SFC32 random number generator implementation
|
||||||
|
|
||||||
|
typedef struct rnstate_s {
|
||||||
|
UINT32 data[3];
|
||||||
|
UINT32 counter;
|
||||||
|
} rnstate_t;
|
||||||
|
|
||||||
|
/** Generate a raw uniform random number using a particular state.
|
||||||
|
*
|
||||||
|
* \param state The RNG state to use.
|
||||||
|
* \return A random UINT32.
|
||||||
|
*/
|
||||||
|
static inline UINT32 RandomState_Get32(rnstate_t *state) {
|
||||||
|
UINT32 result, b, c;
|
||||||
|
|
||||||
|
b = state->data[1];
|
||||||
|
c = state->data[2];
|
||||||
|
result = state->data[0] + b + state->counter++;
|
||||||
|
|
||||||
|
state->data[0] = b ^ (b >> 9);
|
||||||
|
state->data[1] = c * 9;
|
||||||
|
state->data[2] = ((c << 21) | (c >> 11)) + result;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Seed an SFC32 RNG state with up to 96 bits of seed data.
|
||||||
|
*
|
||||||
|
* \param state The RNG state to seed.
|
||||||
|
* \param seeds A pointer to up to 3 UINT32s to use as seed data.
|
||||||
|
* \param seed_count The number of seed words.
|
||||||
|
*/
|
||||||
|
static inline void RandomState_Seed(rnstate_t *state, UINT32 *seeds, size_t seed_count)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
state->counter = 1;
|
||||||
|
|
||||||
|
for(i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
UINT32 seed_word;
|
||||||
|
|
||||||
|
if(i < seed_count)
|
||||||
|
seed_word = seeds[i];
|
||||||
|
else
|
||||||
|
seed_word = 0;
|
||||||
|
|
||||||
|
// For SFC32, seed data should be stored in the state in reverse order.
|
||||||
|
state->data[2-i] = seed_word;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < 16; i++)
|
||||||
|
RandomState_Get32(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a uniform number in the range [0, limit).
|
||||||
|
* Technique is based on a combination of scaling and rejection sampling
|
||||||
|
* and is adapted from Daniel Lemire.
|
||||||
|
*
|
||||||
|
* \note Any UINT32 is a valid argument for limit.
|
||||||
|
*
|
||||||
|
* \param state The RNG state to use.
|
||||||
|
* \param limit The upper limit of the range.
|
||||||
|
* \return A UINT32 in the range [0, limit).
|
||||||
|
*/
|
||||||
|
static inline UINT32 RandomState_GetKey32(rnstate_t *state, const UINT32 limit)
|
||||||
|
{
|
||||||
|
UINT32 raw_random, scaled_lower_word;
|
||||||
|
UINT64 scaled_random;
|
||||||
|
|
||||||
|
// This algorithm won't work correctly if passed a 0.
|
||||||
|
if (limit == 0) return 0;
|
||||||
|
|
||||||
|
raw_random = RandomState_Get32(state);
|
||||||
|
scaled_random = (UINT64)raw_random * (UINT64)limit;
|
||||||
|
|
||||||
|
/*The high bits of scaled_random now contain the number we want, but it is
|
||||||
|
possible, depending on the number we generated and the value of limit,
|
||||||
|
that there is bias in the result. The rest of this code is for ensuring
|
||||||
|
that does not happen.
|
||||||
|
*/
|
||||||
|
scaled_lower_word = (UINT32)scaled_random;
|
||||||
|
|
||||||
|
// If we're lucky, we can bail out now and avoid the division
|
||||||
|
if (scaled_lower_word < limit)
|
||||||
|
{
|
||||||
|
// Scale the limit to improve the chance of success.
|
||||||
|
// After this, the first result might turn out to be good enough.
|
||||||
|
UINT32 scaled_limit;
|
||||||
|
// An explanation for this trick: scaled_limit should be
|
||||||
|
// (UINT32_MAX+1)%range, but if that was computed directly the result
|
||||||
|
// would need to be computed as a UINT64. This trick allows it to be
|
||||||
|
// computed using 32-bit arithmetic.
|
||||||
|
scaled_limit = (-limit) % limit;
|
||||||
|
|
||||||
|
while (scaled_lower_word < scaled_limit)
|
||||||
|
{
|
||||||
|
raw_random = RandomState_Get32(state);
|
||||||
|
scaled_random = (UINT64)raw_random * (UINT64)limit;
|
||||||
|
scaled_lower_word = (UINT32)scaled_random;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return scaled_random >> 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default seed is the hexadecimal digits of pi, though it will be overwritten.
|
||||||
|
static rnstate_t m_randomstate = {
|
||||||
|
.data = {0x4A3B6035U, 0x99555606U, 0x6F603421U},
|
||||||
|
.counter = 16
|
||||||
|
};
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// RNG functions (not synched)
|
// RNG functions (not synched)
|
||||||
|
@ -31,13 +143,7 @@
|
||||||
*/
|
*/
|
||||||
fixed_t M_RandomFixed(void)
|
fixed_t M_RandomFixed(void)
|
||||||
{
|
{
|
||||||
#if RAND_MAX < 65535
|
return RandomState_Get32(&m_randomstate) >> (32-FRACBITS);
|
||||||
// Compensate for insufficient randomness.
|
|
||||||
fixed_t rndv = (rand()&1)<<15;
|
|
||||||
return rand()^rndv;
|
|
||||||
#else
|
|
||||||
return (rand() & 0xFFFF);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Provides a random byte. Distribution is uniform.
|
/** Provides a random byte. Distribution is uniform.
|
||||||
|
@ -47,7 +153,7 @@ fixed_t M_RandomFixed(void)
|
||||||
*/
|
*/
|
||||||
UINT8 M_RandomByte(void)
|
UINT8 M_RandomByte(void)
|
||||||
{
|
{
|
||||||
return (rand() & 0xFF);
|
return RandomState_Get32(&m_randomstate) >> 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Provides a random integer for picking random elements from an array.
|
/** Provides a random integer for picking random elements from an array.
|
||||||
|
@ -59,7 +165,22 @@ UINT8 M_RandomByte(void)
|
||||||
*/
|
*/
|
||||||
INT32 M_RandomKey(INT32 a)
|
INT32 M_RandomKey(INT32 a)
|
||||||
{
|
{
|
||||||
return (INT32)((rand()/((float)RAND_MAX+1.0f))*a);
|
boolean range_is_negative;
|
||||||
|
INT64 range;
|
||||||
|
INT32 random_result;
|
||||||
|
|
||||||
|
range = a;
|
||||||
|
range_is_negative = range < 0;
|
||||||
|
|
||||||
|
if(range_is_negative)
|
||||||
|
range = -range;
|
||||||
|
|
||||||
|
random_result = RandomState_GetKey32(&m_randomstate, (UINT32)range);
|
||||||
|
|
||||||
|
if(range_is_negative)
|
||||||
|
random_result = -random_result;
|
||||||
|
|
||||||
|
return random_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Provides a random integer in a given range.
|
/** Provides a random integer in a given range.
|
||||||
|
@ -72,7 +193,46 @@ INT32 M_RandomKey(INT32 a)
|
||||||
*/
|
*/
|
||||||
INT32 M_RandomRange(INT32 a, INT32 b)
|
INT32 M_RandomRange(INT32 a, INT32 b)
|
||||||
{
|
{
|
||||||
return (INT32)((rand()/((float)RAND_MAX+1.0f))*(b-a+1))+a;
|
if (b < a)
|
||||||
|
{
|
||||||
|
INT32 temp;
|
||||||
|
|
||||||
|
temp = a;
|
||||||
|
a = b;
|
||||||
|
b = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UINT32 spread = b-a+1;
|
||||||
|
return (INT32)((INT64)RandomState_GetKey32(&m_randomstate, spread) + a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Attempts to seed the unsynched RNG from a good random number source
|
||||||
|
* provided by the operating system.
|
||||||
|
* \return true on success, false on failure.
|
||||||
|
*/
|
||||||
|
boolean M_RandomSeedFromOS(void)
|
||||||
|
{
|
||||||
|
UINT32 complete_word_count;
|
||||||
|
|
||||||
|
union {
|
||||||
|
UINT32 words[3];
|
||||||
|
char bytes[sizeof(UINT32[3])];
|
||||||
|
} seed_data;
|
||||||
|
|
||||||
|
complete_word_count = I_GetRandomBytes((char *)&seed_data.bytes, sizeof(seed_data)) / sizeof(UINT32);
|
||||||
|
|
||||||
|
// If we get even 1 word of seed, it's fine, but any less probably is not fine.
|
||||||
|
if (complete_word_count == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RandomState_Seed(&m_randomstate, (UINT32 *)&seed_data.words, complete_word_count);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void M_RandomSeed(UINT32 seed)
|
||||||
|
{
|
||||||
|
RandomState_Seed(&m_randomstate, &seed, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -246,10 +406,18 @@ void P_SetRandSeedD(const char *rfile, INT32 rline, UINT32 seed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets a randomized seed for setting the random seed.
|
/** Gets a randomized seed for setting the random seed.
|
||||||
|
* This function will never return 0, as the current P_Random implementation
|
||||||
|
* cannot handle a zero seed. Any other seed is equally likely.
|
||||||
*
|
*
|
||||||
* \sa P_GetRandSeed
|
* \sa P_GetRandSeed
|
||||||
*/
|
*/
|
||||||
UINT32 M_RandomizedSeed(void)
|
UINT32 M_RandomizedSeed(void)
|
||||||
{
|
{
|
||||||
return ((serverGamedata->totalplaytime & 0xFFFF) << 16) | M_RandomFixed();
|
UINT32 seed;
|
||||||
|
|
||||||
|
do {
|
||||||
|
seed = RandomState_Get32(&m_randomstate);
|
||||||
|
} while(seed == 0);
|
||||||
|
|
||||||
|
return seed;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// Copyright (C) 1993-1996 by id Software, Inc.
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
||||||
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
||||||
// Copyright (C) 2012-2016 by Matthew "Kaito Sinclaire" Walsh.
|
// Copyright (C) 2012-2016 by Matthew "Kaito Sinclaire" Walsh.
|
||||||
|
// Copyright (C) 2022-2023 by tertu marybig.
|
||||||
// Copyright (C) 1999-2023 by Sonic Team Junior.
|
// Copyright (C) 1999-2023 by Sonic Team Junior.
|
||||||
//
|
//
|
||||||
// This program is free software distributed under the
|
// This program is free software distributed under the
|
||||||
|
@ -29,6 +30,8 @@ fixed_t M_RandomFixed(void);
|
||||||
UINT8 M_RandomByte(void);
|
UINT8 M_RandomByte(void);
|
||||||
INT32 M_RandomKey(INT32 a);
|
INT32 M_RandomKey(INT32 a);
|
||||||
INT32 M_RandomRange(INT32 a, INT32 b);
|
INT32 M_RandomRange(INT32 a, INT32 b);
|
||||||
|
boolean M_RandomSeedFromOS(void);
|
||||||
|
void M_RandomSeed(UINT32 a);
|
||||||
|
|
||||||
// PRNG functions
|
// PRNG functions
|
||||||
#ifdef DEBUGRANDOM
|
#ifdef DEBUGRANDOM
|
||||||
|
|
|
@ -41,6 +41,12 @@ typedef DWORD (WINAPI *p_timeGetTime) (void);
|
||||||
typedef UINT (WINAPI *p_timeEndPeriod) (UINT);
|
typedef UINT (WINAPI *p_timeEndPeriod) (UINT);
|
||||||
typedef HANDLE (WINAPI *p_OpenFileMappingA) (DWORD, BOOL, LPCSTR);
|
typedef HANDLE (WINAPI *p_OpenFileMappingA) (DWORD, BOOL, LPCSTR);
|
||||||
typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
|
typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
|
||||||
|
|
||||||
|
// This is for RtlGenRandom.
|
||||||
|
#define SystemFunction036 NTAPI SystemFunction036
|
||||||
|
#include <ntsecapi.h>
|
||||||
|
#undef SystemFunction036
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -2776,6 +2782,38 @@ INT32 I_PutEnv(char *variable)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t I_GetRandomBytes(char *destination, size_t count)
|
||||||
|
{
|
||||||
|
#if defined (__unix__) || defined (UNIXCOMMON) || defined(__APPLE__)
|
||||||
|
FILE *rndsource;
|
||||||
|
size_t actual_bytes;
|
||||||
|
|
||||||
|
if (!(rndsource = fopen("/dev/urandom", "r")))
|
||||||
|
if (!(rndsource = fopen("/dev/random", "r")))
|
||||||
|
actual_bytes = 0;
|
||||||
|
|
||||||
|
if (rndsource)
|
||||||
|
{
|
||||||
|
actual_bytes = fread(destination, 1, count, rndsource);
|
||||||
|
fclose(rndsource);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actual_bytes == 0)
|
||||||
|
I_OutputMsg("I_GetRandomBytes(): couldn't get any random bytes");
|
||||||
|
|
||||||
|
return actual_bytes;
|
||||||
|
#elif defined (_WIN32)
|
||||||
|
if (RtlGenRandom(destination, count))
|
||||||
|
return count;
|
||||||
|
|
||||||
|
I_OutputMsg("I_GetRandomBytes(): couldn't get any random bytes");
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
#warning SDL I_GetRandomBytes is not implemented on this platform.
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
INT32 I_ClipboardCopy(const char *data, size_t size)
|
INT32 I_ClipboardCopy(const char *data, size_t size)
|
||||||
{
|
{
|
||||||
char storage[256];
|
char storage[256];
|
||||||
|
|
Loading…
Reference in a new issue