1
0

Convert most calls to blocking GetHeight/GetBiomeAt to direct chunk accesses

* Hopefully fixes #5094
This commit is contained in:
Tiger Wang 2021-01-05 02:13:02 +00:00
parent eeb63b8901
commit 9328afe65c
14 changed files with 204 additions and 88 deletions

View File

@ -115,13 +115,14 @@ private:
/** Returns true if there's either a water source block close enough to hydrate the specified position, or it's raining there. */
static bool IsWaterInNear(const cChunk & a_Chunk, const Vector3i a_RelPos)
{
const auto WorldPos = a_Chunk.RelativeToAbsolute(a_RelPos);
if (a_Chunk.GetWorld()->IsWeatherWetAtXYZ(WorldPos))
if (a_Chunk.IsWeatherWetAt(a_RelPos.addedY(1)))
{
// Rain hydrates farmland, too
return true;
}
const auto WorldPos = a_Chunk.RelativeToAbsolute(a_RelPos);
// Search for water in a close proximity:
// Ref.: https://minecraft.gamepedia.com/Farmland#Hydration
// TODO: Rewrite this to use the chunk and its neighbors directly

View File

@ -626,7 +626,6 @@ void cChunk::SpawnMobs(cMobSpawner & a_MobSpawner)
int WorldX, WorldY, WorldZ;
PositionToWorldPosition(TryX, TryY, TryZ, WorldX, WorldY, WorldZ);
EMCSBiome Biome = m_ChunkMap->GetBiomeAt(WorldX, WorldZ);
// MG TODO :
// Moon cycle (for slime)
// check player and playerspawn presence < 24 blocks
@ -641,12 +640,16 @@ void cChunk::SpawnMobs(cMobSpawner & a_MobSpawner)
*/
NumberOfTries++;
if (!IsLightValid())
Vector3i Try(TryX, TryY, TryZ);
const auto Chunk = GetRelNeighborChunkAdjustCoords(Try);
if ((Chunk == nullptr) || !Chunk->IsValid() || !Chunk->IsLightValid())
{
continue;
}
auto newMob = a_MobSpawner.TryToSpawnHere(this, {TryX, TryY, TryZ}, Biome, MaxNbOfSuccess);
auto newMob = a_MobSpawner.TryToSpawnHere(this, Try, GetBiomeAt(Try.x, Try.z), MaxNbOfSuccess);
if (newMob == nullptr)
{
continue;
@ -1190,15 +1193,56 @@ bool cChunk::UnboundedRelFastSetBlock(Vector3i a_RelPos, BLOCKTYPE a_BlockType,
int cChunk::GetHeight(int a_X, int a_Z)
int cChunk::GetHeight(int a_X, int a_Z) const
{
ASSERT((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width));
return m_HeightMap[a_X + a_Z * Width];
}
if ((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width))
bool cChunk::IsWeatherSunnyAt(int a_RelX, int a_RelZ) const
{
return m_World->IsWeatherSunny() || IsBiomeNoDownfall(GetBiomeAt(a_RelX, a_RelZ));
}
bool cChunk::IsWeatherWetAt(const int a_RelX, const int a_RelZ) const
{
const auto Biome = GetBiomeAt(a_RelX, a_RelZ);
return m_World->IsWeatherWet() && !IsBiomeNoDownfall(Biome) && !IsBiomeCold(Biome);
}
bool cChunk::IsWeatherWetAt(const Vector3i a_Position) const
{
if ((a_Position.y < 0) || !IsWeatherWetAt(a_Position.x, a_Position.z))
{
return m_HeightMap[a_X + a_Z * Width];
return false;
}
return 0;
if (a_Position.y >= cChunkDef::Height)
{
return true;
}
for (int y = GetHeight(a_Position.x, a_Position.z); y >= a_Position.y; y--)
{
if (cBlockInfo::IsRainBlocker(GetBlock({ a_Position.x, y, a_Position.z })))
{
return false;
}
}
return true;
}

View File

@ -221,7 +221,17 @@ public:
/** Sets the sign text. Returns true if successful. Also sends update packets to all clients in the chunk */
bool SetSignLines(int a_RelX, int a_RelY, int a_RelZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4);
int GetHeight( int a_X, int a_Z);
int GetHeight( int a_X, int a_Z) const;
/** Returns true if it is sunny at the specified location. This takes into account biomes. */
bool IsWeatherSunnyAt(int a_RelX, int a_RelZ) const;
/** Returns true if it is raining or storming at the specified location, taking into account biomes. */
bool IsWeatherWetAt(int a_RelX, int a_RelZ) const;
/** Returns true if it is raining or storming at the specified location,
and the rain reaches (the bottom of) the specified block position. */
bool IsWeatherWetAt(Vector3i a_Position) const;
void SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client);

View File

@ -337,6 +337,63 @@ bool cChunkMap::IsChunkQueued(int a_ChunkX, int a_ChunkZ) const
bool cChunkMap::IsWeatherSunnyAt(int a_BlockX, int a_BlockZ) const
{
int ChunkX, ChunkZ, BlockY = 0;
cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSChunks);
const auto Chunk = FindChunk(ChunkX, ChunkZ);
if ((Chunk == nullptr) || !Chunk->IsValid())
{
return m_World->IsWeatherSunny();
}
return Chunk->IsWeatherSunnyAt(a_BlockX, a_BlockZ);
}
bool cChunkMap::IsWeatherWetAt(int a_BlockX, int a_BlockZ) const
{
int ChunkX, ChunkZ, BlockY = 0;
cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSChunks);
const auto Chunk = FindChunk(ChunkX, ChunkZ);
if ((Chunk == nullptr) || !Chunk->IsValid())
{
return m_World->IsWeatherWet();
}
return Chunk->IsWeatherWetAt(a_BlockX, a_BlockZ);
}
bool cChunkMap::IsWeatherWetAt(const Vector3i a_Position) const
{
const auto ChunkPosition = cChunkDef::BlockToChunk(a_Position);
const auto Position = cChunkDef::AbsoluteToRelative(a_Position, ChunkPosition);
cCSLock Lock(m_CSChunks);
const auto Chunk = FindChunk(ChunkPosition.m_ChunkX, ChunkPosition.m_ChunkZ);
if ((Chunk == nullptr) || !Chunk->IsValid())
{
return m_World->IsWeatherWet();
}
return Chunk->IsWeatherWetAt(Position);
}
bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkZ) const
{
cCSLock Lock(m_CSChunks);
@ -647,10 +704,7 @@ EMCSBiome cChunkMap::GetBiomeAt(int a_BlockX, int a_BlockZ) const
{
return Chunk->GetBiomeAt(X, Z);
}
else
{
return m_World->GetGenerator().GetBiomeAt(a_BlockX, a_BlockZ);
}
return EMCSBiome::biInvalidBiome;
}

