From ff3daea78ce99491df0c245ea9fcff944bf3c9a9 Mon Sep 17 00:00:00 2001 From: maj00r Date: Mon, 14 Sep 2026 17:34:59 +0200 Subject: [PATCH] seed the random engines from the system pool, not std::random_device --- application/application.cpp | 17 ++++++++++++++--- application/editoruipanels.cpp | 3 +-- utilities/utilities.cpp | 29 +++++++++++++++++++++++++---- utilities/utilities.h | 11 +++++++++++ utilities/uuid.hpp | 3 ++- 5 files changed, 53 insertions(+), 10 deletions(-) diff --git a/application/application.cpp b/application/application.cpp index e5d0b6ba..49d493c2 100644 --- a/application/application.cpp +++ b/application/application.cpp @@ -321,7 +321,7 @@ int eu07_application::init(int Argc, char *Argv[]) } if (!Global.random_seed) - Global.random_seed = std::random_device{}(); + Global.random_seed = clock_seed(); Global.random_engine.seed(Global.random_seed); // configure the OS console according to Globals.ShowSystemConsole. @@ -1140,10 +1140,21 @@ int eu07_application::init_settings(int Argc, char *Argv[]) Global.local_start_vehicle = ToLower(Argv[++i]); } } + else if (token == "-seed") + { + // a number is the seed; anything else is a word that stands for one. said in the + // log as it was given, because that is what gets passed on to somebody else + if (i + 1 < Argc) + { + Global.random_seed = seed_of(Argv[++i]); + WriteLog("seed given on the command line: " + std::string(Argv[i]) + " -> " + std::to_string(Global.random_seed)); + } + } else { std::cout << "usage: " << std::string(Argv[0]) << " [-s sceneryfilepath]" - << " [-v vehiclename]" << std::endl; + << " [-v vehiclename]" + << " [-seed number|word]" << std::endl; return -1; } } @@ -1421,7 +1432,7 @@ int eu07_application::init_data() int eu07_application::init_modes() { - Global.local_random_engine.seed(std::random_device{}()); + Global.local_random_engine.seed(clock_seed()); if ((!Global.network_servers.empty() || Global.network_client) && Global.SceneryFile.empty()) { diff --git a/application/editoruipanels.cpp b/application/editoruipanels.cpp index f15f791e..cbfe0ae7 100644 --- a/application/editoruipanels.cpp +++ b/application/editoruipanels.cpp @@ -403,10 +403,9 @@ std::string *brush_object_list::GetRandomObject() if (Objects.empty()) return ∅ - static std::mt19937 rng{std::random_device{}()}; std::uniform_int_distribution dist(0, Objects.size() - 1); - return &Objects[dist(rng)]; + return &Objects[dist(Global.local_random_engine)]; } void brush_object_list::render() diff --git a/utilities/utilities.cpp b/utilities/utilities.cpp index 156d16cc..1f5f0682 100644 --- a/utilities/utilities.cpp +++ b/utilities/utilities.cpp @@ -15,6 +15,8 @@ Copyright (C) 2007-2014 Maciej Cierniak // //#include //#include +#include +#include #include //#ifndef WIN32 //#include @@ -30,7 +32,6 @@ Copyright (C) 2007-2014 Maciej Cierniak #include "utilities/U8.h" -//#include "utilities/Logs.h" // TODO: This shouldn't be in Globals? bool DebugModeFlag = false; @@ -137,15 +138,35 @@ int Random(int min, int max) return dist(Global.random_engine); } +std::uint32_t clock_seed() +{ + static std::atomic nth{0}; + return static_cast(std::time({})) + nth.fetch_add(1, std::memory_order_relaxed); +} + +std::uint32_t seed_of(std::string const &Text) +{ + std::uint32_t number{}; + if (std::from_chars(Text.data(), Text.data() + Text.size(), number).ec == std::errc{}) + { + return number; + } + std::uint32_t hash{}; + for (auto const character : Text) + { + hash = hash * 31u + static_cast(static_cast(character)); + } + return hash; +} + std::string generate_uuid_v4() { - std::random_device rd; - std::mt19937 gen(rd()); + static thread_local std::mt19937 generator{clock_seed()}; std::uniform_int_distribution dist(0, 255); std::array bytes; for (auto &b : bytes) - b = static_cast(dist(gen)); + b = static_cast(dist(generator)); // UUID v4 (RFC 4122) bytes[6] = bytes[6] & 0x0F | 0x40; diff --git a/utilities/utilities.h b/utilities/utilities.h index bc38b125..d013fade 100644 --- a/utilities/utilities.h +++ b/utilities/utilities.h @@ -62,6 +62,17 @@ inline long Round(double const f) double Random(double a, double b); int Random(int min, int max); + +// Seed for the random engines: the clock, plus a counter so two engines seeded in +// the same second do not come out the same. +std::uint32_t clock_seed(); + +// The seed a word stands for. A number is taken as it is written; anything else is +// hashed the way Java's String.hashCode does it, so the same word gives the same +// world on every platform and every build - which is the whole point of telling +// somebody a seed. +std::uint32_t seed_of(std::string const &Text); + std::string generate_uuid_v4(); double LocalRandom(double a, double b); diff --git a/utilities/uuid.hpp b/utilities/uuid.hpp index d94fd0cc..ff32f5d2 100644 --- a/utilities/uuid.hpp +++ b/utilities/uuid.hpp @@ -6,6 +6,7 @@ #include #include #include +#include "utilities/utilities.h" class UID { public: @@ -13,7 +14,7 @@ public: static UID random() { - static thread_local std::mt19937_64 gen(std::random_device{}()); + static thread_local std::mt19937_64 gen{clock_seed()}; UID u; uint64_t a = gen(); uint64_t b = gen();