remove embedded noise data, mix bits for determinism instead

This commit is contained in:
jacob 2025-02-27 08:18:14 -06:00
parent bd693ac1eb
commit 48999f5ab4
11 changed files with 52 additions and 168 deletions

BIN
res/noise.dat (Stored with Git LFS)

Binary file not shown.

View File

@ -322,7 +322,6 @@ void app_entry_point(struct string args_str)
struct sock_startup_receipt sock_sr = sock_startup();
struct host_startup_receipt host_sr = host_startup(&sock_sr);
struct resource_startup_receipt resource_sr = resource_startup();
struct rng_startup_receipt rng_sr = rng_startup(&resource_sr);
struct renderer_startup_receipt renderer_sr = renderer_startup(&window);
struct work_startup_receipt work_sr = work_startup(worker_count);
struct asset_cache_startup_receipt asset_cache_sr = asset_cache_startup(&work_sr);
@ -339,7 +338,6 @@ void app_entry_point(struct string args_str)
(UNUSED)user_sr;
(UNUSED)playback_sr;
(UNUSED)rng_sr;
/* Show window */
sys_window_show(&window);

101
src/rng.c
View File

@ -1,111 +1,26 @@
#include "rng.h"
#include "sys.h"
#include "resource.h"
#include "arena.h"
GLOBAL struct {
struct arena arena;
u64 *noise;
u64 noise_count;
} G = ZI, DEBUG_ALIAS(G, G_rng);
/* ========================== *
* Generate random number file
* (unused function)
* ========================== */
#if 0
INTERNAL void gen_random_file(struct string path, u32 count)
{
{
/* Clear file */
struct sys_file f = sys_file_open_write(path);
sys_file_write(f, STRING(0, 0));
sys_file_close(f);
}
struct sys_file f = sys_file_open_append(path);
for (u32 i = 0; i < count; ++i) {
u64 rand = rng_rand_u64();
sys_file_write(f, STRING_FROM_STRUCT(&rand));
}
sys_file_close(f);
}
#endif
/* ========================== *
* Startup
* ========================== */
struct rng_startup_receipt rng_startup(struct resource_startup_receipt *resource_sr)
{
(UNUSED)resource_sr;
struct string noise_path = LIT("res/noise.dat");
if (resource_exists(noise_path)) {
struct resource r = resource_open(noise_path);
G.noise_count = r.data.len / sizeof(*G.noise);
G.arena = arena_alloc(sizeof(u64) * G.noise_count);
G.noise = arena_push_array(&G.arena, u64, G.noise_count);
MEMCPY(G.noise, r.data.text, r.data.len);
resource_close(r);
} else {
sys_panic(LIT("Failed to locate pre-computed noise resource"));
}
return (struct rng_startup_receipt) { 0 };
}
/* ========================== *
* Rand
* ========================== */
u32 rng_rand_u32(void)
u32 rng_true_u32(void)
{
u32 v = 0;
sys_rand(STRING_FROM_STRUCT(&v));
sys_true_rand(STRING_FROM_STRUCT(&v));
return v;
}
u64 rng_rand_u64(void)
u64 rng_true_u64(void)
{
u64 v = 0;
sys_rand(STRING_FROM_STRUCT(&v));
sys_true_rand(STRING_FROM_STRUCT(&v));
return v;
}
f32 rng_rand_f32(f32 range_start, f32 range_end)
f32 rng_true_f32(f32 range_start, f32 range_end)
{
return ((f32)rng_rand_u32() / (f32)U32_MAX) * (range_end - range_start) + range_start;
return ((f32)rng_true_u32() / (f32)U32_MAX) * (range_end - range_start) + range_start;
}
f64 rng_rand_f64(f64 range_start, f64 range_end)
f64 rng_true_f64(f64 range_start, f64 range_end)
{
return ((f64)rng_rand_u64() / (f64)U64_MAX) * (range_end - range_start) + range_start;
}
/* ========================== *
* Noise
* Functions that return a deterministic pre-computed random number based on the provided seed
*
* NOTE: Noise pattern repeats after period depending on how much noise data exists in noise.dat
* ========================== */
/* TODO: Use deterministic prng rather than embedded data */
u32 rng_noise_u32(u64 seed)
{
return (u32)G.noise[seed % G.noise_count];
}
u64 rng_noise_u64(u64 seed)
{
return G.noise[seed % G.noise_count];
}
f32 rng_noise_f32(u64 seed, f32 range_start, f32 range_end)
{
return ((f32)rng_noise_u32(seed) / (f32)U32_MAX) * (range_end - range_start) + range_start;
}
f32 rng_noise_f64(u64 seed, f64 range_start, f64 range_end)
{
return ((f64)rng_noise_u64(seed) / (f64)U64_MAX) * (range_end - range_start) + range_start;
return ((f64)rng_true_u64() / (f64)U64_MAX) * (range_end - range_start) + range_start;
}