View File

@ -5,9 +5,6 @@
#pragma once
#include <functional>
#include "ChunkDataCallback.h"
#include "EffectID.h"
#include "FunctionRef.h"
@ -120,6 +117,10 @@ public:
/** Returns true iff the chunk is in the loader / generator queue. */
bool IsChunkQueued(int a_ChunkX, int a_ChunkZ) const;
bool IsWeatherSunnyAt(int a_BlockX, int a_BlockZ) const;
bool IsWeatherWetAt(int a_BlockX, int a_BlockZ) const;
bool IsWeatherWetAt(Vector3i a_Position) const;
bool IsChunkValid (int a_ChunkX, int a_ChunkZ) const;
bool HasChunkAnyClients (int a_ChunkX, int a_ChunkZ) const;
int GetHeight (int a_BlockX, int a_BlockZ); // Waits for the chunk to get loaded / generated
@ -143,15 +144,15 @@ public:
Ignored if the chunk is invalid. */
void SetBlockMeta(Vector3i a_BlockPos, NIBBLETYPE a_BlockMeta);
void SetBlock (Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
bool GetBlockTypeMeta (Vector3i a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
bool GetBlockInfo (Vector3i, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) const;
void SetBlock (Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
bool GetBlockTypeMeta (Vector3i a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
bool GetBlockInfo (Vector3i, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) const;
/** Special function used for growing trees, replaces only blocks that tree may overwrite */
void ReplaceTreeBlocks(const sSetBlockVector & a_Blocks);
void ReplaceTreeBlocks(const sSetBlockVector & a_Blocks);
/** Returns the biome at the specified coords. Reads the biome from the chunk, if loaded, otherwise uses the world generator to provide the biome value */
EMCSBiome GetBiomeAt (int a_BlockX, int a_BlockZ) const;
/** Returns the biome at the specified coords. Reads the biome from the chunk, if loaded, invalid otherwise. */
EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ) const;
/** Sets the biome at the specified coords. Returns true if successful, false if not (chunk not loaded).
Doesn't resend the chunk to clients. */

View File

@ -1198,8 +1198,8 @@ void cEntity::TickBurning(cChunk & a_Chunk)
m_TicksLeftBurning = 0;
}
// Fire is extinguished by rain
if (GetWorld()->IsWeatherWetAtXYZ(GetPosition().Floor()))
// Fire is extinguished by rain:
if (a_Chunk.IsWeatherWetAt(cChunkDef::AbsoluteToRelative(GetPosition().Floor(), a_Chunk.GetPos())))
{
m_TicksLeftBurning = 0;
}

View File

@ -101,11 +101,16 @@ 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, POSZ_TOINT) == 0))
PREPARE_REL_AND_CHUNK(GetPosition().Floor(), a_Chunk);
if (!RelSuccess)
{
return;
}
auto & Random = GetRandomProvider();
if (IsBlockWater(Chunk->GetBlock(Rel)) && (Chunk->GetMeta(Rel) == 0))
{
if (!m_CanPickupItem && (m_AttachedMobID == cEntity::INVALID_ID)) // Check if you can't already pickup a fish and if the floater isn't attached to a mob.
{
@ -113,7 +118,7 @@ void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
m_BitePos = GetPosition();
m_World->BroadcastSoundEffect("entity.bobber.splash", GetPosition(), 1, 1);
SetPosY(GetPosY() - 1);
AddSpeedY(-10);
m_CanPickupItem = true;
m_PickupCountDown = 20;
m_CountDownTime = Random.RandInt(100, 900);
@ -132,9 +137,9 @@ void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
}
m_CountDownTime--;
if (m_World->GetHeight(POSX_TOINT, POSZ_TOINT) == POSY_TOINT)
if (Chunk->IsWeatherWetAt(Rel))
{
if (m_World->IsWeatherWet() && Random.RandBool(0.25)) // 25% chance of an extra countdown when being rained on.
if (Random.RandBool(0.25)) // 25% chance of an extra countdown when being rained on.
{
m_CountDownTime--;
}
@ -150,7 +155,10 @@ void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
}
// Check water at the top of floater otherwise it floats into the air above the water
if (IsBlockWater(m_World->GetBlock(POSX_TOINT, FloorC(GetPosY() + GetHeight()), POSZ_TOINT)))
if (
const auto Above = Rel.addedY(FloorC(GetPosY() + GetHeight()));
(Above.y < cChunkDef::Height) && IsBlockWater(m_World->GetBlock(Above))
)
{
SetSpeedY(0.7);
}

View File

@ -74,10 +74,6 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, Vector3i a_RelPos, eMonsterType a_MobType, EMCSBiome a_Biome, bool a_DisableSolidBelowCheck)
{
if (a_Chunk == nullptr)
{
return false;
}
if ((a_RelPos.y >= cChunkDef::Height - 1) || (a_RelPos.y <= 0))
{
return false;
@ -502,9 +498,6 @@ cMonster * cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, Vector3i a_RelPos, EMCS
m_NewPack = false;
}
// Make sure we are looking at the right chunk to spawn in
a_Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(a_RelPos);
if ((m_AllowedTypes.find(m_MobType) != m_AllowedTypes.end()) && CanSpawnHere(a_Chunk, a_RelPos, m_MobType, a_Biome))
{
auto NewMob = cMonster::NewMonsterFromType(m_MobType);

View File

@ -1,6 +1,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Chunk.h"
#include "Enderman.h"
#include "../Entities/Player.h"
#include "../LineBlockTracer.h"
@ -148,11 +149,14 @@ void cEnderman::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
return;
}
// Take damage when wet
if (
cChunkDef::IsValidHeight(POSY_TOINT) &&
(GetWorld()->IsWeatherWetAtXYZ(GetPosition().Floor()) || IsInWater())
)
PREPARE_REL_AND_CHUNK(GetPosition().Floor(), a_Chunk);
if (!RelSuccess)
{
return;
}
// Take damage when wet:
if (IsInWater() || Chunk->IsWeatherWetAt(Rel))
{
EventLosePlayer();
TakeDamage(dtEnvironment, nullptr, 1, 0);

View File

@ -1667,10 +1667,10 @@ bool cMonster::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk)
}
if (
(Chunk->GetBlock(Rel.x, Rel.y, Rel.z) != E_BLOCK_SOULSAND) && // Not on soulsand
(GetWorld()->GetTimeOfDay() < 12000 + 1000) && // Daytime
GetWorld()->IsWeatherSunnyAt(POSX_TOINT, POSZ_TOINT) && // Not raining
!IsInWater() // Isn't swimming
(Chunk->GetBlock(Rel) != E_BLOCK_SOULSAND) && // Not on soulsand
(GetWorld()->GetTimeOfDay() < 12000 + 1000) && // Daytime
Chunk->IsWeatherSunnyAt(Rel.x, Rel.z) && // Not raining
!IsInWater() // Isn't swimming
)
{
int MobHeight = CeilC(a_Location.y + GetHeight()) - 1; // The block Y coord of the mob's head

View File

@ -1,6 +1,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Chunk.h"
#include "SnowGolem.h"
#include "../BlockInfo.h"
#include "../World.h"
@ -36,17 +37,22 @@ void cSnowGolem::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
// The base class tick destroyed us
return;
}
if (IsBiomeNoDownfall(m_World->GetBiomeAt(POSX_TOINT, POSZ_TOINT)))
PREPARE_REL_AND_CHUNK(GetPosition().Floor(), a_Chunk);
if (!RelSuccess)
{
return;
}
if (IsBiomeNoDownfall(Chunk->GetBiomeAt(Rel.x, Rel.z)))
{
TakeDamage(dtEnvironment, nullptr, GetRawDamageAgainst(*this), GetKnockbackAmountAgainst(*this));
}
else
else if (const auto Below = Rel.addedY(-1); Below.y >= 0)
{
BLOCKTYPE BlockBelow = m_World->GetBlock(POSX_TOINT, POSY_TOINT - 1, POSZ_TOINT);
BLOCKTYPE Block = m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT);
if ((Block == E_BLOCK_AIR) && cBlockInfo::IsSolid(BlockBelow))
if ((Chunk->GetBlock(Rel) == E_BLOCK_AIR) && cBlockInfo::IsSolid(Chunk->GetBlock(Below)))
{
m_World->SetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT, E_BLOCK_SNOW, 0);
Chunk->SetBlock(Rel, E_BLOCK_SNOW, 0);
}
}
}

