From 360d8eade0332f2c1aa5c205ca772cd506c35b26 Mon Sep 17 00:00:00 2001 From: peterbell10 Date: Tue, 13 Jun 2017 20:35:30 +0100 Subject: FastRandom rewrite (#3754) --- src/BlockEntities/DropSpenserEntity.cpp | 2 +- src/BlockEntities/MobSpawnerEntity.cpp | 12 +- src/Blocks/BlockBigFlower.h | 3 +- src/Blocks/BlockCocoaPod.h | 4 +- src/Blocks/BlockCrops.h | 22 ++-- src/Blocks/BlockDeadBush.h | 11 +- src/Blocks/BlockDirt.h | 8 +- src/Blocks/BlockGlowstone.h | 4 +- src/Blocks/BlockGravel.h | 3 +- src/Blocks/BlockHandler.cpp | 6 +- src/Blocks/BlockLeaves.h | 12 +- src/Blocks/BlockMelon.h | 3 +- src/Blocks/BlockMobHead.h | 6 +- src/Blocks/BlockMobSpawner.h | 4 +- src/Blocks/BlockNetherWart.h | 4 +- src/Blocks/BlockOre.h | 16 +-- src/Blocks/BlockPlant.h | 3 +- src/Blocks/BlockPortal.h | 3 +- src/Blocks/BlockSapling.h | 6 +- src/Blocks/BlockSeaLantern.h | 3 +- src/Blocks/BlockTallGrass.h | 9 +- src/Chunk.cpp | 31 +++-- src/Chunk.h | 3 +- src/ChunkMap.cpp | 11 +- src/ChunkMap.h | 3 +- src/Enchantments.cpp | 4 +- src/Entities/Entity.cpp | 10 +- src/Entities/ExpBottleEntity.cpp | 2 +- src/Entities/Floater.cpp | 10 +- src/Entities/Player.cpp | 13 +-- src/Entities/ThrownEggEntity.cpp | 5 +- src/FastRandom.cpp | 119 +++++-------------- src/FastRandom.h | 200 ++++++++++++++++++++++++++------ src/Generating/ChunkGenerator.cpp | 3 +- src/Generating/DungeonRoomsFinisher.cpp | 4 +- src/Item.cpp | 26 ++--- src/Items/ItemFishingRod.h | 16 +-- src/Items/ItemHandler.cpp | 3 +- src/Items/ItemThrowable.h | 3 +- src/Map.cpp | 4 +- src/MobSpawner.cpp | 16 +-- src/MobSpawner.h | 2 - src/Mobs/Chicken.cpp | 2 +- src/Mobs/Horse.cpp | 8 +- src/Mobs/Monster.cpp | 62 +++++----- src/Mobs/PassiveMonster.cpp | 3 +- src/Mobs/Path.cpp | 2 +- src/Mobs/Path.h | 1 - src/Mobs/Rabbit.cpp | 4 +- src/Mobs/Sheep.cpp | 12 +- src/Mobs/Skeleton.cpp | 6 +- src/Mobs/Slime.cpp | 6 +- src/Mobs/Villager.cpp | 6 +- src/Mobs/Witch.cpp | 6 +- src/Mobs/Wolf.cpp | 4 +- src/ProbabDistrib.cpp | 3 +- src/ProbabDistrib.h | 3 +- src/Server.cpp | 7 +- src/Simulator/FireSimulator.cpp | 6 +- src/UI/SlotArea.cpp | 7 +- src/World.cpp | 64 +++++----- src/World.h | 8 +- tests/FastRandom/FastRandomTest.cpp | 14 +-- 63 files changed, 458 insertions(+), 408 deletions(-) diff --git a/src/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp index e2e40148b..032c5cf12 100644 --- a/src/BlockEntities/DropSpenserEntity.cpp +++ b/src/BlockEntities/DropSpenserEntity.cpp @@ -174,7 +174,7 @@ void cDropSpenserEntity::DropFromSlot(cChunk & a_Chunk, int a_SlotNum) cItems Pickups; Pickups.push_back(m_Contents.RemoveOneItem(a_SlotNum)); - const int PickupSpeed = m_World->GetTickRandomNumber(4) + 2; // At least 2, at most 6 + const int PickupSpeed = GetRandomProvider().RandInt(2, 6); // At least 2, at most 6 int PickupSpeedX = 0, PickupSpeedY = 0, PickupSpeedZ = 0; switch (Meta & E_META_DROPSPENSER_FACING_MASK) { diff --git a/src/BlockEntities/MobSpawnerEntity.cpp b/src/BlockEntities/MobSpawnerEntity.cpp index 47bf85e9b..3bb04682a 100644 --- a/src/BlockEntities/MobSpawnerEntity.cpp +++ b/src/BlockEntities/MobSpawnerEntity.cpp @@ -107,7 +107,7 @@ bool cMobSpawnerEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cMobSpawnerEntity::ResetTimer(void) { - m_SpawnDelay = static_cast(200 + m_World->GetTickRandomNumber(600)); + m_SpawnDelay = GetRandomProvider().RandInt(200, 800); m_World->BroadcastBlockEntity(m_PosX, m_PosY, m_PosZ); } @@ -138,7 +138,7 @@ void cMobSpawnerEntity::SpawnEntity(void) virtual bool Item(cChunk * a_Chunk) { - cFastRandom Random; + auto & Random = GetRandomProvider(); bool EntitiesSpawned = false; for (size_t i = 0; i < 4; i++) @@ -148,9 +148,9 @@ void cMobSpawnerEntity::SpawnEntity(void) break; } - int RelX = static_cast(m_RelX + static_cast(Random.NextFloat() - Random.NextFloat()) * 4.0); - int RelY = m_RelY + Random.NextInt(3) - 1; - int RelZ = static_cast(m_RelZ + static_cast(Random.NextFloat() - Random.NextFloat()) * 4.0); + int RelX = m_RelX + static_cast((Random.RandReal() - Random.RandReal()) * 4.0); + int RelY = m_RelY + Random.RandInt(-1, 1); + int RelZ = m_RelZ + static_cast((Random.RandReal() - Random.RandReal()) * 4.0); cChunk * Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(RelX, RelZ); if ((Chunk == nullptr) || !Chunk->IsValid()) @@ -171,7 +171,7 @@ void cMobSpawnerEntity::SpawnEntity(void) } Monster->SetPosition(PosX, RelY, PosZ); - Monster->SetYaw(Random.NextFloat() * 360.0f); + Monster->SetYaw(Random.RandReal(360.0f)); if (Chunk->GetWorld()->SpawnMobFinalize(Monster) != cEntity::INVALID_ID) { EntitiesSpawned = true; diff --git a/src/Blocks/BlockBigFlower.h b/src/Blocks/BlockBigFlower.h index fe7f47b71..8ff07fdcd 100644 --- a/src/Blocks/BlockBigFlower.h +++ b/src/Blocks/BlockBigFlower.h @@ -67,8 +67,7 @@ public: ) ) { - MTRand r1; - if (r1.randInt(10) == 5) + if (GetRandomProvider().RandBool(0.10)) { cItems Pickups; if (FlowerMeta == E_META_BIG_FLOWER_DOUBLE_TALL_GRASS) diff --git a/src/Blocks/BlockCocoaPod.h b/src/Blocks/BlockCocoaPod.h index a48bfdfc2..035cc2f4f 100644 --- a/src/Blocks/BlockCocoaPod.h +++ b/src/Blocks/BlockCocoaPod.h @@ -30,9 +30,7 @@ public: virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override { - cFastRandom Random; - - if (Random.NextInt(5) == 0) + if (GetRandomProvider().RandBool(0.20)) { NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); NIBBLETYPE TypeMeta = Meta & 0x03; diff --git a/src/Blocks/BlockCrops.h b/src/Blocks/BlockCrops.h index 5ca264774..378505430 100644 --- a/src/Blocks/BlockCrops.h +++ b/src/Blocks/BlockCrops.h @@ -26,7 +26,7 @@ public: virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override { - cFastRandom rand; + auto & rand = GetRandomProvider(); // If not fully grown, drop the "seed" of whatever is growing: if (a_Meta < RipeMeta) @@ -51,30 +51,30 @@ public: { case E_BLOCK_BEETROOTS: { - char SeedCount = static_cast(1 + (rand.NextInt(3) + rand.NextInt(3)) / 2); // [1 .. 3] with high preference of 2 - a_Pickups.push_back(cItem(E_ITEM_BEETROOT_SEEDS, SeedCount, 0)); - char BeetrootCount = static_cast(1 + (rand.NextInt(3) + rand.NextInt(3)) / 2); // [1 .. 3] with high preference of 2 - a_Pickups.push_back(cItem(E_ITEM_BEETROOT, BeetrootCount, 0)); + char SeedCount = 1 + ((rand.RandInt(2) + rand.RandInt(2)) / 2); // [1 .. 3] with high preference of 2 + a_Pickups.emplace_back(E_ITEM_BEETROOT_SEEDS, SeedCount, 0); + char BeetrootCount = 1 + ((rand.RandInt(2) + rand.RandInt(2)) / 2); // [1 .. 3] with high preference of 2 + a_Pickups.emplace_back(E_ITEM_BEETROOT, BeetrootCount, 0); break; } case E_BLOCK_CROPS: { - a_Pickups.push_back(cItem(E_ITEM_WHEAT, 1, 0)); - a_Pickups.push_back(cItem(E_ITEM_SEEDS, static_cast(1 + (rand.NextInt(3) + rand.NextInt(3)) / 2), 0)); // [1 .. 3] with high preference of 2 + a_Pickups.emplace_back(E_ITEM_WHEAT, 1, 0); + a_Pickups.emplace_back(E_ITEM_SEEDS, 1 + ((rand.RandInt(2) + rand.RandInt(2)) / 2), 0); // [1 .. 3] with high preference of 2 break; } case E_BLOCK_CARROTS: { - a_Pickups.push_back(cItem(E_ITEM_CARROT, static_cast(1 + (rand.NextInt(3) + rand.NextInt(3)) / 2), 0)); // [1 .. 3] with high preference of 2 + a_Pickups.emplace_back(E_ITEM_CARROT, 1 + ((rand.RandInt(2) + rand.RandInt(2)) / 2), 0); // [1 .. 3] with high preference of 2 break; } case E_BLOCK_POTATOES: { - a_Pickups.push_back(cItem(E_ITEM_POTATO, static_cast(1 + (rand.NextInt(3) + rand.NextInt(3)) / 2), 0)); // [1 .. 3] with high preference of 2 - if (rand.NextInt(21) == 0) + a_Pickups.emplace_back(E_ITEM_POTATO, 1 + ((rand.RandInt(2) + rand.RandInt(2)) / 2), 0); // [1 .. 3] with high preference of 2 + if (rand.RandBool(0.05)) { // With a 5% chance, drop a poisonous potato as well - a_Pickups.push_back(cItem(E_ITEM_POISONOUS_POTATO, 1, 0)); + a_Pickups.emplace_back(E_ITEM_POISONOUS_POTATO, 1, 0); } break; } diff --git a/src/Blocks/BlockDeadBush.h b/src/Blocks/BlockDeadBush.h index 0e81d6c2f..f9ce9a9ff 100644 --- a/src/Blocks/BlockDeadBush.h +++ b/src/Blocks/BlockDeadBush.h @@ -46,11 +46,10 @@ public: virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override { // Drop 0-3 sticks - cFastRandom random; - int chance = random.NextInt(3); + char chance = GetRandomProvider().RandInt(3); if (chance != 0) { - a_Pickups.push_back(cItem(E_ITEM_STICK, static_cast(chance), 0)); + a_Pickups.emplace_back(E_ITEM_STICK, chance, 0); } } @@ -74,7 +73,7 @@ public: // Spawn the pickups: if (!Drops.empty()) { - MTRand r1; + auto & r1 = GetRandomProvider(); // Mid-block position first double MicroX, MicroY, MicroZ; @@ -83,8 +82,8 @@ public: MicroZ = a_BlockZ + 0.5; // Add random offset second - MicroX += r1.rand(1) - 0.5; - MicroZ += r1.rand(1) - 0.5; + MicroX += r1.RandReal(-0.5, 0.5); + MicroZ += r1.RandReal(-0.5, 0.5); a_WorldInterface.SpawnItemPickups(Drops, MicroX, MicroY, MicroZ); } diff --git a/src/Blocks/BlockDirt.h b/src/Blocks/BlockDirt.h index 33325d53a..3712e22f7 100644 --- a/src/Blocks/BlockDirt.h +++ b/src/Blocks/BlockDirt.h @@ -70,12 +70,12 @@ public: } // Grass spreads to adjacent dirt blocks: - cFastRandom rand; + auto & rand = GetRandomProvider(); for (int i = 0; i < 2; i++) // Pick two blocks to grow to { - int OfsX = rand.NextInt(3) - 1; // [-1 .. 1] - int OfsY = rand.NextInt(5) - 3; // [-3 .. 1] - int OfsZ = rand.NextInt(3) - 1; // [-1 .. 1] + int OfsX = rand.RandInt(-1, 1); + int OfsY = rand.RandInt(-3, 1); + int OfsZ = rand.RandInt(-1, 1); BLOCKTYPE DestBlock; NIBBLETYPE DestMeta; diff --git a/src/Blocks/BlockGlowstone.h b/src/Blocks/BlockGlowstone.h index cb36b9a73..4b0d72a93 100644 --- a/src/Blocks/BlockGlowstone.h +++ b/src/Blocks/BlockGlowstone.h @@ -18,10 +18,8 @@ public: virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override { - cFastRandom Random; - // Add more than one dust - a_Pickups.push_back(cItem(E_ITEM_GLOWSTONE_DUST, static_cast(2 + Random.NextInt(3)), 0)); + a_Pickups.emplace_back(E_ITEM_GLOWSTONE_DUST, GetRandomProvider().RandInt(2, 4), 0); } virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override diff --git a/src/Blocks/BlockGravel.h b/src/Blocks/BlockGravel.h index 7bd68a050..6229d1d75 100644 --- a/src/Blocks/BlockGravel.h +++ b/src/Blocks/BlockGravel.h @@ -18,8 +18,7 @@ public: virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override { - cFastRandom Random; - if (Random.NextInt(10) == 0) + if (GetRandomProvider().RandBool(0.10)) { a_Pickups.Add(E_ITEM_FLINT, 1, 0); } diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp index faed798ff..36b20088c 100644 --- a/src/Blocks/BlockHandler.cpp +++ b/src/Blocks/BlockHandler.cpp @@ -526,7 +526,7 @@ void cBlockHandler::DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterfac if (!Pickups.empty()) { - MTRand r1; + auto & r1 = GetRandomProvider(); // Mid-block position first double MicroX, MicroY, MicroZ; @@ -535,8 +535,8 @@ void cBlockHandler::DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterfac MicroZ = a_BlockZ + 0.5; // Add random offset second - MicroX += r1.rand(1) - 0.5; - MicroZ += r1.rand(1) - 0.5; + MicroX += r1.RandReal(-0.5, 0.5); + MicroZ += r1.RandReal(-0.5, 0.5); a_WorldInterface.SpawnItemPickups(Pickups, MicroX, MicroY, MicroZ); } diff --git a/src/Blocks/BlockLeaves.h b/src/Blocks/BlockLeaves.h index 73c93116e..1f25ac49e 100644 --- a/src/Blocks/BlockLeaves.h +++ b/src/Blocks/BlockLeaves.h @@ -37,22 +37,22 @@ public: virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override { - cFastRandom rand; + auto & rand = GetRandomProvider(); // There is a chance to drop a sapling that varies depending on the type of leaf broken. // TODO: Take into account fortune for sapling drops. - int chance; + double chance = 0.0; if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_JUNGLE)) { // Jungle leaves have a 2.5% chance of dropping a sapling. - chance = rand.NextInt(40); + chance = 0.025; } else { // Other leaves have a 5% chance of dropping a sapling. - chance = rand.NextInt(20); + chance = 0.05; } - if (chance == 0) + if (rand.RandBool(chance)) { a_Pickups.push_back( cItem( @@ -66,7 +66,7 @@ public: // 0.5 % chance of dropping an apple, if the leaves' type is Apple Leaves if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_APPLE)) { - if (rand.NextInt(200) == 0) + if (rand.RandBool(0.005)) { a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0)); } diff --git a/src/Blocks/BlockMelon.h b/src/Blocks/BlockMelon.h index a9723dcec..baf3053e1 100644 --- a/src/Blocks/BlockMelon.h +++ b/src/Blocks/BlockMelon.h @@ -18,8 +18,7 @@ public: virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override { - cFastRandom Random; - a_Pickups.push_back(cItem(E_ITEM_MELON_SLICE, static_cast(3 + Random.NextInt(5)), 0)); + a_Pickups.emplace_back(E_ITEM_MELON_SLICE, GetRandomProvider().RandInt(3, 7), 0); } virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override diff --git a/src/Blocks/BlockMobHead.h b/src/Blocks/BlockMobHead.h index a271c3b43..930a1a921 100644 --- a/src/Blocks/BlockMobHead.h +++ b/src/Blocks/BlockMobHead.h @@ -42,7 +42,7 @@ public: cItems Pickups; Pickups.Add(E_ITEM_HEAD, 1, static_cast(MobHeadEntity->GetType())); - MTRand r1; + auto & r1 = GetRandomProvider(); // Mid-block position first double MicroX, MicroY, MicroZ; @@ -51,8 +51,8 @@ public: MicroZ = MobHeadEntity->GetPosZ() + 0.5; // Add random offset second - MicroX += r1.rand(1) - 0.5; - MicroZ += r1.rand(1) - 0.5; + MicroX += r1.RandReal(-0.5, 0.5); + MicroZ += r1.RandReal(-0.5, 0.5); MobHeadEntity->GetWorld()->SpawnItemPickups(Pickups, MicroX, MicroY, MicroZ); return false; diff --git a/src/Blocks/BlockMobSpawner.h b/src/Blocks/BlockMobSpawner.h index a6205490f..b6493c5b3 100644 --- a/src/Blocks/BlockMobSpawner.h +++ b/src/Blocks/BlockMobSpawner.h @@ -45,8 +45,8 @@ public: return; } - cFastRandom Random; - int Reward = 15 + Random.NextInt(15) + Random.NextInt(15); + auto & Random = GetRandomProvider(); + int Reward = 15 + Random.RandInt(14) + Random.RandInt(14); a_WorldInterface.SpawnExperienceOrb(static_cast(a_BlockX), static_cast(a_BlockY + 1), static_cast(a_BlockZ), Reward); } } ; diff --git a/src/Blocks/BlockNetherWart.h b/src/Blocks/BlockNetherWart.h index 0e3497688..aa7144458 100644 --- a/src/Blocks/BlockNetherWart.h +++ b/src/Blocks/BlockNetherWart.h @@ -21,12 +21,12 @@ public: virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override { - cFastRandom rand; + auto & rand = GetRandomProvider(); if (a_Meta == 0x3) { // Fully grown, drop the entire produce: - a_Pickups.push_back(cItem(E_ITEM_NETHER_WART, static_cast(1 + (rand.NextInt(3) + rand.NextInt(3))) / 2, 0)); + a_Pickups.emplace_back(E_ITEM_NETHER_WART, 1 + (rand.RandInt(2) + rand.RandInt(2)) / 2, 0); } else { diff --git a/src/Blocks/BlockOre.h b/src/Blocks/BlockOre.h index 70d599843..202672cb5 100644 --- a/src/Blocks/BlockOre.h +++ b/src/Blocks/BlockOre.h @@ -20,19 +20,19 @@ public: virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override { - cFastRandom Random; + auto & Random = GetRandomProvider(); switch (m_BlockType) { case E_BLOCK_LAPIS_ORE: { - a_Pickups.push_back(cItem(E_ITEM_DYE, static_cast(4 + Random.NextInt(5)), 4)); + a_Pickups.emplace_back(E_ITEM_DYE, Random.RandInt(4, 8), 4); break; } case E_BLOCK_REDSTONE_ORE: case E_BLOCK_REDSTONE_ORE_GLOWING: { - a_Pickups.push_back(cItem(E_ITEM_REDSTONE_DUST, static_cast(4 + Random.NextInt(2)), 0)); + a_Pickups.emplace_back(E_ITEM_REDSTONE_DUST, Random.RandInt(4, 5), 0); break; } case E_BLOCK_DIAMOND_ORE: @@ -84,7 +84,7 @@ public: return; } - cFastRandom Random; + auto & Random = GetRandomProvider(); int Reward = 0; switch (m_BlockType) @@ -93,27 +93,27 @@ public: case E_BLOCK_LAPIS_ORE: { // Lapis and nether quartz get 2 - 5 experience - Reward = Random.NextInt(4) + 2; + Reward = Random.RandInt(2, 5); break; } case E_BLOCK_REDSTONE_ORE: case E_BLOCK_REDSTONE_ORE_GLOWING: { // Redstone gets 1 - 5 experience - Reward = Random.NextInt(5) + 1; + Reward = Random.RandInt(1, 5); break; } case E_BLOCK_DIAMOND_ORE: case E_BLOCK_EMERALD_ORE: { // Diamond and emerald get 3 - 7 experience - Reward = Random.NextInt(5) + 3; + Reward = Random.RandInt(3, 7); break; } case E_BLOCK_COAL_ORE: { // Coal gets 0 - 2 experience - Reward = Random.NextInt(3); + Reward = Random.RandInt(2); break; } diff --git a/src/Blocks/BlockPlant.h b/src/Blocks/BlockPlant.h index 0155dc466..d7c5d9f83 100644 --- a/src/Blocks/BlockPlant.h +++ b/src/Blocks/BlockPlant.h @@ -75,10 +75,9 @@ protected: */ virtual PlantAction CanGrow(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) { - cFastRandom rand; // Plant can grow if it has the required amount of light, and it passes a random chance based on surrounding blocks PlantAction Action = HasEnoughLight(a_Chunk, a_RelX, a_RelY, a_RelZ); - if ((Action == paGrowth) && (rand.NextInt(GetGrowthChance(a_Chunk, a_RelX, a_RelY, a_RelZ)) != 0)) + if ((Action == paGrowth) && !GetRandomProvider().RandBool(1.0 / GetGrowthChance(a_Chunk, a_RelX, a_RelY, a_RelZ))) { Action = paStay; } diff --git a/src/Blocks/BlockPortal.h b/src/Blocks/BlockPortal.h index 504c018fb..6b5c5c0e0 100644 --- a/src/Blocks/BlockPortal.h +++ b/src/Blocks/BlockPortal.h @@ -40,8 +40,7 @@ public: virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override { - cFastRandom Random; - if (Random.NextInt(2000) != 0) + if (GetRandomProvider().RandBool(0.9995)) { return; } diff --git a/src/Blocks/BlockSapling.h b/src/Blocks/BlockSapling.h index b7d2f163b..a356eda8f 100644 --- a/src/Blocks/BlockSapling.h +++ b/src/Blocks/BlockSapling.h @@ -37,16 +37,16 @@ public: // Only grow if we have the right amount of light if (Light > 8) { - cFastRandom random; + auto & random = GetRandomProvider(); // Only grow if we are in the right growth stage and have the right amount of space around them. - if (((Meta & 0x08) != 0) && (random.NextInt(99) < 45) && CanGrowAt(a_Chunk, a_RelX, a_RelY, a_RelZ, Meta)) + if (((Meta & 0x08) != 0) && random.RandBool(0.45) && CanGrowAt(a_Chunk, a_RelX, a_RelY, a_RelZ, Meta)) { int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; a_Chunk.GetWorld()->GrowTree(BlockX, a_RelY, BlockZ); } // Only move to the next growth stage if we haven't gone there yet - else if (((Meta & 0x08) == 0) && (random.NextInt(99) < 45)) + else if (((Meta & 0x08) == 0) && random.RandBool(0.45)) { a_Chunk.SetMeta(a_RelX, a_RelY, a_RelZ, Meta | 0x08); } diff --git a/src/Blocks/BlockSeaLantern.h b/src/Blocks/BlockSeaLantern.h index 3454259b5..691e7de10 100644 --- a/src/Blocks/BlockSeaLantern.h +++ b/src/Blocks/BlockSeaLantern.h @@ -20,8 +20,7 @@ public: virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override { // Reset meta to 0 - cFastRandom Random; - a_Pickups.push_back(cItem(E_ITEM_PRISMARINE_CRYSTALS, static_cast(2 + Random.NextInt(2)), 0)); + a_Pickups.emplace_back(E_ITEM_PRISMARINE_CRYSTALS, GetRandomProvider().RandInt(2, 3), 0); } } ; diff --git a/src/Blocks/BlockTallGrass.h b/src/Blocks/BlockTallGrass.h index 746c67617..fb65bca65 100644 --- a/src/Blocks/BlockTallGrass.h +++ b/src/Blocks/BlockTallGrass.h @@ -26,8 +26,7 @@ public: virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override { // Drop seeds, sometimes - cFastRandom Random; - if (Random.NextInt(8) == 0) + if (GetRandomProvider().RandBool(0.125)) { a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0)); } @@ -47,7 +46,7 @@ public: // Spawn the pickups: if (!Drops.empty()) { - MTRand r1; + auto & r1 = GetRandomProvider(); // Mid-block position first double MicroX, MicroY, MicroZ; @@ -56,8 +55,8 @@ public: MicroZ = a_BlockZ + 0.5; // Add random offset second - MicroX += r1.rand(1) - 0.5; - MicroZ += r1.rand(1) - 0.5; + MicroX += r1.RandReal(-0.5, 0.5); + MicroZ += r1.RandReal(-0.5, 0.5); a_WorldInterface.SpawnItemPickups(Drops, MicroX, MicroY, MicroZ); } diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 9516559ce..a5818fbeb 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -507,14 +507,19 @@ void cChunk::CollectMobCensus(cMobCensus & toFill) void cChunk::GetThreeRandomNumbers(int & a_X, int & a_Y, int & a_Z, int a_MaxX, int a_MaxY, int a_MaxZ) { - ASSERT(a_MaxX * a_MaxY * a_MaxZ * 8 < 0x00ffffff); - int Random = m_World->GetTickRandomNumber(0x00ffffff); - a_X = Random % (a_MaxX * 2); - a_Y = (Random / (a_MaxX * 2)) % (a_MaxY * 2); - a_Z = ((Random / (a_MaxX * 2)) / (a_MaxY * 2)) % (a_MaxZ * 2); - a_X /= 2; - a_Y /= 2; - a_Z /= 2; + ASSERT( + (a_MaxX > 0) && (a_MaxY > 0) && (a_MaxZ > 0) && + (a_MaxX <= std::numeric_limits::max() / a_MaxY) && // a_MaxX * a_MaxY doesn't overflow + (a_MaxX * a_MaxY <= std::numeric_limits::max() / a_MaxZ) // a_MaxX * a_MaxY * a_MaxZ doesn't overflow + ); + + // MTRand gives an inclusive range [0, Max] but this gives the exclusive range [0, Max) + int OverallMax = (a_MaxX - 1) * (a_MaxY - 1) * (a_MaxZ - 1); + int Random = m_World->GetTickRandomNumber(OverallMax); + + a_X = Random % a_MaxX; + a_Y = (Random / a_MaxX) % a_MaxY; + a_Z = ((Random / a_MaxX) / a_MaxY) % a_MaxZ; } @@ -848,7 +853,7 @@ void cChunk::TickBlocks(void) void cChunk::ApplyWeatherToTop() { if ( - (m_World->GetTickRandomNumber(100) != 0) || + (GetRandomProvider().RandBool(0.99)) || ( (m_World->GetWeather() != eWeather_Rain) && (m_World->GetWeather() != eWeather_ThunderStorm) @@ -932,8 +937,10 @@ void cChunk::ApplyWeatherToTop() -bool cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_TickRandom) +bool cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType) { + auto & Random = GetRandomProvider(); + // Convert the stem BlockType into produce BlockType BLOCKTYPE ProduceType; switch (a_BlockType) @@ -969,7 +976,7 @@ bool cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Bl // Pick a direction in which to place the produce: int x = 0, z = 0; - int CheckType = a_TickRandom.randInt(3); // The index to the neighbors array which should be checked for emptiness + int CheckType = Random.RandInt(3); // The index to the neighbors array which should be checked for emptiness switch (CheckType) { case 0: x = 1; break; @@ -1001,7 +1008,7 @@ bool cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Bl case E_BLOCK_FARMLAND: { // Place a randomly-facing produce: - NIBBLETYPE Meta = (ProduceType == E_BLOCK_MELON) ? 0 : static_cast(a_TickRandom.randInt(4) % 4); + NIBBLETYPE Meta = (ProduceType == E_BLOCK_MELON) ? 0 : static_cast(Random.RandInt(4) % 4); LOGD("Growing melon / pumpkin at {%d, %d, %d} (<%d, %d> from stem), overwriting %s, growing on top of %s, meta %d", a_RelX + x + m_PosX * cChunkDef::Width, a_RelY, a_RelZ + z + m_PosZ * cChunkDef::Width, x, z, diff --git a/src/Chunk.h b/src/Chunk.h index e5fb162be..3a0d37768 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -27,7 +27,6 @@ namespace Json class cWorld; class cClientHandle; class cServer; -class MTRand; class cPlayer; class cChunkMap; class cBeaconEntity; @@ -609,7 +608,7 @@ private: bool GrowTallGrass (int a_RelX, int a_RelY, int a_RelZ); /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem); returns true if the pumpkin or melon sucessfully grew */ - bool GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_Random); + bool GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType); /** Called by Tick() when an entity moves out of this chunk into a neighbor; moves the entity and sends spawn / despawn packet to clients */ void MoveEntityToNewChunk(cEntity * a_Entity); diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 60fcfdef0..019746b9d 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -1735,7 +1735,7 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_ case E_BLOCK_TNT: { // Activate the TNT, with a random fuse between 10 to 30 game ticks - int FuseTime = 10 + m_World->GetTickRandomNumber(20); + int FuseTime = GetRandomProvider().RandInt(10, 30); m_World->SpawnPrimedTNT(a_BlockX + x + 0.5, a_BlockY + y + 0.5, a_BlockZ + z + 0.5, FuseTime); area.SetBlockTypeMeta(bx + x, by + y, bz + z, E_BLOCK_AIR, 0); a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z)); @@ -1775,7 +1775,8 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_ default: { - if (m_World->GetTickRandomNumber(100) <= 25) // 25% chance of pickups + auto & Random = GetRandomProvider(); + if (Random.RandBool(0.25)) // 25% chance of pickups { cItems Drops; cBlockHandler * Handler = BlockHandler(Block); @@ -1783,7 +1784,7 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_ Handler->ConvertToPickups(Drops, area.GetBlockMeta(bx + x, by + y, bz + z)); // Stone becomes cobblestone, coal ore becomes coal, etc. m_World->SpawnItemPickups(Drops, bx + x, by + y, bz + z); } - else if ((m_World->GetTNTShrapnelLevel() > slNone) && (m_World->GetTickRandomNumber(100) < 20)) // 20% chance of flinging stuff around + else if ((m_World->GetTNTShrapnelLevel() > slNone) && Random.RandBool(0.20)) // 20% chance of flinging stuff around { // If the block is shrapnel-able, make a falling block entity out of it: if ( @@ -2516,7 +2517,7 @@ void cChunkMap::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) -bool cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand) +bool cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) { int ChunkX, ChunkZ; cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); @@ -2525,7 +2526,7 @@ bool cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCK cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if (Chunk != nullptr) { - return Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Rand); + return Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType); } return false; } diff --git a/src/ChunkMap.h b/src/ChunkMap.h index f1631f91b..e7694e907 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -15,7 +15,6 @@ class cWorld; class cWorldInterface; class cItem; -class MTRand; class cChunkStay; class cChunk; class cPlayer; @@ -362,7 +361,7 @@ public: void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty); /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem); returns true if the pumpkin or melon sucessfully grew */ - bool GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand); + bool GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); /** Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config; returns the amount of blocks the sugarcane grew inside this call */ int GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); diff --git a/src/Enchantments.cpp b/src/Enchantments.cpp index a8300aabb..a18f6d68a 100644 --- a/src/Enchantments.cpp +++ b/src/Enchantments.cpp @@ -1012,14 +1012,12 @@ void cEnchantments::CheckEnchantmentConflictsFromVector(cWeightedEnchantments & cEnchantments cEnchantments::GetRandomEnchantmentFromVector(cWeightedEnchantments & a_Enchantments) { - cFastRandom Random; - int AllWeights = 0; for (cWeightedEnchantments::iterator it = a_Enchantments.begin(); it != a_Enchantments.end(); ++it) { AllWeights += (*it).m_Weight; } - int RandomNumber = Random.GenerateRandomInteger(0, AllWeights - 1); + int RandomNumber = GetRandomProvider().RandInt(AllWeights - 1); for (cWeightedEnchantments::iterator it = a_Enchantments.begin(); it != a_Enchantments.end(); ++it) { RandomNumber -= (*it).m_Weight; diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index db70044b4..5d26f501b 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -495,12 +495,11 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) { int Chance = static_cast(ThornsLevel * 15); - cFastRandom Random; - int RandomValue = Random.GenerateRandomInteger(0, 100); + auto & Random = GetRandomProvider(); - if (RandomValue <= Chance) + if (Random.RandBool(Chance / 100.0)) { - a_TDI.Attacker->TakeDamage(dtAttack, this, 0, Random.GenerateRandomInteger(1, 4), 0); + a_TDI.Attacker->TakeDamage(dtAttack, this, 0, Random.RandInt(1, 4), 0); } } @@ -574,8 +573,7 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) TotalEPF = 25; } - cFastRandom Random; - float RandomValue = Random.GenerateRandomInteger(50, 100) * 0.01f; + float RandomValue = GetRandomProvider().RandReal(0.5f, 1.0f); TotalEPF = ceil(TotalEPF * RandomValue); diff --git a/src/Entities/ExpBottleEntity.cpp b/src/Entities/ExpBottleEntity.cpp index b76df923a..4072b939b 100644 --- a/src/Entities/ExpBottleEntity.cpp +++ b/src/Entities/ExpBottleEntity.cpp @@ -40,6 +40,6 @@ void cExpBottleEntity::Break(const Vector3d &a_HitPos) { // Spawn an experience orb with a reward between 3 and 11. m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SPLASH_POTION, POSX_TOINT, POSY_TOINT, POSZ_TOINT, 0); - m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), 3 + m_World->GetTickRandomNumber(8)); + m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), GetRandomProvider().RandInt(3, 11)); Destroy(); } diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp index 8f98cb36c..eeaa6cf3d 100644 --- a/src/Entities/Floater.cpp +++ b/src/Entities/Floater.cpp @@ -128,6 +128,8 @@ void cFloater::SpawnOn(cClientHandle & a_Client) void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { + auto & Random = GetRandomProvider(); + HandlePhysics(a_Dt, a_Chunk); if (IsBlockWater(m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT)) && (m_World->GetBlockMeta(POSX_TOINT, POSY_TOINT, POSX_TOINT) == 0)) @@ -141,13 +143,13 @@ void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) SetPosY(GetPosY() - 1); m_CanPickupItem = true; m_PickupCountDown = 20; - m_CountDownTime = 100 + m_World->GetTickRandomNumber(800); + m_CountDownTime = Random.RandInt(100, 900); LOGD("Floater %i can be picked up", GetUniqueID()); } else if (m_CountDownTime == 20) // Calculate the position where the particles should spawn and start producing them. { LOGD("Started producing particles for floater %i", GetUniqueID()); - m_ParticlePos.Set(GetPosX() + (-4 + m_World->GetTickRandomNumber(8)), GetPosY(), GetPosZ() + (-4 + m_World->GetTickRandomNumber(8))); + m_ParticlePos.Set(GetPosX() + Random.RandInt(-4, 4), GetPosY(), GetPosZ() + Random.RandInt(-4, 4)); m_World->GetBroadcaster().BroadcastParticleEffect("splash", static_cast(m_ParticlePos), Vector3f{}, 0, 15); } else if (m_CountDownTime < 20) @@ -159,14 +161,14 @@ void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) m_CountDownTime--; if (m_World->GetHeight(POSX_TOINT, POSZ_TOINT) == POSY_TOINT) { - if (m_World->IsWeatherWet() && m_World->GetTickRandomNumber(3) == 0) // 25% chance of an extra countdown when being rained on. + if (m_World->IsWeatherWet() && Random.RandBool(0.25)) // 25% chance of an extra countdown when being rained on. { m_CountDownTime--; } } else // if the floater is underground it has a 50% chance of not decreasing the countdown. { - if (m_World->GetTickRandomNumber(1) == 0) + if (Random.RandBool()) { m_CountDownTime++; } diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index b11c07a0b..7232ed614 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -1069,9 +1069,9 @@ void cPlayer::KilledBy(TakeDamageInfo & a_TDI) { case dtRangedAttack: DamageText = "was shot"; break; case dtLightning: DamageText = "was plasmified by lightining"; break; - case dtFalling: DamageText = (GetWorld()->GetTickRandomNumber(10) % 2 == 0) ? "fell to death" : "hit the ground too hard"; break; + case dtFalling: DamageText = GetRandomProvider().RandBool() ? "fell to death" : "hit the ground too hard"; break; case dtDrowning: DamageText = "drowned"; break; - case dtSuffocating: DamageText = (GetWorld()->GetTickRandomNumber(10) % 2 == 0) ? "git merge'd into a block" : "fused with a block"; break; + case dtSuffocating: DamageText = GetRandomProvider().RandBool() ? "git merge'd into a block" : "fused with a block"; break; case dtStarving: DamageText = "forgot the importance of food"; break; case dtCactusContact: DamageText = "was impaled on a cactus"; break; case dtLavaContact: DamageText = "was melted by lava"; break; @@ -2295,18 +2295,17 @@ void cPlayer::UseEquippedItem(int a_Amount) int UnbreakingLevel = static_cast(Item.m_Enchantments.GetLevel(cEnchantments::enchUnbreaking)); if (UnbreakingLevel > 0) { - int chance; + double chance = 0.0; if (ItemCategory::IsArmor(Item.m_ItemType)) { - chance = 60 + (40 / (UnbreakingLevel + 1)); + chance = 0.6 + (0.4 / (UnbreakingLevel + 1)); } else { - chance = 100 / (UnbreakingLevel + 1); + chance = 1.0 / (UnbreakingLevel + 1); } - cFastRandom Random; - if (Random.NextInt(101) <= chance) + if (GetRandomProvider().RandBool(chance)) { return; } diff --git a/src/Entities/ThrownEggEntity.cpp b/src/Entities/ThrownEggEntity.cpp index b20050529..3131f4841 100644 --- a/src/Entities/ThrownEggEntity.cpp +++ b/src/Entities/ThrownEggEntity.cpp @@ -74,11 +74,12 @@ void cThrownEggEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cThrownEggEntity::TrySpawnChicken(const Vector3d & a_HitPos) { - if (m_World->GetTickRandomNumber(7) == 1) + auto & Random = GetRandomProvider(); + if (Random.RandBool(0.125)) { m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, mtChicken, true); } - else if (m_World->GetTickRandomNumber(32) == 1) + else if (Random.RandBool(1.0 / 33.0)) { m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, mtChicken, true); m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, mtChicken, true); diff --git a/src/FastRandom.cpp b/src/FastRandom.cpp index 718092aee..de33a112c 100644 --- a/src/FastRandom.cpp +++ b/src/FastRandom.cpp @@ -1,11 +1,11 @@ // FastRandom.cpp -// Implements the cFastRandom class representing a fast random number generator #include "Globals.h" #include "FastRandom.h" +#include #include #if defined (__GNUC__) @@ -13,109 +13,50 @@ #elif defined (_MSC_VER) #define ATTRIBUTE_TLS static __declspec(thread) #else - #error "Unknown thread local storage qualifier" + #define ATTRIBUTE_TLS thread_local #endif -static unsigned int GetRandomSeed() -{ - ATTRIBUTE_TLS bool SeedCounterInitialized = 0; - ATTRIBUTE_TLS unsigned int SeedCounter = 0; - - if (!SeedCounterInitialized) - { - std::random_device rd; - std::uniform_int_distribution dist; - SeedCounter = dist(rd); - SeedCounterInitialized = true; - } - return ++SeedCounter; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// cFastRandom: - - - - - -cFastRandom::cFastRandom(void) : - m_LinearRand(GetRandomSeed()) -{ -} - - - - - -int cFastRandom::NextInt(int a_Range) -{ - std::uniform_int_distribution<> distribution(0, a_Range - 1); - return distribution(m_LinearRand); -} - - - - - - -float cFastRandom::NextFloat(float a_Range) -{ - std::uniform_real_distribution distribution(0, a_Range); - return distribution(m_LinearRand); -} - - -int cFastRandom::GenerateRandomInteger(int a_Begin, int a_End) +MTRand & GetRandomProvider() { - std::uniform_int_distribution<> distribution(a_Begin, a_End); - return distribution(m_LinearRand); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// MTRand: - -MTRand::MTRand() : - m_MersenneRand(GetRandomSeed()) -{ -} - - - + // Some compilers don't support thread_local for non-POD types, this is purely a work around for that restriction. + // There should be minimal overhead for the non-initializing case and all thread's instances are deleted properly. + ATTRIBUTE_TLS MTRand * LocalPtr = nullptr; + if (LocalPtr == nullptr) + { + // This list allows deletion of elements as if they had static storage duration + static std::mutex CSDeleteList; + static std::list> DeleteList; + cRandomDeviceSeeder seeder; + auto NewInstance = cpp14::make_unique(seeder); + auto TempPtr = NewInstance.get(); -int MTRand::randInt(int a_Range) -{ - std::uniform_int_distribution<> distribution(0, a_Range); - return distribution(m_MersenneRand); + std::lock_guard Lock(CSDeleteList); + DeleteList.push_front(std::move(NewInstance)); + LocalPtr = TempPtr; // Set after push_back so LocalPtr won't dangle if it throws + } + return *LocalPtr; } -int MTRand::randInt() +UInt32 Detail::GetRandomSeed() { - std::uniform_int_distribution<> distribution(0, std::numeric_limits::max()); - return distribution(m_MersenneRand); -} - - - - + ATTRIBUTE_TLS bool SeedCounterInitialized = false; + ATTRIBUTE_TLS UInt32 SeedCounter = 0; -double MTRand::rand(double a_Range) -{ - std::uniform_real_distribution<> distribution(0, a_Range); - return distribution(m_MersenneRand); + if (!SeedCounterInitialized) + { + std::random_device rd; + std::uniform_int_distribution dist; + SeedCounter = dist(rd); + SeedCounterInitialized = true; + } + return ++SeedCounter; } diff --git a/src/FastRandom.h b/src/FastRandom.h index a21da8391..77bafc217 100644 --- a/src/FastRandom.h +++ b/src/FastRandom.h @@ -4,17 +4,10 @@ // Declares the cFastRandom class representing a fast random number generator /* -The cFastRandom aims to provide a very fast, although not very cryptographically secure, random generator. -It is fast to instantiate, fast to query next, and partially multi-thread-safe. -It is multi-thread-safe in the sense that it can be accessed from multiple threads without crashing, but it may -yield duplicate numbers in that case. - -Internally, this works similar to cNoise's integral noise generation, with some predefined inputs: the seed is -taken from a global counter and the random is calculated using a counter that is incremented on each use (hence -the multi-thread duplication). Two alternatives exists for each function, one that takes a range parameter, -and another that takes an additional "salt" parameter; this salt is used as an additional input to the random, -in order to avoid multi-thread duplication. If two threads both use the class at the same time with different -salts, the values they get will be different. +The cFastRandom alias should be avoided in favor of the result of a call to GetRandomProvider(). +The MTRand generator used is faster, has a better range and provides higher quality randomness. +Note that MTRand is relatively costly to construct and so instances should be long lived, +prefer calls to GetRandomProvider over creating new instances. */ @@ -23,58 +16,195 @@ salts, the values they get will be different. #pragma once #include +#include +#include -class cFastRandom +namespace Detail +{ + /** Returns a low quality seed. */ + UInt32 GetRandomSeed(); + + /** Aliases true_type if Char is any variant of char ignoring signed-ness. */ + template + using IsChar = typename std::is_same::type, signed char>::type; + + template + struct cUniformImpl : + public std::conditional< + IsChar::value, + typename std::conditional< // Match signed-ness of IntType + std::is_signed::value, + std::uniform_int_distribution, + std::uniform_int_distribution + >::type, + std::uniform_int_distribution + > + { + }; + + /** uniform_int_distribution is undefined so this aliases a valid type. */ + template + using cUniform = typename cUniformImpl::type; +} + + + + + +/** Class to wrap any random engine to provide a more convenient interface. */ +template +class cRandomWrapper { public: + /** Initialize with a low quality seed. */ + cRandomWrapper(): + m_Engine(Detail::GetRandomSeed()) + { + } - cFastRandom(void); - /** Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M */ - int NextInt(int a_Range); + /** Initialize with a SeedSequence. */ + template + cRandomWrapper(SeedSeq & a_SeedSeq): + m_Engine(a_SeedSeq) + { + } - /** Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M */ - float NextFloat(float a_Range); - /** Returns a random float between 0 and 1. */ - float NextFloat(void) { return NextFloat(1); } - /** Returns a random int in the range [a_Begin .. a_End] */ - int GenerateRandomInteger(int a_Begin, int a_End); + /** Return a random IntType in the range [a_Min, a_Max]. */ + template + IntType RandInt(ArgType a_Min, ArgType a_Max) + { + ASSERT( + (a_Max >= a_Min) && + (a_Max <= std::numeric_limits::max()) && + (a_Min >= std::numeric_limits::min()) + ); + Detail::cUniform dist( + static_cast(a_Min), + static_cast(a_Max) + ); + return static_cast(dist(m_Engine)); + } -private: - std::minstd_rand m_LinearRand; -}; + /** Return a random IntType in the range [0, a_Max]. */ + template + IntType RandInt(ArgType a_Max) + { + ASSERT((a_Max >= 0) && (a_Max <= std::numeric_limits::max())); + Detail::cUniform dist(IntType(0), static_cast(a_Max)); + return static_cast(dist(m_Engine)); + } + + + + + + /** Return a random IntType in the range [0, std::numeric_limits::max()]. */ + template + IntType RandInt() + { + Detail::cUniform dist(IntType(0), std::numeric_limits::max()); + return static_cast(dist(m_Engine)); + } + + + + + + /** Return a random RealType in the range [a_Min, a_Max). */ + template + RealType RandReal(ArgType a_Min, ArgType a_Max) + { + std::uniform_real_distribution dist(a_Min, a_Max); + return dist(m_Engine); + } -class MTRand -{ -public: - MTRand(void); - /** Returns a random integer in the range [0 .. a_Range]. */ - int randInt(int a_Range); - /** Returns a random integer in the range [0 .. MAX_INT]. */ - int randInt(void); + /** Return a random RealType in the range [0, a_Max). */ + template + RealType RandReal(ArgType a_Max) + { + std::uniform_real_distribution dist(RealType(0), a_Max); + return dist(m_Engine); + } - /** Returns a random floating point number in the range [0 .. a_Range]. */ - double rand(double a_Range); + + + + + /** Return a random RealType in the range [0, 1). */ + template + RealType RandReal() + { + std::uniform_real_distribution dist; + return dist(m_Engine); + } + + + + + + /** Return a random bool with the given probability of being true. */ + bool RandBool(double a_TrueProbability = 0.5) + { + std::bernoulli_distribution dist(a_TrueProbability); + return dist(m_Engine); + } + + + + + /** Returns a reference to the underlying random engine. */ + RandomEngine & Engine() + { + return m_Engine; + } private: - std::mt19937 m_MersenneRand; + RandomEngine m_Engine; }; + +/** Utility to seed a random engine with maximal entropy from random_device. */ +struct cRandomDeviceSeeder +{ + using result_type = std::random_device::result_type; + + template + void generate(Itr first, Itr last) + { + std::random_device rd; + std::uniform_int_distribution dist; + for (; first != last; ++first) + { + *first = dist(rd); + } + } +}; + + + + + +using cFastRandom = cRandomWrapper; +using MTRand = cRandomWrapper; + +/** Returns the current thread's random number source. */ +MTRand & GetRandomProvider(); diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp index 0bbd63f82..8c0997d6b 100644 --- a/src/Generating/ChunkGenerator.cpp +++ b/src/Generating/ChunkGenerator.cpp @@ -59,8 +59,7 @@ bool cChunkGenerator::Start(cPluginInterface & a_PluginInterface, cChunkSink & a } else { - MTRand rnd; - m_Seed = rnd.randInt(); + m_Seed = GetRandomProvider().RandInt(); LOGINFO("Chosen a new random seed for world: %d", m_Seed); a_IniFile.SetValueI("Seed", "Seed", m_Seed); } diff --git a/src/Generating/DungeonRoomsFinisher.cpp b/src/Generating/DungeonRoomsFinisher.cpp index c1ff763eb..2b4b94993 100644 --- a/src/Generating/DungeonRoomsFinisher.cpp +++ b/src/Generating/DungeonRoomsFinisher.cpp @@ -162,7 +162,7 @@ protected: int RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width - 1); int RelEndX = Clamp(a_EndX - BlockX, 0, cChunkDef::Width); int RelEndZ = Clamp(a_EndZ - BlockZ, 0, cChunkDef::Width); - cFastRandom rnd; + auto & rnd = GetRandomProvider(); for (int y = a_StartY; y < a_EndY; y++) { for (int z = RelStartZ; z < RelEndZ; z++) @@ -171,7 +171,7 @@ protected: { if (cBlockInfo::CanBeTerraformed(a_ChunkDesc.GetBlockType(x, y, z))) { - BLOCKTYPE BlockType = (rnd.NextInt(101) < 75) ? a_DstBlockType1 : a_DstBlockType2; + BLOCKTYPE BlockType = rnd.RandBool(0.75) ? a_DstBlockType1 : a_DstBlockType2; a_ChunkDesc.SetBlockType(x, y, z, BlockType); } } // for x diff --git a/src/Item.cpp b/src/Item.cpp index 2eb009d4a..fdce06d1c 100644 --- a/src/Item.cpp +++ b/src/Item.cpp @@ -332,9 +332,9 @@ bool cItem::EnchantByXPLevels(int a_NumXPLevels) return false; } - cFastRandom Random; - int ModifiedEnchantmentLevel = a_NumXPLevels + static_cast(Random.NextFloat(static_cast(Enchantability / 4))) + static_cast(Random.NextFloat(static_cast(Enchantability / 4))) + 1; - float RandomBonus = 1.0F + (Random.NextFloat(1) + Random.NextFloat(1) - 1.0F) * 0.15F; + auto & Random = GetRandomProvider(); + int ModifiedEnchantmentLevel = a_NumXPLevels + Random.RandInt(Enchantability / 4) + Random.RandInt(Enchantability / 4) + 1; + float RandomBonus = 1.0F + (Random.RandReal() + Random.RandReal() - 1.0F) * 0.15F; int FinalEnchantmentLevel = static_cast(ModifiedEnchantmentLevel * RandomBonus + 0.5F); cWeightedEnchantments Enchantments; @@ -352,12 +352,10 @@ bool cItem::EnchantByXPLevels(int a_NumXPLevels) // Checking for conflicting enchantments cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment1); - float NewEnchantmentLevel = static_cast(a_NumXPLevels); - // Next Enchantment (Second) - NewEnchantmentLevel = NewEnchantmentLevel / 2; - float SecondEnchantmentChance = (NewEnchantmentLevel + 1) / 50 * 100; - if (Enchantments.empty() || (Random.NextFloat(100) > SecondEnchantmentChance)) + float NewEnchantmentLevel = a_NumXPLevels / 2.0f; + float SecondEnchantmentChance = (NewEnchantmentLevel + 1) / 50.0f; + if (Enchantments.empty() || !Random.RandBool(SecondEnchantmentChance)) { return true; } @@ -370,9 +368,9 @@ bool cItem::EnchantByXPLevels(int a_NumXPLevels) cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment2); // Next Enchantment (Third) - NewEnchantmentLevel = NewEnchantmentLevel / 2; - float ThirdEnchantmentChance = (NewEnchantmentLevel + 1) / 50 * 100; - if (Enchantments.empty() || (Random.NextFloat(100) > ThirdEnchantmentChance)) + NewEnchantmentLevel = NewEnchantmentLevel / 2.0f; + float ThirdEnchantmentChance = (NewEnchantmentLevel + 1) / 50.0f; + if (Enchantments.empty() || !Random.RandBool(ThirdEnchantmentChance)) { return true; } @@ -385,9 +383,9 @@ bool cItem::EnchantByXPLevels(int a_NumXPLevels) cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment3); // Next Enchantment (Fourth) - NewEnchantmentLevel = NewEnchantmentLevel / 2; - float FourthEnchantmentChance = (NewEnchantmentLevel + 1) / 50 * 100; - if (Enchantments.empty() || (Random.NextFloat(100) > FourthEnchantmentChance)) + NewEnchantmentLevel = NewEnchantmentLevel / 2.0f; + float FourthEnchantmentChance = (NewEnchantmentLevel + 1) / 50.0f; + if (Enchantments.empty() || !Random.RandBool(FourthEnchantmentChance)) { return true; } diff --git a/src/Items/ItemFishingRod.h b/src/Items/ItemFishingRod.h index 3958b12e5..2becc16b0 100644 --- a/src/Items/ItemFishingRod.h +++ b/src/Items/ItemFishingRod.h @@ -108,6 +108,8 @@ public: return false; } + auto & Random = GetRandomProvider(); + if (a_Player->IsFishing()) { cFloaterCallback FloaterInfo; @@ -122,10 +124,10 @@ public: else if (FloaterInfo.CanPickup()) { cItems Drops; - int ItemCategory = a_World->GetTickRandomNumber(99); + int ItemCategory = Random.RandInt(99); if (ItemCategory <= 4) // Treasures 5% { - int Treasure = a_World->GetTickRandomNumber(5); + int Treasure = Random.RandInt(5); switch (Treasure) { case 0: @@ -140,7 +142,7 @@ public: } case 2: { - Drops.Add(cItem(E_ITEM_FISHING_ROD, 1, static_cast(a_World->GetTickRandomNumber(50)))); // Fishing rod with durability. TODO: Enchantments on it + Drops.Add(cItem(E_ITEM_FISHING_ROD, 1, Random.RandInt(50))); // Fishing rod with durability. TODO: Enchantments on it break; } case 3: @@ -164,14 +166,14 @@ public: } else if (ItemCategory <= 14) // Junk 10% { - int Junk = a_World->GetTickRandomNumber(70); + int Junk = Random.RandInt(70); if (Junk <= 1) { Drops.Add(cItem(E_ITEM_DYE, 10, 0)); } else if (Junk <= 4) { - Drops.Add(cItem(E_ITEM_BOW, 1, static_cast(a_World->GetTickRandomNumber(64)))); + Drops.Add(cItem(E_ITEM_BOW, 1, Random.RandInt(64))); } else if (Junk <= 9) { @@ -214,7 +216,7 @@ public: } else // Fish { - int FishType = a_World->GetTickRandomNumber(99); + int FishType = Random.RandInt(99); if (FishType <= 1) // Clownfish has a 2% chance of spawning { Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_CLOWNFISH)); @@ -250,7 +252,7 @@ public: } else { - cFloater * Floater = new cFloater(a_Player->GetPosX(), a_Player->GetStance(), a_Player->GetPosZ(), a_Player->GetLookVector() * 15, a_Player->GetUniqueID(), static_cast(100 + static_cast(a_World->GetTickRandomNumber(800)) - (a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchLure) * 100))); + cFloater * Floater = new cFloater(a_Player->GetPosX(), a_Player->GetStance(), a_Player->GetPosZ(), a_Player->GetLookVector() * 15, a_Player->GetUniqueID(), (Random.RandInt(100, 900) - static_cast(a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchLure) * 100))); if (!Floater->Initialize(*a_World)) { delete Floater; diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp index d037092b1..8e3d79506 100644 --- a/src/Items/ItemHandler.cpp +++ b/src/Items/ItemHandler.cpp @@ -861,8 +861,7 @@ bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item) float Chance; if (Success && GetEatEffect(EffectType, EffectDurationTicks, EffectIntensity, Chance)) { - cFastRandom r1; - if (r1.NextFloat() < Chance) + if (GetRandomProvider().RandBool(Chance)) { a_Player->AddEntityEffect(EffectType, EffectDurationTicks, EffectIntensity, Chance); } diff --git a/src/Items/ItemThrowable.h b/src/Items/ItemThrowable.h index eaeb118f0..7bdba17cc 100644 --- a/src/Items/ItemThrowable.h +++ b/src/Items/ItemThrowable.h @@ -36,8 +36,7 @@ public: Vector3d Speed = a_Player->GetLookVector() * m_SpeedCoeff; // Play sound - cFastRandom Random; - a_World->BroadcastSoundEffect("entity.arrow.shoot", a_Player->GetPosX(), a_Player->GetPosY() - a_Player->GetHeight(), a_Player->GetPosZ(), 0.5f, 0.4f / (Random.NextFloat(1.0f) * 0.4f + 0.8f)); + a_World->BroadcastSoundEffect("entity.arrow.shoot", a_Player->GetPosX(), a_Player->GetPosY() - a_Player->GetHeight(), a_Player->GetPosZ(), 0.5f, 0.4f / GetRandomProvider().RandReal(0.8f, 1.2f)); if (a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, &a_Player->GetEquippedItem(), &Speed) == cEntity::INVALID_ID) { diff --git a/src/Map.cpp b/src/Map.cpp index 5db09adba..2fe901d8e 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -306,10 +306,8 @@ const cMapDecorator cMap::CreateDecorator(const cEntity * a_TrackedEntity) if (GetDimension() == dimNether) { - cFastRandom Random; - // TODO 2014-02-19 xdot: Refine - Rot = Random.NextInt(16); + Rot = GetRandomProvider().RandInt(15); } else { diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp index 60f7cfb43..98c428bab 100644 --- a/src/MobSpawner.cpp +++ b/src/MobSpawner.cpp @@ -111,12 +111,8 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) if (allowedMobsSize > 0) { std::set::iterator itr = allowedMobs.begin(); - int iRandom = m_Random.NextInt(static_cast(allowedMobsSize)); - for (int i = 0; i < iRandom; i++) - { - ++itr; - } + std::advance(itr, GetRandomProvider().RandInt(allowedMobsSize - 1)); return *itr; } @@ -139,7 +135,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R return false; // Make sure mobs do not spawn on bedrock. } - cFastRandom Random; + auto & Random = GetRandomProvider(); BLOCKTYPE TargetBlock = a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ); cPlayer * a_Closest_Player = a_Chunk->GetWorld()->FindClosestPlayer(a_Chunk->PositionToWorldPosition(a_RelX, a_RelY, a_RelZ), 24); @@ -202,7 +198,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R (BlockBelow == E_BLOCK_GRASS) || (BlockBelow == E_BLOCK_LEAVES) || (BlockBelow == E_BLOCK_NEW_LEAVES) ) && (a_RelY >= 62) && - (Random.NextInt(3) != 0) + (Random.RandBool(2.0 / 3.0)) ); } @@ -260,7 +256,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R (!cBlockInfo::IsTransparent(BlockBelow)) && (SkyLight <= 7) && (BlockLight <= 7) && - (Random.NextInt(2) == 0) + (Random.RandBool()) ); } @@ -274,7 +270,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R (!cBlockInfo::IsTransparent(BlockBelow)) && (SkyLight <= 7) && (BlockLight <= 7) && - (Random.NextInt(2) == 0) + (Random.RandBool()) ); } @@ -298,7 +294,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R (TargetBlock == E_BLOCK_AIR) && (BlockAbove == E_BLOCK_AIR) && (!cBlockInfo::IsTransparent(BlockBelow)) && - (Random.NextInt(20) == 0) + (Random.RandBool(0.05)) ); } diff --git a/src/MobSpawner.h b/src/MobSpawner.h index 3a2776df4..dd5b64fba 100644 --- a/src/MobSpawner.h +++ b/src/MobSpawner.h @@ -5,7 +5,6 @@ #include "BlockID.h" #include "ChunkDef.h" #include "Chunk.h" -#include "FastRandom.h" #include "Mobs/Monster.h" // This is a side-effect of keeping Mobfamily inside Monster class. I'd prefer to keep both (Mobfamily and Monster) inside a "Monster" namespace MG TODO : do it @@ -66,7 +65,6 @@ protected : bool m_NewPack; eMonsterType m_MobType; std::set m_Spawned; - cFastRandom m_Random; } ; diff --git a/src/Mobs/Chicken.cpp b/src/Mobs/Chicken.cpp index 0224078f5..1068295e6 100644 --- a/src/Mobs/Chicken.cpp +++ b/src/Mobs/Chicken.cpp @@ -34,7 +34,7 @@ void cChicken::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) return; // Babies don't lay eggs } - if ((m_EggDropTimer == 6000) && (m_World->GetTickRandomNumber(1) == 0)) + if ((m_EggDropTimer == 6000) && GetRandomProvider().RandBool()) { cItems Drops; m_EggDropTimer = 0; diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp index 978471b8d..acf79d3b1 100644 --- a/src/Mobs/Horse.cpp +++ b/src/Mobs/Horse.cpp @@ -41,16 +41,18 @@ void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) return; } + auto & Random = GetRandomProvider(); + if (!m_bIsMouthOpen) { - if (m_World->GetTickRandomNumber(50) == 25) + if (Random.RandBool(0.02)) { m_bIsMouthOpen = true; } } else { - if (m_World->GetTickRandomNumber(10) == 5) + if (Random.RandBool(0.10)) { m_bIsMouthOpen = false; } @@ -60,7 +62,7 @@ void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (m_TameAttemptTimes < m_TimesToTame) { - if (m_World->GetTickRandomNumber(50) == 25) + if (Random.RandBool(0.02)) { m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_EAST)); m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_WEST)); diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 00045fc69..ecda6e724 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -512,7 +512,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI) case mtOcelot: case mtWolf: { - Reward = m_World->GetTickRandomNumber(2) + 1; + Reward = GetRandomProvider().RandInt(1, 3); break; } @@ -531,7 +531,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI) case mtSlime: case mtMagmaCube: { - Reward = 6 + (m_World->GetTickRandomNumber(2)); + Reward = GetRandomProvider().RandInt(6, 8); break; } case mtBlaze: @@ -657,13 +657,15 @@ void cMonster::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) if (m_IdleInterval > std::chrono::seconds(1)) { + auto & Random = GetRandomProvider(); + // At this interval the results are predictable - int rem = m_World->GetTickRandomNumber(6) + 1; + int rem = Random.RandInt(1, 7); m_IdleInterval -= std::chrono::seconds(1); // So nothing gets dropped when the server hangs for a few seconds Vector3d Dist; - Dist.x = static_cast(m_World->GetTickRandomNumber(10)) - 5.0; - Dist.z = static_cast(m_World->GetTickRandomNumber(10)) - 5.0; + Dist.x = static_cast(Random.RandInt(-5, 5)); + Dist.z = static_cast(Random.RandInt(-5, 5)); if ((Dist.SqrLength() > 2) && (rem >= 3)) { @@ -1005,7 +1007,7 @@ cPawn * cMonster::GetTarget () cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType) { - cFastRandom Random; + auto & Random = GetRandomProvider(); cMonster * toReturn = nullptr; // Create the mob entity @@ -1013,23 +1015,23 @@ cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType) { case mtMagmaCube: { - toReturn = new cMagmaCube(1 << Random.NextInt(3)); // Size 1, 2 or 4 + toReturn = new cMagmaCube(1 << Random.RandInt(2)); // Size 1, 2 or 4 break; } case mtSlime: { - toReturn = new cSlime(1 << Random.NextInt(3)); // Size 1, 2 or 4 + toReturn = new cSlime(1 << Random.RandInt(2)); // Size 1, 2 or 4 break; } case mtSkeleton: { // TODO: Actual detection of spawning in Nether - toReturn = new cSkeleton((Random.NextInt(1) == 0) ? false : true); + toReturn = new cSkeleton(false); break; } case mtVillager: { - int VillagerType = Random.NextInt(6); + int VillagerType = Random.RandInt(6); if (VillagerType == 6) { // Give farmers a better chance of spawning @@ -1042,10 +1044,10 @@ cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType) case mtHorse: { // Horses take a type (species), a colour, and a style (dots, stripes, etc.) - int HorseType = Random.NextInt(8); - int HorseColor = Random.NextInt(7); - int HorseStyle = Random.NextInt(5); - int HorseTameTimes = Random.NextInt(6) + 1; + int HorseType = Random.RandInt(7); + int HorseColor = Random.RandInt(6); + int HorseStyle = Random.RandInt(4); + int HorseTameTimes = Random.RandInt(1, 6); if ((HorseType == 5) || (HorseType == 6) || (HorseType == 7)) { @@ -1097,11 +1099,10 @@ cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType) void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth) { - MTRand r1; - int Count = static_cast(static_cast(r1.randInt()) % (a_Max + 1 - a_Min) + a_Min); + auto Count = GetRandomProvider().RandInt(static_cast(a_Min), static_cast(a_Max)); if (Count > 0) { - a_Drops.push_back(cItem(a_Item, static_cast(Count), a_ItemHealth)); + a_Drops.emplace_back(a_Item, Count, a_ItemHealth); } } @@ -1111,9 +1112,7 @@ void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned void cMonster::AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short a_Item, short a_ItemHealth) { - MTRand r1; - int Count = r1.randInt() % 1000; - if (Count < (a_Chance * 10)) + if (GetRandomProvider().RandBool(a_Chance / 100.0)) { a_Drops.push_back(cItem(a_Item, 1, a_ItemHealth)); } @@ -1125,11 +1124,10 @@ void cMonster::AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short void cMonster::AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, unsigned int a_LootingLevel) { - MTRand r1; - unsigned int Count = static_cast(static_cast(r1.randInt()) % 200); - if (Count < (5 + a_LootingLevel)) + auto & r1 = GetRandomProvider(); + if (r1.RandBool((5 + a_LootingLevel) / 200.0)) { - size_t Rare = static_cast(r1.randInt()) % a_Items.Size(); + size_t Rare = r1.RandInt(a_Items.Size() - 1); a_Drops.push_back(a_Items.at(Rare)); } } @@ -1140,8 +1138,11 @@ void cMonster::AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, unsigne void cMonster::AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLevel) { - MTRand r1; - if (r1.randInt() % 200 < ((m_DropChanceHelmet * 200) + (a_LootingLevel * 2))) + auto & r1 = GetRandomProvider(); + + double LootingBonus = a_LootingLevel / 100.0; + + if (r1.RandBool(m_DropChanceHelmet + LootingBonus)) { if (!GetEquippedHelmet().IsEmpty()) { @@ -1149,7 +1150,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLe } } - if (r1.randInt() % 200 < ((m_DropChanceChestplate * 200) + (a_LootingLevel * 2))) + if (r1.RandBool(m_DropChanceChestplate + LootingBonus)) { if (!GetEquippedChestplate().IsEmpty()) { @@ -1157,7 +1158,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLe } } - if (r1.randInt() % 200 < ((m_DropChanceLeggings * 200) + (a_LootingLevel * 2))) + if (r1.RandBool(m_DropChanceLeggings + LootingBonus)) { if (!GetEquippedLeggings().IsEmpty()) { @@ -1165,7 +1166,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLe } } - if (r1.randInt() % 200 < ((m_DropChanceBoots * 200) + (a_LootingLevel * 2))) + if (r1.RandBool(m_DropChanceBoots + LootingBonus)) { if (!GetEquippedBoots().IsEmpty()) { @@ -1180,8 +1181,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLe void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingLevel) { - MTRand r1; - if (r1.randInt() % 200 < ((m_DropChanceWeapon * 200) + (a_LootingLevel * 2))) + if (GetRandomProvider().RandBool(m_DropChanceWeapon + (a_LootingLevel / 100.0))) { if (!GetEquippedWeapon().IsEmpty()) { diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp index 42884fb56..5646f8e32 100644 --- a/src/Mobs/PassiveMonster.cpp +++ b/src/Mobs/PassiveMonster.cpp @@ -127,8 +127,7 @@ void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) Callback.Baby->InheritFromParents(this, m_LovePartner); } - cFastRandom Random; - m_World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, 1 + Random.NextInt(6)); + m_World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, GetRandomProvider().RandInt(1, 6)); m_LovePartner->ResetLoveMode(); ResetLoveMode(); diff --git a/src/Mobs/Path.cpp b/src/Mobs/Path.cpp index 1273b2517..9f34e73d9 100644 --- a/src/Mobs/Path.cpp +++ b/src/Mobs/Path.cpp @@ -148,7 +148,7 @@ bool cPath::StepOnce() // Check if we have a new NearestPoint. if ((m_Destination - CurrentCell->m_Location).Length() < 5) { - if (m_Rand.NextInt(4) == 0) + if (GetRandomProvider().RandBool(0.25)) { m_NearestPointToTarget = CurrentCell; } diff --git a/src/Mobs/Path.h b/src/Mobs/Path.h index f51b7da77..977c5be1c 100644 --- a/src/Mobs/Path.h +++ b/src/Mobs/Path.h @@ -178,7 +178,6 @@ private: double m_HalfWidth; int m_StepsLeft; cPathCell * m_NearestPointToTarget; - cFastRandom m_Rand; /* Control fields */ ePathFinderStatus m_Status; diff --git a/src/Mobs/Rabbit.cpp b/src/Mobs/Rabbit.cpp index 9750ed5d1..f4de0ba0c 100644 --- a/src/Mobs/Rabbit.cpp +++ b/src/Mobs/Rabbit.cpp @@ -10,8 +10,8 @@ cRabbit::cRabbit(void) : - cRabbit(static_cast(cFastRandom().NextInt( - static_cast(eRabbitType::SaltAndPepper) + 1 // Max possible Rabbit-Type + cRabbit(static_cast(GetRandomProvider().RandInt( + static_cast(eRabbitType::SaltAndPepper) // Max possible Rabbit-Type )), 0) { } diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp index 810d37a70..4adcedae9 100644 --- a/src/Mobs/Sheep.cpp +++ b/src/Mobs/Sheep.cpp @@ -65,8 +65,8 @@ void cSheep::OnRightClicked(cPlayer & a_Player) a_Player.UseEquippedItem(); cItems Drops; - int NumDrops = m_World->GetTickRandomNumber(2) + 1; - Drops.push_back(cItem(E_BLOCK_WOOL, static_cast(NumDrops), static_cast(m_WoolColor))); + char NumDrops = GetRandomProvider().RandInt(1, 3); + Drops.emplace_back(E_BLOCK_WOOL, NumDrops, static_cast(m_WoolColor)); m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); m_World->BroadcastSoundEffect("entity.sheep.shear", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 1.0f); } @@ -121,7 +121,7 @@ void cSheep::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } else { - if (m_World->GetTickRandomNumber(600) == 1) + if (GetRandomProvider().RandBool(1.0 / 600.0)) { if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS) { @@ -167,8 +167,7 @@ void cSheep::InheritFromParents(cPassiveMonster * a_Parent1, cPassiveMonster * a return; } } - cFastRandom Random; - SetFurColor((Random.NextInt(100) < 50) ? Parent1->GetFurColor() : Parent2->GetFurColor()); + SetFurColor(GetRandomProvider().RandBool() ? Parent1->GetFurColor() : Parent2->GetFurColor()); m_World->BroadcastEntityMetadata(*this); } @@ -178,8 +177,7 @@ void cSheep::InheritFromParents(cPassiveMonster * a_Parent1, cPassiveMonster * a NIBBLETYPE cSheep::GenerateNaturalRandomColor(void) { - cFastRandom Random; - int Chance = Random.NextInt(101); + int Chance = GetRandomProvider().RandInt(100); if (Chance <= 81) { diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp index 311533b69..0d6d5e428 100644 --- a/src/Mobs/Skeleton.cpp +++ b/src/Mobs/Skeleton.cpp @@ -51,12 +51,12 @@ void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer) bool cSkeleton::Attack(std::chrono::milliseconds a_Dt) { StopMovingToPosition(); // Todo handle this in a better way, the skeleton does some uneeded recalcs due to inStateChasing - cFastRandom Random; + auto & Random = GetRandomProvider(); if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0)) { - Vector3d Inaccuracy = Vector3d(Random.NextFloat(0.5) - 0.25, Random.NextFloat(0.5) - 0.25, Random.NextFloat(0.5) - 0.25); + Vector3d Inaccuracy = Vector3d(Random.RandReal(-0.25, 0.25), Random.RandReal(-0.25, 0.25), Random.RandReal(-0.25, 0.25)); Vector3d Speed = (GetTarget()->GetPosition() + Inaccuracy - GetPosition()) * 5; - Speed.y = Speed.y - 1 + Random.NextInt(3); + Speed.y += Random.RandInt(-1, 1); cArrowEntity * Arrow = new cArrowEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); if (Arrow == nullptr) { diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp index dca5c5887..3f832ae87 100644 --- a/src/Mobs/Slime.cpp +++ b/src/Mobs/Slime.cpp @@ -70,8 +70,8 @@ void cSlime::KilledBy(TakeDamageInfo & a_TDI) if (m_Size != 1) { - cFastRandom Random; - int SpawnAmount = 2 + Random.NextInt(3); + auto & Random = GetRandomProvider(); + int SpawnAmount = Random.RandInt(2, 4); for (int i = 0; i < SpawnAmount; ++i) { @@ -80,7 +80,7 @@ void cSlime::KilledBy(TakeDamageInfo & a_TDI) cSlime * NewSlime = new cSlime(m_Size / 2); NewSlime->SetPosition(GetPosX() + AddX, GetPosY() + 0.5, GetPosZ() + AddZ); - NewSlime->SetYaw(Random.NextFloat(1.0f) * 360.0f); + NewSlime->SetYaw(Random.RandReal(360.0f)); m_World->SpawnMobFinalize(NewSlime); } } diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp index 46a448eb5..26462ba31 100644 --- a/src/Mobs/Villager.cpp +++ b/src/Mobs/Villager.cpp @@ -32,7 +32,7 @@ bool cVillager::DoTakeDamage(TakeDamageInfo & a_TDI) if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPlayer()) { - if (m_World->GetTickRandomNumber(5) == 3) + if (GetRandomProvider().RandBool(1.0 / 6.0)) { m_World->BroadcastEntityStatus(*this, esVillagerAngry); } @@ -90,7 +90,7 @@ void cVillager::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } // Don't always try to do a special action. Each tick has 1% to do a special action. - if (m_World->GetTickRandomNumber(99) != 0) + if (GetRandomProvider().RandBool(0.99)) { return; } @@ -150,7 +150,7 @@ void cVillager::HandleFarmerPrepareFarmCrops() m_VillagerAction = true; m_CropsPos = Vector3i(static_cast(GetPosX()) + X - 5, static_cast(GetPosY()) + Y - 3, static_cast(GetPosZ()) + Z - 5); - MoveToPosition(Vector3f(static_cast(m_CropsPos.x + 0.5), static_cast(m_CropsPos.y), static_cast(m_CropsPos.z + 0.5))); + MoveToPosition(Vector3d(m_CropsPos.x + 0.5, m_CropsPos.y + 0.0, m_CropsPos.z + 0.5)); return; } // for Y loop. } // Repeat the procces 5 times. diff --git a/src/Mobs/Witch.cpp b/src/Mobs/Witch.cpp index fa4074a54..3f56108ae 100644 --- a/src/Mobs/Witch.cpp +++ b/src/Mobs/Witch.cpp @@ -24,11 +24,11 @@ void cWitch::GetDrops(cItems & a_Drops, cEntity * a_Killer) { LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); } - MTRand r1; - int DropTypeCount = (r1.randInt() % 3) + 1; + auto & r1 = GetRandomProvider(); + int DropTypeCount = r1.RandInt(1, 3); for (int i = 0; i < DropTypeCount; i++) { - int DropType = r1.randInt() % 7; + int DropType = r1.RandInt(6); switch (DropType) { case 0: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GLASS_BOTTLE); break; diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp index 325c47183..560a6b2fa 100644 --- a/src/Mobs/Wolf.cpp +++ b/src/Mobs/Wolf.cpp @@ -138,7 +138,7 @@ void cWolf::ReceiveNearbyFightInfo(AString a_PlayerID, cPawn * a_Opponent, bool // If a player is asking for help and we already have a target, // there's a 50% chance of helping and a 50% chance of doing nothing // This helps spread a wolf pack's targets over several mobs - else if (m_World->GetTickRandomNumber(9)> 4) + else if (GetRandomProvider().RandBool()) { return; } @@ -179,7 +179,7 @@ void cWolf::OnRightClicked(cPlayer & a_Player) a_Player.GetInventory().RemoveOneEquippedItem(); } - if (m_World->GetTickRandomNumber(7) == 0) + if (GetRandomProvider().RandBool(0.125)) { // Taming succeeded SetMaxHealth(20); diff --git a/src/ProbabDistrib.cpp b/src/ProbabDistrib.cpp index f63aa0605..7e38d5c12 100644 --- a/src/ProbabDistrib.cpp +++ b/src/ProbabDistrib.cpp @@ -100,8 +100,7 @@ bool cProbabDistrib::SetDefString(const AString & a_DefString) int cProbabDistrib::Random(MTRand & a_Rand) const { - int v = a_Rand.randInt(m_Sum); - return MapValue(v); + return MapValue(a_Rand.RandInt(m_Sum)); } diff --git a/src/ProbabDistrib.h b/src/ProbabDistrib.h index aeac5a30a..a051c29d3 100644 --- a/src/ProbabDistrib.h +++ b/src/ProbabDistrib.h @@ -20,8 +20,7 @@ Usage: -// fwd: -class MTRand; +#include "FastRandom.h" diff --git a/src/Server.cpp b/src/Server.cpp index fbd190f0d..6d56a5890 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -17,6 +17,7 @@ #include "WebAdmin.h" #include "Protocol/ProtocolRecognizer.h" #include "CommandOutput.h" +#include "FastRandom.h" #include "IniFile.h" #include "Vector3.h" @@ -218,9 +219,9 @@ bool cServer::InitServer(cSettingsRepositoryInterface & a_Settings, bool a_Shoul m_ShouldAuthenticate = a_ShouldAuth; if (m_ShouldAuthenticate) { - MTRand mtrand1; - unsigned int r1 = (mtrand1.randInt() % 1147483647) + 1000000000; - unsigned int r2 = (mtrand1.randInt() % 1147483647) + 1000000000; + auto & rand = GetRandomProvider(); + unsigned int r1 = rand.RandInt(1000000000U, 0x7fffffffU); + unsigned int r2 = rand.RandInt(1000000000U, 0x7fffffffU); std::ostringstream sid; sid << std::hex << r1; sid << std::hex << r2; diff --git a/src/Simulator/FireSimulator.cpp b/src/Simulator/FireSimulator.cpp index 5fc19ae15..1b2963bdc 100644 --- a/src/Simulator/FireSimulator.cpp +++ b/src/Simulator/FireSimulator.cpp @@ -301,7 +301,7 @@ int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, in void cFireSimulator::TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) { /* - if (m_World.GetTickRandomNumber(10000) > 100) + if (GetRandomProvider().RandBool(0.99)) { // Make the chance to spread 100x smaller return; @@ -317,7 +317,7 @@ void cFireSimulator::TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int // No need to check the coords for equality with the parent block, // it cannot catch fire anyway (because it's not an air block) - if (m_World.GetTickRandomNumber(MAX_CHANCE_FLAMMABILITY) > m_Flammability) + if (!GetRandomProvider().RandBool(m_Flammability * (1.0 / MAX_CHANCE_FLAMMABILITY))) { continue; } @@ -381,7 +381,7 @@ void cFireSimulator::RemoveFuelNeighbors(cChunk * a_Chunk, int a_RelX, int a_Rel return; } - bool ShouldReplaceFuel = (m_World.GetTickRandomNumber(MAX_CHANCE_REPLACE_FUEL) < m_ReplaceFuelChance); + bool ShouldReplaceFuel = (GetRandomProvider().RandBool(m_ReplaceFuelChance * (1.0 / MAX_CHANCE_REPLACE_FUEL))); if (ShouldReplaceFuel && !cRoot::Get()->GetPluginManager()->CallHookBlockSpread(m_World, AbsX, Y, AbsZ, ssFireSpread)) { Neighbour->SetBlock(X, Y, Z, E_BLOCK_FIRE, 0); diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp index 71a43e01d..24c9bfb03 100644 --- a/src/UI/SlotArea.cpp +++ b/src/UI/SlotArea.cpp @@ -1003,8 +1003,7 @@ void cSlotAreaAnvil::OnTakeResult(cPlayer & a_Player) NIBBLETYPE BlockMeta; a_Player.GetWorld()->GetBlockTypeMeta(PosX, PosY, PosZ, Block, BlockMeta); - cFastRandom Random; - if (!a_Player.IsGameModeCreative() && (Block == E_BLOCK_ANVIL) && (Random.NextFloat(1.0F) < 0.12F)) + if (!a_Player.IsGameModeCreative() && (Block == E_BLOCK_ANVIL) && GetRandomProvider().RandBool(0.12)) { NIBBLETYPE Orientation = BlockMeta & 0x3; NIBBLETYPE AnvilDamage = BlockMeta >> 2; @@ -1578,8 +1577,8 @@ void cSlotAreaEnchanting::UpdateResult(cPlayer & a_Player) { int Bookshelves = std::min(GetBookshelvesCount(*a_Player.GetWorld()), 15); - cFastRandom Random; - int Base = (Random.GenerateRandomInteger(1, 8) + static_cast(floor(static_cast(Bookshelves / 2)) + Random.GenerateRandomInteger(0, Bookshelves))); + auto & Random = GetRandomProvider(); + int Base = (Random.RandInt(1, 8) + (Bookshelves / 2) + Random.RandInt(0, Bookshelves)); int TopSlot = std::max(Base / 3, 1); int MiddleSlot = (Base * 2) / 3 + 1; int BottomSlot = std::max(Base, Bookshelves * 2); diff --git a/src/World.cpp b/src/World.cpp index f396aad86..0fa719670 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -245,22 +245,20 @@ void cWorld::CastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) int cWorld::GetDefaultWeatherInterval(eWeather a_Weather) { + auto & Random = GetRandomProvider(); switch (a_Weather) { case eWeather_Sunny: { - auto dif = m_MaxSunnyTicks - m_MinSunnyTicks + 1; - return m_MinSunnyTicks + (m_TickRand.randInt() % dif); + return Random.RandInt(m_MinSunnyTicks, m_MaxSunnyTicks); } case eWeather_Rain: { - auto dif = m_MaxRainTicks - m_MinRainTicks + 1; - return m_MinRainTicks + (m_TickRand.randInt() % dif); + return Random.RandInt(m_MinRainTicks, m_MaxRainTicks); } case eWeather_ThunderStorm: { - auto dif = m_MaxThunderStormTicks - m_MinThunderStormTicks + 1; - return m_MinThunderStormTicks + (m_TickRand.randInt() % dif); + return Random.RandInt(m_MinThunderStormTicks, m_MaxThunderStormTicks); } } @@ -812,7 +810,7 @@ eWeather cWorld::ChooseNewWeather() case eWeather_Rain: { // 1 / 8 chance of turning into a thunderstorm - return ((m_TickRand.randInt() % 256) < 32) ? eWeather_ThunderStorm : eWeather_Sunny; + return GetRandomProvider().RandBool(0.125) ? eWeather_ThunderStorm : eWeather_Sunny; } } @@ -1074,7 +1072,7 @@ void cWorld::TickWeather(float a_Dt) if (m_Weather == eWeather_ThunderStorm) { // 0.5% chance per tick of thunderbolt - if (m_TickRand.randInt() % 199 == 0) + if (GetRandomProvider().RandBool(0.005)) { CastThunderbolt(0, 0, 0); // TODO: find random positions near players to cast thunderbolts. } @@ -1697,7 +1695,7 @@ void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks) bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal) { - cFastRandom random; + auto & random = GetRandomProvider(); BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); @@ -1735,7 +1733,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy } else { - BlockMeta += random.NextInt(4) + 2; + BlockMeta += random.RandInt(2, 5); BlockMeta = std::min(BlockMeta, static_cast(7)); } FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); @@ -1770,7 +1768,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy } else { - BlockMeta += random.NextInt(4) + 2; + BlockMeta += random.RandInt(2, 5); BlockMeta = std::min(BlockMeta, static_cast(7)); } FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); @@ -1793,7 +1791,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy } else { - BlockMeta += random.NextInt(4) + 2; + BlockMeta += random.RandInt(2, 5); BlockMeta = std::min(BlockMeta, static_cast(7)); } FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); @@ -1825,7 +1823,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy } else { - BlockMeta += random.NextInt(4) + 2; + BlockMeta += random.RandInt(2, 5); BlockMeta = std::min(BlockMeta, static_cast(7)); } FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); @@ -1848,7 +1846,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy } else { - BlockMeta += random.NextInt(4) + 2; + BlockMeta += random.RandInt(2, 5); BlockMeta = std::min(BlockMeta, static_cast(7)); } FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); @@ -1884,14 +1882,14 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy { ++GrowState; } - else if (random.NextInt(99) < 45) + else if (random.RandBool(0.45)) { ++GrowState; } FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, static_cast(GrowState << 3 | TypeMeta)); } - else if (random.NextInt(99) < 45) + else if (random.RandBool(0.45)) { GrowTreeFromSapling(a_BlockX, a_BlockY, a_BlockZ, BlockMeta); } @@ -1905,12 +1903,12 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy { return false; } - MTRand r1; + auto & r1 = GetRandomProvider(); for (int i = 0; i < 60; i++) { - int OfsX = static_cast(r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3; - int OfsY = static_cast(r1.randInt(3) + r1.randInt(3)) - 3; - int OfsZ = static_cast(r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3; + int OfsX = (r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3)) / 2 - 3; + int OfsY = r1.RandInt(3) + r1.RandInt(3) - 3; + int OfsZ = (r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3)) / 2 - 3; BLOCKTYPE Ground = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ); if (Ground != E_BLOCK_GRASS) { @@ -1923,7 +1921,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy } BLOCKTYPE SpawnType; NIBBLETYPE SpawnMeta = 0; - switch (r1.randInt(10)) + switch (r1.RandInt(10)) { case 0: SpawnType = E_BLOCK_YELLOW_FLOWER; break; case 1: SpawnType = E_BLOCK_RED_ROSE; break; @@ -2031,8 +2029,7 @@ int cWorld::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocks bool cWorld::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) { - MTRand Rand; - return m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, Rand); + return m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType); } @@ -2162,6 +2159,7 @@ bool cWorld::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlock void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed, bool IsPlayerCreated) { + auto & Random = GetRandomProvider(); a_FlyAwaySpeed /= 100; // Pre-divide, so that we don't have to divide each time inside the loop for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr) { @@ -2171,9 +2169,9 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double continue; } - float SpeedX = static_cast(a_FlyAwaySpeed * (GetTickRandomNumber(10) - 5)); - float SpeedY = static_cast(a_FlyAwaySpeed * GetTickRandomNumber(50)); - float SpeedZ = static_cast(a_FlyAwaySpeed * (GetTickRandomNumber(10) - 5)); + float SpeedX = static_cast(a_FlyAwaySpeed * Random.RandInt(-5, 5)); + float SpeedY = static_cast(a_FlyAwaySpeed * Random.RandInt(50)); + float SpeedZ = static_cast(a_FlyAwaySpeed * Random.RandInt(-5, 5)); cPickup * Pickup = new cPickup( a_BlockX, a_BlockY, a_BlockZ, @@ -2310,10 +2308,11 @@ UInt32 cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, int a_FuseTick TNT = nullptr; return cEntity::INVALID_ID; } + auto & Random = GetRandomProvider(); TNT->SetSpeed( - a_InitialVelocityCoeff * (GetTickRandomNumber(2) - 1), /** -1, 0, 1 */ + a_InitialVelocityCoeff * Random.RandInt(-1, 1), a_InitialVelocityCoeff * 2, - a_InitialVelocityCoeff * (GetTickRandomNumber(2) - 1) + a_InitialVelocityCoeff * Random.RandInt(-1, 1) ); return TNT->GetUniqueID(); } @@ -3793,6 +3792,15 @@ UInt32 cWorld::CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cPr +int cWorld::GetTickRandomNumber(int a_Range) +{ + return GetRandomProvider().RandInt(a_Range); +} + + + + + void cWorld::TabCompleteUserName(const AString & a_Text, AStringVector & a_Results) { typedef std::pair pair_t; diff --git a/src/World.h b/src/World.h index 00467e2fa..495fdf174 100644 --- a/src/World.h +++ b/src/World.h @@ -28,7 +28,6 @@ #include "MapManager.h" #include "Blocks/WorldInterface.h" #include "Blocks/BroadcastInterface.h" -#include "FastRandom.h" #include "EffectID.h" @@ -801,8 +800,8 @@ public: Item parameter is currently used for Fireworks to correctly set entity metadata based on item metadata. */ UInt32 CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const cItem * a_Item, const Vector3d * a_Speed = nullptr); // tolua_export - /** Returns a random number from the m_TickRand in range [0 .. a_Range]. To be used only in the tick thread! */ - int GetTickRandomNumber(int a_Range) { return static_cast(m_TickRand.randInt(a_Range)); } + /** Returns a random number in range [0 .. a_Range]. */ + int GetTickRandomNumber(int a_Range); /** Appends all usernames starting with a_Text (case-insensitive) into Results */ void TabCompleteUserName(const AString & a_Text, AStringVector & a_Results); @@ -880,9 +879,6 @@ private: /** The dimension of the world, used by the client to provide correct lighting scheme */ eDimension m_Dimension; - /** This random generator is to be used only in the Tick() method, and thus only in the World-Tick-thread (MTRand is not exactly thread-safe) */ - MTRand m_TickRand; - bool m_IsSpawnExplicitlySet; double m_SpawnX; double m_SpawnY; diff --git a/tests/FastRandom/FastRandomTest.cpp b/tests/FastRandom/FastRandomTest.cpp index 9ab775936..2bfa7ced6 100644 --- a/tests/FastRandom/FastRandomTest.cpp +++ b/tests/FastRandom/FastRandomTest.cpp @@ -15,14 +15,13 @@ static void TestInts(void) cFastRandom rnd; int sum = 0; const int BUCKETS = 8; - int Counts[BUCKETS]; - memset(Counts, 0, sizeof(Counts)); + int Counts[BUCKETS] = {0}; const int ITER = 10000; for (int i = 0; i < ITER; i++) { - int v = rnd.NextInt(1000); + int v = rnd.RandInt(1000); assert_test(v >= 0); - assert_test(v < 1000); + assert_test(v <= 1000); Counts[v % BUCKETS]++; sum += v; } @@ -43,12 +42,11 @@ static void TestFloats(void) cFastRandom rnd; float sum = 0; const int BUCKETS = 8; - int Counts[BUCKETS]; - memset(Counts, 0, sizeof(Counts)); + int Counts[BUCKETS] = {0}; const int ITER = 10000; for (int i = 0; i < ITER; i++) { - float v = rnd.NextFloat(1000); + float v = rnd.RandReal(1000.0f); assert_test(v >= 0); assert_test(v <= 1000); Counts[static_cast(v) % BUCKETS]++; @@ -76,7 +74,7 @@ static void TestReCreation(void) for (int i = 0; i < ITER; ++i) { cFastRandom rnd; - int val = rnd.NextInt(10); + int val = rnd.RandInt(9); if (val == lastVal) { numSame += 1; -- cgit v1.2.3