View File

@ -1,22 +1,10 @@
#ifndef RNG_H
#define RNG_H
struct resource_startup_receipt;
/* Startup */
struct rng_startup_receipt { i32 _; };
struct rng_startup_receipt rng_startup(struct resource_startup_receipt *resource_sr);
/* Rand */
u32 rng_rand_u32(void);
u64 rng_rand_u64(void);
f32 rng_rand_f32(f32 range_start, f32 range_end);
f64 rng_rand_f64(f64 range_start, f64 range_end);
/* Noise */
u32 rng_noise_u32(u64 seed);
u64 rng_noise_u64(u64 seed);
f32 rng_noise_f32(u64 seed, f32 range_start, f32 range_end);
f32 rng_noise_f64(u64 seed, f64 range_start, f64 range_end);
u32 rng_true_u32(void);
u64 rng_true_u64(void);
f32 rng_true_f32(f32 range_start, f32 range_end);
f64 rng_true_f64(f64 range_start, f64 range_end);
#endif

View File

@ -381,20 +381,6 @@ INLINE b32 sim_ent_is_valid_and_active(struct sim_ent *ent)
return ent->valid && sim_ent_has_prop(ent, SIM_ENT_PROP_ACTIVE);
}
#if 0
INLINE b32 sim_ent_should_simulate(struct sim_ent *ent)
{
b32 res = false;
if (sim_ent_is_valid_and_active(ent)) {
res = true;
if (sim_ent_has_prop(ent, SIM_ENT_PROP_SYNC_DST) && !sim_ent_has_prop(ent, SIM_ENT_PROP_SYNC_PREDICT)) {
res = false;
}
}
return res;
}
#else
INLINE b32 sim_ent_should_predict(struct sim_ent *ent)
{
return sim_ent_id_eq(ent->predictor, ent->ss->local_client_ent);
@ -415,7 +401,6 @@ INLINE b32 sim_ent_should_simulate(struct sim_ent *ent)
return res;
}
#endif
/* ========================== *
* Ent functions
* ========================== */

View File