View File

@ -87,7 +87,6 @@ void cFireSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX,
for (cCoordWithIntList::iterator itr = Data.begin(); itr != Data.end();)
{
Vector3i relPos(itr->x, itr->y, itr->z);
auto absPos = a_Chunk->RelativeToAbsolute(relPos);
auto blockType = a_Chunk->GetBlock(relPos);
if (!IsAllowedBlock(blockType))
@ -101,12 +100,16 @@ void cFireSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX,
auto BurnsForever = ((relPos.y > 0) && DoesBurnForever(a_Chunk->GetBlock(relPos.addedY(-1))));
auto BlockMeta = a_Chunk->GetMeta(relPos);
auto Raining = std::any_of(std::begin(gCrossCoords), std::end(gCrossCoords),
[this, absPos](Vector3i cc)
auto Raining = std::any_of(std::begin(gCrossCoords), std::end(gCrossCoords), [a_Chunk, relPos](Vector3i cc)
{
auto Adjusted = relPos + cc;
const auto Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(Adjusted);
if ((Chunk != nullptr) && Chunk->IsValid())
{
return (m_World.IsWeatherWetAtXYZ(absPos + cc));
return Chunk->IsWeatherWetAt(Adjusted);
}
);
return false;
});
// Randomly burn out the fire if it is raining:
if (!BurnsForever && Raining && GetRandomProvider().RandBool(CHANCE_BASE_RAIN_EXTINGUISH + (BlockMeta * CHANCE_AGE_M_RAIN_EXTINGUISH)))

View File

@ -548,28 +548,27 @@ void cWorld::ChangeWeather(void)
bool cWorld::IsWeatherWetAtXYZ(Vector3i a_Pos)
bool cWorld::IsWeatherSunnyAt(int a_BlockX, int a_BlockZ) const
{
if ((a_Pos.y < 0) || !IsWeatherWetAt(a_Pos.x, a_Pos.z))
{
return false;
}
return m_ChunkMap.IsWeatherSunnyAt(a_BlockX, a_BlockZ);
}
if (a_Pos.y >= cChunkDef::Height)
{
return true;
}
for (int y = GetHeight(a_Pos.x, a_Pos.z); y >= a_Pos.y; y--)
{
auto BlockType = GetBlock({a_Pos.x, y, a_Pos.z});
if (cBlockInfo::IsRainBlocker(BlockType))
{
return false;
}
}
return true;
bool cWorld::IsWeatherWetAt(int a_BlockX, int a_BlockZ)
{
return m_ChunkMap.IsWeatherWetAt(a_BlockX, a_BlockZ);
}
bool cWorld::IsWeatherWetAtXYZ(const Vector3i a_Position)
{
return m_ChunkMap.IsWeatherWetAt(a_Position);
}

View File

@ -999,10 +999,7 @@ public:
bool IsWeatherSunny(void) const { return (m_Weather == wSunny); }
/** Returns true if it is sunny at the specified location. This takes into account biomes. */
bool IsWeatherSunnyAt(int a_BlockX, int a_BlockZ)
{
return (IsWeatherSunny() || IsBiomeNoDownfall(GetBiomeAt(a_BlockX, a_BlockZ)));
}
bool IsWeatherSunnyAt(int a_BlockX, int a_BlockZ) const;
/** Returns true if the current weather is rainy. */
bool IsWeatherRain(void) const { return (m_Weather == wRain); }
@ -1027,15 +1024,11 @@ public:
/** Returns true if it is raining or storming at the specified location.
This takes into account biomes. */
virtual bool IsWeatherWetAt(int a_BlockX, int a_BlockZ) override
{
auto Biome = GetBiomeAt(a_BlockX, a_BlockZ);
return (IsWeatherWet() && !IsBiomeNoDownfall(Biome) && !IsBiomeCold(Biome));
}
virtual bool IsWeatherWetAt(int a_BlockX, int a_BlockZ) override;
/** Returns true if it is raining or storming at the specified location,
and the rain reaches (the bottom of) the specified block position. */
virtual bool IsWeatherWetAtXYZ(Vector3i a_Pos) override;
virtual bool IsWeatherWetAtXYZ(Vector3i a_Position) override;
/** Returns the seed of the world. */
int GetSeed(void) { return m_Generator.GetSeed(); }