summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/BlockEntities/DropSpenserEntity.cpp2
-rw-r--r--src/BlockEntities/MobSpawnerEntity.cpp12
-rw-r--r--src/Blocks/BlockBigFlower.h3
-rw-r--r--src/Blocks/BlockCocoaPod.h4
-rw-r--r--src/Blocks/BlockCrops.h22
-rw-r--r--src/Blocks/BlockDeadBush.h11
-rw-r--r--src/Blocks/BlockDirt.h8
-rw-r--r--src/Blocks/BlockGlowstone.h4
-rw-r--r--src/Blocks/BlockGravel.h3
-rw-r--r--src/Blocks/BlockHandler.cpp6
-rw-r--r--src/Blocks/BlockLeaves.h12
-rw-r--r--src/Blocks/BlockMelon.h3
-rw-r--r--src/Blocks/BlockMobHead.h6
-rw-r--r--src/Blocks/BlockMobSpawner.h4
-rw-r--r--src/Blocks/BlockNetherWart.h4
-rw-r--r--src/Blocks/BlockOre.h16
-rw-r--r--src/Blocks/BlockPlant.h3
-rw-r--r--src/Blocks/BlockPortal.h3
-rw-r--r--src/Blocks/BlockSapling.h6
-rw-r--r--src/Blocks/BlockSeaLantern.h3
-rw-r--r--src/Blocks/BlockTallGrass.h9
-rw-r--r--src/Chunk.cpp31
-rw-r--r--src/Chunk.h3
-rw-r--r--src/ChunkMap.cpp11
-rw-r--r--src/ChunkMap.h3
-rw-r--r--src/Enchantments.cpp4
-rw-r--r--src/Entities/Entity.cpp10
-rw-r--r--src/Entities/ExpBottleEntity.cpp2
-rw-r--r--src/Entities/Floater.cpp10
-rw-r--r--src/Entities/Player.cpp13
-rw-r--r--src/Entities/ThrownEggEntity.cpp5
-rw-r--r--src/FastRandom.cpp119
-rw-r--r--src/FastRandom.h200
-rw-r--r--src/Generating/ChunkGenerator.cpp3
-rw-r--r--src/Generating/DungeonRoomsFinisher.cpp4
-rw-r--r--src/Item.cpp26
-rw-r--r--src/Items/ItemFishingRod.h16
-rw-r--r--src/Items/ItemHandler.cpp3
-rw-r--r--src/Items/ItemThrowable.h3
-rw-r--r--src/Map.cpp4
-rw-r--r--src/MobSpawner.cpp16
-rw-r--r--src/MobSpawner.h2
-rw-r--r--src/Mobs/Chicken.cpp2
-rw-r--r--src/Mobs/Horse.cpp8
-rw-r--r--src/Mobs/Monster.cpp62
-rw-r--r--src/Mobs/PassiveMonster.cpp3
-rw-r--r--src/Mobs/Path.cpp2
-rw-r--r--src/Mobs/Path.h1
-rw-r--r--src/Mobs/Rabbit.cpp4
-rw-r--r--src/Mobs/Sheep.cpp12
-rw-r--r--src/Mobs/Skeleton.cpp6
-rw-r--r--src/Mobs/Slime.cpp6
-rw-r--r--src/Mobs/Villager.cpp6
-rw-r--r--src/Mobs/Witch.cpp6
-rw-r--r--src/Mobs/Wolf.cpp4
-rw-r--r--src/ProbabDistrib.cpp3
-rw-r--r--src/ProbabDistrib.h3
-rw-r--r--src/Server.cpp7
-rw-r--r--src/Simulator/FireSimulator.cpp6
-rw-r--r--src/UI/SlotArea.cpp7
-rw-r--r--src/World.cpp64
-rw-r--r--src/World.h8
-rw-r--r--tests/FastRandom/FastRandomTest.cpp14
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<short>(200 + m_World->GetTickRandomNumber(600));
+ m_SpawnDelay = GetRandomProvider().RandInt<short>(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<int>(m_RelX + static_cast<double>(Random.NextFloat() - Random.NextFloat()) * 4.0);
- int RelY = m_RelY + Random.NextInt(3) - 1;
- int RelZ = static_cast<int>(m_RelZ + static_cast<double>(Random.NextFloat() - Random.NextFloat()) * 4.0);
+ int RelX = m_RelX + static_cast<int>((Random.RandReal<double>() - Random.RandReal<double>()) * 4.0);
+ int RelY = m_RelY + Random.RandInt(-1, 1);
+ int RelZ = m_RelZ + static_cast<int>((Random.RandReal<double>() - Random.RandReal<double>()) * 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<char>(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<char>(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<char>(2) + rand.RandInt<char>(2)) / 2); // [1 .. 3] with high preference of 2
+ a_Pickups.emplace_back(E_ITEM_BEETROOT_SEEDS, SeedCount, 0);
+ char BeetrootCount = 1 + ((rand.RandInt<char>(2) + rand.RandInt<char>(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<char>(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<char>(2) + rand.RandInt<char>(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<char>(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<char>(2) + rand.RandInt<char>(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<char>(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<char>(2) + rand.RandInt<char>(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<char>(3);
if (chance != 0)
{
- a_Pickups.push_back(cItem(E_ITEM_STICK, static_cast<char>(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<double>(-0.5, 0.5);
+ MicroZ += r1.RandReal<double>(-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<char>(2 + Random.NextInt(3)), 0));
+ a_Pickups.emplace_back(E_ITEM_GLOWSTONE_DUST, GetRandomProvider().RandInt<char>(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<double>(-0.5, 0.5);
+ MicroZ += r1.RandReal<double>(-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<char>(3 + Random.NextInt(5)), 0));
+ a_Pickups.emplace_back(E_ITEM_MELON_SLICE, GetRandomProvider().RandInt<char>(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<short>(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<double>(-0.5, 0.5);
+ MicroZ += r1.RandReal<double>(-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<double>(a_BlockX), static_cast<double>(a_BlockY + 1), static_cast<double>(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<char>(1 + (rand.NextInt(3) + rand.NextInt(3))) / 2, 0));
+ a_Pickups.emplace_back(E_ITEM_NETHER_WART, 1 + (rand.RandInt<char>(2) + rand.RandInt<char>(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<char>(4 + Random.NextInt(5)), 4));
+ a_Pickups.emplace_back(E_ITEM_DYE, Random.RandInt<char>(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<char>(4 + Random.NextInt(2)), 0));
+ a_Pickups.emplace_back(E_ITEM_REDSTONE_DUST, Random.RandInt<char>(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<char>(2 + Random.NextInt(2)), 0));
+ a_Pickups.emplace_back(E_ITEM_PRISMARINE_CRYSTALS, GetRandomProvider().RandInt<char>(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<double>(-0.5, 0.5);
+ MicroZ += r1.RandReal<double>(-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<int>::max() / a_MaxY) && // a_MaxX * a_MaxY doesn't overflow
+ (a_MaxX * a_MaxY <= std::numeric_limits<int>::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<NIBBLETYPE>(a_TickRandom.randInt(4) % 4);
+ NIBBLETYPE Meta = (ProduceType == E_BLOCK_MELON) ? 0 : static_cast<NIBBLETYPE>(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<int>(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<Vector3f>(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<int>(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 <mutex>
#include <random>
#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<unsigned int> 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<float> 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<std::unique_ptr<MTRand>> DeleteList;
+ cRandomDeviceSeeder seeder;
+ auto NewInstance = cpp14::make_unique<MTRand>(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<std::mutex> 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<int>::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<UInt32> 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 <random>
+#include <type_traits>
+#include <limits>
-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 <class Char>
+ using IsChar = typename std::is_same<typename std::make_signed<Char>::type, signed char>::type;
+
+ template <class IntType>
+ struct cUniformImpl :
+ public std::conditional<
+ IsChar<IntType>::value,
+ typename std::conditional< // Match signed-ness of IntType
+ std::is_signed<IntType>::value,
+ std::uniform_int_distribution<short>,
+ std::uniform_int_distribution<unsigned short>
+ >::type,
+ std::uniform_int_distribution<IntType>
+ >
+ {
+ };
+
+ /** uniform_int_distribution<char> is undefined so this aliases a valid type. */
+ template <class IntType>
+ using cUniform = typename cUniformImpl<IntType>::type;
+}
+
+
+
+
+
+/** Class to wrap any random engine to provide a more convenient interface. */
+template <class RandomEngine>
+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 <class SeedSeq>
+ 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 <class IntType = int, class ArgType>
+ IntType RandInt(ArgType a_Min, ArgType a_Max)
+ {
+ ASSERT(
+ (a_Max >= a_Min) &&
+ (a_Max <= std::numeric_limits<IntType>::max()) &&
+ (a_Min >= std::numeric_limits<IntType>::min())
+ );
+ Detail::cUniform<IntType> dist(
+ static_cast<IntType>(a_Min),
+ static_cast<IntType>(a_Max)
+ );
+ return static_cast<IntType>(dist(m_Engine));
+ }
-private:
- std::minstd_rand m_LinearRand;
-};
+ /** Return a random IntType in the range [0, a_Max]. */
+ template <class IntType = int, class ArgType>
+ IntType RandInt(ArgType a_Max)
+ {
+ ASSERT((a_Max >= 0) && (a_Max <= std::numeric_limits<IntType>::max()));
+ Detail::cUniform<IntType> dist(IntType(0), static_cast<IntType>(a_Max));
+ return static_cast<IntType>(dist(m_Engine));
+ }
+
+
+
+
+
+ /** Return a random IntType in the range [0, std::numeric_limits<IntType>::max()]. */
+ template <class IntType = int>
+ IntType RandInt()
+ {
+ Detail::cUniform<IntType> dist(IntType(0), std::numeric_limits<IntType>::max());
+ return static_cast<IntType>(dist(m_Engine));
+ }
+
+
+
+
+
+ /** Return a random RealType in the range [a_Min, a_Max). */
+ template <class RealType = float, class ArgType>
+ RealType RandReal(ArgType a_Min, ArgType a_Max)
+ {
+ std::uniform_real_distribution<RealType> 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 <class RealType = float, class ArgType>
+ RealType RandReal(ArgType a_Max)
+ {
+ std::uniform_real_distribution<RealType> 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 <class RealType = float>
+ RealType RandReal()
+ {
+ std::uniform_real_distribution<RealType> 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 <class Itr>
+ void generate(Itr first, Itr last)
+ {
+ std::random_device rd;
+ std::uniform_int_distribution<result_type> dist;
+ for (; first != last; ++first)
+ {
+ *first = dist(rd);
+ }
+ }
+};
+
+
+
+
+
+using cFastRandom = cRandomWrapper<std::minstd_rand>;
+using MTRand = cRandomWrapper<std::mt19937>;
+
+/** 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<int>(Random.NextFloat(static_cast<float>(Enchantability / 4))) + static_cast<int>(Random.NextFloat(static_cast<float>(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<int>(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<float>(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<short>(a_World->GetTickRandomNumber(50)))); // Fishing rod with durability. TODO: Enchantments on it
+ Drops.Add(cItem(E_ITEM_FISHING_ROD, 1, Random.RandInt<short>(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<short>(a_World->GetTickRandomNumber(64))));
+ Drops.Add(cItem(E_ITEM_BOW, 1, Random.RandInt<short>(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<int>(100 + static_cast<unsigned int>(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<int>(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<eMonsterType>::iterator itr = allowedMobs.begin();
- int iRandom = m_Random.NextInt(static_cast<int>(allowedMobsSize));
- for (int i = 0; i < iRandom; i++)
- {
- ++itr;
- }
+ std::advance(itr, GetRandomProvider().RandInt<size_t>(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<cMonster*> 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<double>(m_World->GetTickRandomNumber(10)) - 5.0;
- Dist.z = static_cast<double>(m_World->GetTickRandomNumber(10)) - 5.0;
+ Dist.x = static_cast<double>(Random.RandInt(-5, 5));
+ Dist.z = static_cast<double>(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<int>(static_cast<unsigned int>(r1.randInt()) % (a_Max + 1 - a_Min) + a_Min);
+ auto Count = GetRandomProvider().RandInt<char>(static_cast<char>(a_Min), static_cast<char>(a_Max));
if (Count > 0)
{
- a_Drops.push_back(cItem(a_Item, static_cast<char>(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<unsigned int>(static_cast<unsigned long>(r1.randInt()) % 200);
- if (Count < (5 + a_LootingLevel))
+ auto & r1 = GetRandomProvider();
+ if (r1.RandBool((5 + a_LootingLevel) / 200.0))
{
- size_t Rare = static_cast<size_t>(r1.randInt()) % a_Items.Size();
+ size_t Rare = r1.RandInt<size_t>(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<eRabbitType>(cFastRandom().NextInt(
- static_cast<UInt8>(eRabbitType::SaltAndPepper) + 1 // Max possible Rabbit-Type
+ cRabbit(static_cast<eRabbitType>(GetRandomProvider().RandInt<UInt8>(
+ static_cast<UInt8>(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<char>(NumDrops), static_cast<short>(m_WoolColor)));
+ char NumDrops = GetRandomProvider().RandInt<char>(1, 3);
+ Drops.emplace_back(E_BLOCK_WOOL, NumDrops, static_cast<short>(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<double>(-0.25, 0.25), Random.RandReal<double>(-0.25, 0.25), Random.RandReal<double>(-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<int>(GetPosX()) + X - 5, static_cast<int>(GetPosY()) + Y - 3, static_cast<int>(GetPosZ()) + Z - 5);
- MoveToPosition(Vector3f(static_cast<float>(m_CropsPos.x + 0.5), static_cast<float>(m_CropsPos.y), static_cast<float>(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<unsigned int>(1000000000U, 0x7fffffffU);
+ unsigned int r2 = rand.RandInt<unsigned int>(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<int>(floor(static_cast<float>(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<NIBBLETYPE>(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<NIBBLETYPE>(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<NIBBLETYPE>(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<NIBBLETYPE>(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<NIBBLETYPE>(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<NIBBLETYPE>(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<int>(r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3;
- int OfsY = static_cast<int>(r1.randInt(3) + r1.randInt(3)) - 3;
- int OfsZ = static_cast<int>(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<float>(a_FlyAwaySpeed * (GetTickRandomNumber(10) - 5));
- float SpeedY = static_cast<float>(a_FlyAwaySpeed * GetTickRandomNumber(50));
- float SpeedZ = static_cast<float>(a_FlyAwaySpeed * (GetTickRandomNumber(10) - 5));
+ float SpeedX = static_cast<float>(a_FlyAwaySpeed * Random.RandInt(-5, 5));
+ float SpeedY = static_cast<float>(a_FlyAwaySpeed * Random.RandInt(50));
+ float SpeedZ = static_cast<float>(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<AString::size_type, AString> 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<int>(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<int>(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;