@ -51,14 +51,15 @@ void sim_accel_reset(struct sim_snapshot *ss, struct sim_accel *accel)
/* TODO: Remove this */
INTERNAL void spawn_test_entities(struct sim_snapshot *world, struct v2 offset)
INTERNAL void spawn_test_entities(struct sim_step_ctx *ctx, struct v2 offset)
{
struct sim_snapshot *world = ctx->world;
struct sim_ent *root = sim_ent_from_id(world, SIM_ENT_ROOT_ID);
root->mass_unscaled = F32_INFINITY;
root->inertia_unscaled = F32_INFINITY;
/* Enemy */
{
if (ctx->is_master) {
struct sim_ent *e = sim_ent_alloc_sync_src(root);
struct v2 pos = V2(1, -2);
@ -73,7 +74,7 @@ INTERNAL void spawn_test_entities(struct sim_snapshot *world, struct v2 offset)
e->layer = SIM_LAYER_SHOULDERS;
sim_ent_enable_prop(e, SIM_ENT_PROP_PHYSICAL_DYNAMIC);
e->mass_unscaled = 10;
e->mass_unscaled = 1;
e->inertia_unscaled = 10;
e->linear_ground_friction = 250;
e->angular_ground_friction = 200;
@ -127,8 +128,9 @@ INTERNAL void spawn_test_entities(struct sim_snapshot *world, struct v2 offset)
#endif
}
INTERNAL struct sim_ent *spawn_test_player(struct sim_snapshot *world)
INTERNAL struct sim_ent *spawn_test_player(struct sim_step_ctx *ctx)
{
struct sim_snapshot *world = ctx->world;
struct sim_ent *root = sim_ent_from_id(world, SIM_ENT_ROOT_ID);
/* Player */
@ -226,8 +228,9 @@ INTERNAL struct sim_ent *spawn_test_player_camera(struct sim_snapshot *world, st
return camera_ent;
}
INTERNAL void test_clear_level(struct sim_snapshot *world)
INTERNAL void test_clear_level(struct sim_step_ctx *ctx)
{
struct sim_snapshot *world = ctx->world;
for (u64 j = 0; j < world->num_ents_reserved; ++j) {
struct sim_ent *ent = &world->ents[j];
if (ent->valid) {
@ -296,7 +299,7 @@ INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, collision_data_array, st
/* Create test blood */
/* TODO: Remove this */
{
struct xform xf = XFORM_TRS(.t = point, .r = rng_rand_f32(0, TAU));
struct xform xf = XFORM_TRS(.t = point, .r = rng_true_f32(0, TAU));
struct sim_ent *decal = sim_ent_alloc_sync_src(root);
decal->sprite = sprite_tag_from_path(LIT("res/graphics/blood.ase"));
decal->sprite_tint = RGBA_32_F(1, 1, 1, 0.25f);
@ -305,10 +308,10 @@ INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, collision_data_array, st
f32 perp_range = 0.5;
struct v2 linear_velocity = v2_mul(normal, 0.5);
linear_velocity = v2_add(linear_velocity, v2_mul(v2_perp(normal), rng_rand_f32(-perp_range, perp_range)));
linear_velocity = v2_add(linear_velocity, v2_mul(v2_perp(normal), rng_true_f32(-perp_range, perp_range)));
f32 angular_velocity_range = 5;
f32 angular_velocity = rng_rand_f32(-angular_velocity_range, angular_velocity_range);
f32 angular_velocity = rng_true_f32(-angular_velocity_range, angular_velocity_range);
sim_ent_enable_prop(decal, SIM_ENT_PROP_PHYSICAL_KINEMATIC);
sim_ent_set_linear_velocity(decal, linear_velocity);
@ -509,7 +512,6 @@ void sim_step(struct sim_step_ctx *ctx)
}
}
/* Dereference hovered ent */
client_ent->client_hovered_ent = cmd_ent->cmd_hovered_ent;
u32 flags = control->flags;
@ -523,8 +525,8 @@ void sim_step(struct sim_step_ctx *ctx)
}
}
if (flags & SIM_CONTROL_FLAG_CLEAR_ALL) {
if (!(old_control.flags & SIM_CONTROL_FLAG_CLEAR_ALL)) {
test_clear_level(world);
if (is_master && !(old_control.flags & SIM_CONTROL_FLAG_CLEAR_ALL)) {
test_clear_level(ctx);
}
}
if (flags & SIM_CONTROL_FLAG_SPAWN_TEST) {
@ -533,7 +535,7 @@ void sim_step(struct sim_step_ctx *ctx)
u32 count = 1;
f32 spread = 1;
for (u32 j = 0; j < count; ++j) {
spawn_test_entities(world, V2(0, (((f32)j / (f32)count) - 0.5) * spread));
spawn_test_entities(ctx, V2(0, (((f32)j / (f32)count) - 0.5) * spread));
}
}
}
@ -554,7 +556,7 @@ void sim_step(struct sim_step_ctx *ctx)
/* FIXME: Ents never released when client disconnects */
struct sim_ent *control_ent = sim_ent_from_id(world, ent->client_control_ent);
if (!control_ent->valid) {
control_ent = spawn_test_player(world);
control_ent = spawn_test_player(ctx);
control_ent->predictor = ent->id;
sim_ent_enable_prop(control_ent, SIM_ENT_PROP_CONTROLLED);
ent->client_control_ent = control_ent->id;

View File

@ -442,7 +442,7 @@ struct string sys_get_clipboard_text(struct arena *arena);
* Util
* ========================== */
void sys_rand(struct string b);
void sys_true_rand(struct string b);
u32 sys_num_logical_processors(void);
void sys_exit(void);
void sys_panic(struct string msg);

View File

@ -1856,7 +1856,7 @@ struct string sys_get_clipboard_text(struct arena *arena)
* RNG
* ========================== */
void sys_rand(struct string b)
void sys_true_rand(struct string b)
{
BCryptGenRandom(BCRYPT_RNG_ALG_HANDLE, (PUCHAR)b.text, b.len, 0);
}

View File

@ -6,7 +6,7 @@
struct uid uid_rand(void)
{
struct uid res = ZI;
sys_rand(STRING_FROM_STRUCT(&res));
sys_true_rand(STRING_FROM_STRUCT(&res));
return res;
}

View File

@ -750,11 +750,10 @@ INTERNAL void user_update(void)
i64 frequency_ns = NS_FROM_SECONDS(0.01f);
f32 shake = ent->shake;
if (shake > 0) {
u64 basis = ent->id.uid.lo;
u64 angle_seed0 = basis + (u64)(G.ss_blended->sim_time_ns / frequency_ns);
u64 angle_seed0 = ent->id.uid.lo + (u64)(G.ss_blended->sim_time_ns / frequency_ns);
u64 angle_seed1 = angle_seed0 + 1;
f32 angle0 = rng_noise_f32(angle_seed0, 0, TAU);
f32 angle1 = rng_noise_f32(angle_seed1, 0, TAU);
f32 angle0 = ((f32)mix64(angle_seed0) / (f32)U64_MAX) * TAU;
f32 angle1 = ((f32)mix64(angle_seed1) / (f32)U64_MAX) * TAU;
struct v2 vec0 = v2_with_len(v2_from_angle(angle0), shake);
/* NOTE: vec1 not completely accurate since shake can change between frames, just a prediction */
@ -2303,11 +2302,11 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
i64 num_predict_ticks = math_round_to_int64(rtt_tick_ratio) + 2;
step_end_tick = step_base_tick + num_predict_ticks;
compute_timescale = 1.1;
} else if (cmds_ahead_on_master > 4) {
/* Slow down time to bring sim time closer to master time */
} else if (cmds_ahead_on_master > 3) {
/* Slow down simulation rate to bring sim time closer to master time */
compute_timescale = 0.9;
} else if (cmds_ahead_on_master < 2) {
/* Speed up time to give master more inputs to work with */
} else if (cmds_ahead_on_master < 1) {
/* Speed up simulation rate to give master more inputs to work with */
compute_timescale = 1.1;
} else {
compute_timescale = 1;

View File

@ -15,8 +15,6 @@
* Hash utils
* ========================== */
/* TODO: Replace with better hash functions */
/* FNV-1a parameters for different hash sizes:
* https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters
*/
@ -32,20 +30,22 @@ INLINE u64 hash_fnv64(u64 seed, struct string s)
return hash;
}
#if 0
#define HASH_FNV128_BASIS U128(0x6C62272E07BB0142, 0x62B821756295C58D)
INLINE u128 hash_fnv128(u128 seed, struct string s)
/* ========================== *
* Bit mixing
* ========================== */
/* Based on Jon Maiga's "mx3"
* (https://jonkagstrom.com/mx3/mx3_rev2.html)
*/
INLINE u64 mix64(u64 x)
{
/* FIXME: Verify MSVC version of 128 is same */
u128 hash = seed;
for (u64 i = 0; i < s.len; ++i) {
u8 c = (u8)s.text[i];
hash = u128_xor_u8(hash, c);
hash = u128_mul(hash, U128(0x1000000, 0x000000000000013B));
}
return hash;
x = (x ^ (x >> 32)) * 0xbea225f9eb34556d;
x = (x ^ (x >> 29)) * 0xbea225f9eb34556d;
x = (x ^ (x >> 32)) * 0xbea225f9eb34556d;
x = (x ^ (x >> 29));
return x;
}
#endif
/* ========================== *
* Merge sort
@ -119,7 +119,7 @@ INLINE void merge_sort(void *items, u64 item_count, u64 item_size, sort_compare_
/* ========================== *
* Fixed Dict
*
* Simple fixed bin-count string->value chaining dict for generic use
* Simple fixed-bin-count string -> pointer chaining hash table for generic use
* ========================== */
struct fixed_dict_entry {