From 9d6a5b9ce0ea5fbbb377b6a950ec1f27ae6a1587 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Tue, 29 May 2012 14:59:43 +0000 Subject: Added code for the chunks to manipulate their neighbors while ticking. Also added some basic farming support - melon and pumpkin growing code. Untested and untestable so far, will test and fix later. git-svn-id: http://mc-server.googlecode.com/svn/trunk@518 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- items.ini | 3 + source/BlockID.h | 3 +- source/Defines.h | 114 ++++++++++++--- source/cChunk.cpp | 365 +++++++++++++++++++++++------------------------ source/cChunk.h | 22 +-- source/cChunkMap.cpp | 74 ++++++++-- source/cChunkMap.h | 12 +- source/cClientHandle.cpp | 69 +-------- source/cWorld.cpp | 96 ++++++++++--- source/cWorld.h | 8 +- 10 files changed, 442 insertions(+), 324 deletions(-) diff --git a/items.ini b/items.ini index 467345f04..d5d693c74 100644 --- a/items.ini +++ b/items.ini @@ -213,5 +213,8 @@ dye=351 inksac=351:0 cactusgreen=351:2 shears=359 +melonslice=360 +pumpkinseeds=361 +melonseeds=362 goldrecord=2256 greenrecord=2257 \ No newline at end of file diff --git a/source/BlockID.h b/source/BlockID.h index 2cb66776b..03a48491f 100644 --- a/source/BlockID.h +++ b/source/BlockID.h @@ -65,7 +65,8 @@ enum ENUM_BLOCK_ID E_BLOCK_DIAMOND_BLOCK = 57, E_BLOCK_WORKBENCH = 58, E_BLOCK_CROPS = 59, - E_BLOCK_SOIL = 60, + E_BLOCK_SOIL = 60, // Deprecated, use E_BLOCK_FARMLAND instead + E_BLOCK_FARMLAND = 60, E_BLOCK_FURNACE = 61, E_BLOCK_BURNING_FURNACE = 62, E_BLOCK_SIGN_POST = 63, diff --git a/source/Defines.h b/source/Defines.h index 2c9729223..5ec09fb1e 100644 --- a/source/Defines.h +++ b/source/Defines.h @@ -18,7 +18,7 @@ extern bool g_BlockOneHitDig[]; inline bool IsValidBlock( int a_BlockID ) //tolua_export { //tolua_export if( a_BlockID > -1 && - a_BlockID <= 121 && //items to 109 are valid for 1.8.1.. 1.9.5 is up to 121 + a_BlockID <= 126 && //items to 109 are valid for Beta1.8.1.. 1.2.5 is up to 126 //a_BlockID != 29 && allow pistons //a_BlockID != 33 && allow pistons a_BlockID != 34 && @@ -29,6 +29,10 @@ inline bool IsValidBlock( int a_BlockID ) //tolua_export return false; } //tolua_export + + + + // Was old :o // Changed to fit the style ;) inline bool IsValidItem( int a_ItemID ) //tolua_export @@ -45,16 +49,28 @@ inline bool IsValidItem( int a_ItemID ) //tolua_export return IsValidBlock( a_ItemID ); } //tolua_export + + + + inline bool IsBlockWater(char a_BlockID) { return (a_BlockID == E_BLOCK_WATER || a_BlockID == E_BLOCK_STATIONARY_WATER); } + + + + inline bool IsBlockLava(char a_BlockID) { return (a_BlockID == E_BLOCK_LAVA || a_BlockID == E_BLOCK_STATIONARY_LAVA); } + + + + inline void AddDirection( int & a_X, int & a_Y, int & a_Z, char a_Direction, bool a_bInverse = false ) { if( !a_bInverse ) @@ -107,6 +123,10 @@ inline void AddDirection( int & a_X, int & a_Y, int & a_Z, char a_Direction, boo } } + + + + inline void AddDirection( int & a_X, unsigned char & a_Y, int & a_Z, char a_Direction, bool a_bInverse = false ) //tolua_export {//tolua_export int Y = a_Y; @@ -116,6 +136,10 @@ inline void AddDirection( int & a_X, unsigned char & a_Y, int & a_Z, char a_Dire else a_Y = (unsigned char)Y; }//tolua_export + + + + #include #define PI 3.14159265358979323846264338327950288419716939937510582097494459072381640628620899862803482534211706798f #define MIN(a,b) (((a)>(b))?(b):(a)) @@ -130,6 +154,10 @@ inline void EulerToVector( float a_Pan, float a_Pitch, float & a_X, float & a_Y, a_Z = sin(a_Pitch / 180 * PI); } + + + + inline void VectorToEuler( float a_X, float a_Y, float a_Z, float & a_Pan, float & a_Pitch ) { if( a_X != 0 ) @@ -139,11 +167,19 @@ inline void VectorToEuler( float a_X, float a_Y, float a_Z, float & a_Pan, float a_Pitch = atan2(a_Y, sqrtf((a_X * a_X) + (a_Z * a_Z))) * 180 / PI; } + + + + inline float GetSignf( float a_Val ) { return (a_Val < 0.f)?-1.f:1.f; } + + + + inline float GetSpecialSignf( float a_Val ) { return (a_Val <= 0.f)?-1.f:1.f; @@ -151,36 +187,68 @@ inline float GetSpecialSignf( float a_Val ) + + namespace ItemCategory { inline bool IsPickaxe(ENUM_ITEM_ID a_ItemID) { - return a_ItemID == E_ITEM_WOODEN_PICKAXE - || a_ItemID == E_ITEM_STONE_PICKAXE - || a_ItemID == E_ITEM_IRON_PICKAXE - || a_ItemID == E_ITEM_GOLD_PICKAXE - || a_ItemID == E_ITEM_DIAMOND_PICKAXE; + return (a_ItemID == E_ITEM_WOODEN_PICKAXE) + || (a_ItemID == E_ITEM_STONE_PICKAXE) + || (a_ItemID == E_ITEM_IRON_PICKAXE) + || (a_ItemID == E_ITEM_GOLD_PICKAXE) + || (a_ItemID == E_ITEM_DIAMOND_PICKAXE); } + + inline bool IsAxe(ENUM_ITEM_ID a_ItemID) { - return a_ItemID == E_ITEM_WOODEN_AXE - || a_ItemID == E_ITEM_STONE_AXE - || a_ItemID == E_ITEM_IRON_AXE - || a_ItemID == E_ITEM_GOLD_AXE - || a_ItemID == E_ITEM_DIAMOND_AXE; + return (a_ItemID == E_ITEM_WOODEN_AXE) + || (a_ItemID == E_ITEM_STONE_AXE) + || (a_ItemID == E_ITEM_IRON_AXE) + || (a_ItemID == E_ITEM_GOLD_AXE) + || (a_ItemID == E_ITEM_DIAMOND_AXE); } + + inline bool IsSword(ENUM_ITEM_ID a_ItemID) { - return a_ItemID == E_ITEM_WOODEN_SWORD - || a_ItemID == E_ITEM_STONE_SWORD - || a_ItemID == E_ITEM_IRON_SWORD - || a_ItemID == E_ITEM_GOLD_SWORD - || a_ItemID == E_ITEM_DIAMOND_SWORD; + return (a_ItemID == E_ITEM_WOODEN_SWORD) + || (a_ItemID == E_ITEM_STONE_SWORD) + || (a_ItemID == E_ITEM_IRON_SWORD) + || (a_ItemID == E_ITEM_GOLD_SWORD) + || (a_ItemID == E_ITEM_DIAMOND_SWORD); + } + + + + inline bool IsHoe(ENUM_ITEM_ID a_ItemID) + { + return (a_ItemID == E_ITEM_WOODEN_HOE) + || (a_ItemID == E_ITEM_STONE_HOE) + || (a_ItemID == E_ITEM_IRON_HOE) + || (a_ItemID == E_ITEM_GOLD_HOE) + || (a_ItemID == E_ITEM_DIAMOND_HOE); + } + + + + inline bool IsShovel(ENUM_ITEM_ID a_ItemID) + { + return (a_ItemID == E_ITEM_WOODEN_SHOVEL) + || (a_ItemID == E_ITEM_STONE_SHOVEL) + || (a_ItemID == E_ITEM_IRON_SHOVEL) + || (a_ItemID == E_ITEM_GOLD_SHOVEL) + || (a_ItemID == E_ITEM_DIAMOND_SHOVEL); } } + + + + //tolua_begin enum eGameMode { @@ -188,6 +256,10 @@ enum eGameMode eGameMode_Creative = 1, }; + + + + enum eWeather { eWeather_Sunny = 0, @@ -195,4 +267,12 @@ enum eWeather eWeather_ThunderStorm = 2, }; -//tolua_end \ No newline at end of file + + + + +//tolua_end + + + + diff --git a/source/cChunk.cpp b/source/cChunk.cpp index 90d94a1ce..ff8badc19 100644 --- a/source/cChunk.cpp +++ b/source/cChunk.cpp @@ -542,6 +542,16 @@ void cChunk::Tick(float a_Dt, MTRand & a_TickRandom) void cChunk::TickBlocks(MTRand & a_TickRandom) { // Tick dem blocks + /* + // DEBUG: + int RandomX = 0; + int RandomY = 1; + int RandomZ = 0; + m_BlockTickX = 0; + m_BlockTickY = 40; + m_BlockTickZ = 0; + */ + int RandomX = a_TickRandom.randInt(); int RandomY = a_TickRandom.randInt(); int RandomZ = a_TickRandom.randInt(); @@ -552,41 +562,43 @@ void cChunk::TickBlocks(MTRand & a_TickRandom) m_BlockTickY = (m_BlockTickY + RandomY) % Height; m_BlockTickZ = (m_BlockTickZ + RandomZ) % Width; - if( m_BlockTickY > m_HeightMap[ m_BlockTickX + m_BlockTickZ * Width ] ) continue; // It's all air up here + if (m_BlockTickY > m_HeightMap[ m_BlockTickX + m_BlockTickZ * Width]) + { + continue; // It's all air up here + } unsigned int Index = MakeIndexNoCheck( m_BlockTickX, m_BlockTickY, m_BlockTickZ ); - char ID = m_BlockTypes[Index]; + BLOCKTYPE ID = m_BlockTypes[Index]; switch( ID ) { - /* - // TODO: re-enable - case E_BLOCK_DIRT: + case E_BLOCK_GRASS: { - char AboveBlock = GetBlock( Index+1 ); - if ( (AboveBlock == 0) && GetNibble( m_BlockSkyLight, Index ) > 0xf/2 ) // Half lit //changed to not allow grass if any one hit object is on top - { - FastSetBlock( m_BlockTickX, m_BlockTickY, m_BlockTickZ, E_BLOCK_GRASS, GetNibble( m_BlockMeta, Index ) ); - } - if ( (g_BlockOneHitDig[AboveBlock]) && GetNibble( m_BlockSkyLight, Index+1 ) > 0xf/2 ) // Half lit //ch$ + // Grass turns into dirt if there's another block on top of it: + BLOCKTYPE AboveBlock = GetBlock(Index + (Width * Width) ); + if (!( (AboveBlock == E_BLOCK_AIR) || (g_BlockOneHitDig[AboveBlock]) || (g_BlockTransparent[AboveBlock]) ) ) { - FastSetBlock( m_BlockTickX, m_BlockTickY, m_BlockTickZ, E_BLOCK_GRASS, GetNibble( m_BlockMeta, Index ) ); + FastSetBlock( m_BlockTickX, m_BlockTickY, m_BlockTickZ, E_BLOCK_DIRT, GetNibble( m_BlockMeta, Index ) ); } - + + // TODO: Grass spreads to nearby blocks if there's enough light and free space above that block + // Ref.: http://www.minecraftwiki.net/wiki/Grass_Block#Growth break; } - */ - case E_BLOCK_GRASS: + case E_BLOCK_CROPS: { - char AboveBlock = GetBlock( Index + (Width * Width) ); - if (!( (AboveBlock == E_BLOCK_AIR) || (g_BlockOneHitDig[AboveBlock]) || (g_BlockTransparent[AboveBlock]) ) ) + NIBBLETYPE Meta = GetMeta(Index); + if (Meta < 7) { - FastSetBlock( m_BlockTickX, m_BlockTickY, m_BlockTickZ, E_BLOCK_DIRT, GetNibble( m_BlockMeta, Index ) ); + FastSetBlock(m_BlockTickX, m_BlockTickY, m_BlockTickZ, E_BLOCK_CROPS, ++Meta); } break; } - case E_BLOCK_SAPLING: //todo: check meta of sapling. change m_World->GrowTree to look change trunk and leaves based on meta of sapling + case E_BLOCK_PUMPKIN_STEM: + case E_BLOCK_MELON_STEM: TickMelonPumpkin(m_BlockTickX, m_BlockTickY, m_BlockTickZ, Index, ID, a_TickRandom); break; + + case E_BLOCK_SAPLING: { // Check the highest bit, if set, grow the tree, if not, set it (1-bit delay): NIBBLETYPE Meta = GetMeta(m_BlockTickX, m_BlockTickY, m_BlockTickZ); @@ -618,6 +630,151 @@ void cChunk::TickBlocks(MTRand & a_TickRandom) +void cChunk::TickMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, int a_BlockIdx, BLOCKTYPE a_BlockType, MTRand & a_TickRandom) +{ + NIBBLETYPE Meta = GetMeta(a_BlockIdx); + if (Meta < 7) + { + FastSetBlock(m_BlockTickX, m_BlockTickY, m_BlockTickZ, a_BlockType, ++Meta); + return; + } + + // Convert the stem BlockType into produce BlockType + BLOCKTYPE ProduceType; + switch (a_BlockType) + { + case E_BLOCK_MELON_STEM: ProduceType = E_BLOCK_MELON; break; + case E_BLOCK_PUMPKIN_STEM: ProduceType = E_BLOCK_PUMPKIN; break; + default: + { + ASSERT(!"Unhandled blocktype in TickMelonPumpkin()"); + return; + } + } + + // Check if there's another melon / pumpkin around that stem, if so, abort: + bool IsValid; + BLOCKTYPE BlockType[4]; + NIBBLETYPE BlockMeta; // unused + IsValid = UnboundedRelGetBlock(a_RelX + 1, a_RelY, a_RelZ, BlockType[0], BlockMeta); + IsValid = IsValid && UnboundedRelGetBlock(a_RelX - 1, a_RelY, a_RelZ, BlockType[1], BlockMeta); + IsValid = IsValid && UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ + 1, BlockType[2], BlockMeta); + IsValid = IsValid && UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ - 1, BlockType[3], BlockMeta); + if ( + !IsValid || + (BlockType[0] == ProduceType) || + (BlockType[1] == ProduceType) || + (BlockType[2] == ProduceType) || + (BlockType[3] == ProduceType) + ) + { + // Neighbors not valid or already taken by the same produce + return; + } + + // Pick a direction in which to place the produce: + int x = 0, z = 0; + int CheckType = a_TickRandom.randInt(4); // The index to the neighbors array which should be checked for emptiness + switch (CheckType) + { + case 0: x = 1; break; + case 1: x = -1; break; + case 2: z = 1; break; + case 3: z = -1; break; + } + + // Check that the block in that direction is empty: + switch (BlockType[CheckType]) + { + case E_BLOCK_AIR: + case E_BLOCK_SNOW: + case E_BLOCK_TALL_GRASS: + case E_BLOCK_DEAD_BUSH: + { + break; + } + default: return; + } + + // Check if there's soil under the neighbor. We already know the neighbors are valid. Place produce if ok + BLOCKTYPE Soil; + UnboundedRelGetBlock(a_RelX + x, a_RelY, a_RelZ + z, Soil, BlockMeta); + switch (Soil) + { + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_FARMLAND: + { + // Place a randomly-facing produce: + UnboundedRelFastSetBlock(a_RelX + x, a_RelY, a_RelZ + z, ProduceType, (NIBBLETYPE)(a_TickRandom.randInt(4) % 4)); + break; + } + } +} + + + + + +bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + if ((a_RelX >= 0) && (a_RelX <= cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ <= cChunkDef::Width)) + { + int BlockIdx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); + a_BlockType = GetBlock(BlockIdx); + a_BlockMeta = GetMeta(BlockIdx); + return true; + } + return m_ChunkMap->LockedGetBlock( + m_PosX * cChunkDef::Width + a_RelX, + ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, + m_PosZ * cChunkDef::Width + a_RelZ, + a_BlockType, a_BlockMeta + ); +} + + + + + +bool cChunk::UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + if ((a_RelX >= 0) && (a_RelX <= cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ <= cChunkDef::Width)) + { + SetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + return true; + } + return m_ChunkMap->LockedSetBlock( + m_PosX * cChunkDef::Width + a_RelX, + ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, + m_PosZ * cChunkDef::Width + a_RelZ, + a_BlockType, a_BlockMeta + ); +} + + + + + +bool cChunk::UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + if ((a_RelX >= 0) && (a_RelX <= cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ <= cChunkDef::Width)) + { + FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + return true; + } + return m_ChunkMap->LockedFastSetBlock( + m_PosX * cChunkDef::Width + a_RelX, + ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, + m_PosZ * cChunkDef::Width + a_RelZ, + a_BlockType, a_BlockMeta + ); +} + + + + + int cChunk::GetHeight( int a_X, int a_Z ) { ASSERT((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width)); @@ -704,176 +861,6 @@ void cChunk::CalculateHeightmap() -void cChunk::CalculateLighting() -{ - // Calculate sunlight - memset(m_BlockSkyLight, 0xff, NumBlocks / 2 ); // Set all to fully lit, so everything above HeightMap is lit - for (int x = 0; x < Width; x++) - { - for (int z = 0; z < Width; z++) - { - char sunlight = 0xf; - for (int y = m_HeightMap[x + z*Width]; y > -1; y--) - { - int index = MakeIndexNoCheck(x, y, z); - - if( g_BlockTransparent[ (int)m_BlockTypes[index] ] == false ) - { - sunlight = 0x0; - } - SetNibble( m_BlockSkyLight, index, sunlight ); - } - } - } - - // Calculate blocklights - for (int x = 0; x < Width; x++) - { - for (int z = 0; z < Width; z++) - { - int MaxHeight = m_HeightMap[x + z * Width]; - for (int y = 0; y < MaxHeight; y++) - { - char BlockID = GetBlock(x, y, z); - SetNibble( m_BlockLight, x, y, z, g_BlockLightValue[(int)BlockID] ); - } - } - } - - SpreadLight(m_BlockSkyLight); - SpreadLight(m_BlockLight); - - MarkDirty(); -} - - - - - -void cChunk::SpreadLight(NIBBLETYPE * a_LightBuffer) -{ - // Spread the light - for(int x = 0; x < Width; x++) for(int z = 0; z < Width; z++) for(int y = 0; y < Height; y++) - { - int index = MakeIndexNoCheck(x, y, z); - if( g_BlockSpreadLightFalloff[ m_BlockTypes[index] ] > 0 ) - { - SpreadLightOfBlock(a_LightBuffer, x, y, z, g_BlockSpreadLightFalloff[ m_BlockTypes[index] ]); - } - } - - for(int x = Width-1; x > -1; x--) for(int z = Width-1; z > -1; z--) for(int y = Height-1; y > -1; y--) - { - int index = MakeIndexNoCheck(x, y, z); - if( g_BlockSpreadLightFalloff[ m_BlockTypes[index] ] > 0 ) - { - SpreadLightOfBlock(a_LightBuffer, x, y, z, g_BlockSpreadLightFalloff[ m_BlockTypes[index] ]); - } - } - - bool bCalcLeft, bCalcRight, bCalcFront, bCalcBack; - bCalcLeft = bCalcRight = bCalcFront = bCalcBack = false; - - // Spread to neighbour chunks X-axis - cChunkPtr LeftChunk = m_ChunkMap->GetChunkNoGen( m_PosX - 1, m_PosY, m_PosZ ); - cChunkPtr RightChunk = m_ChunkMap->GetChunkNoGen( m_PosX + 1, m_PosY, m_PosZ ); - NIBBLETYPE * LeftSky = NULL, * RightSky = NULL; - if (LeftChunk->IsValid()) - { - LeftSky = (a_LightBuffer == m_BlockSkyLight) ? LeftChunk->m_BlockSkyLight : LeftChunk->m_BlockLight; - } - if (RightChunk->IsValid()) - { - RightSky = (a_LightBuffer == m_BlockSkyLight) ? RightChunk->m_BlockSkyLight : RightChunk->m_BlockLight; - } - - for (int z = 0; z < Width; z++) for(int y = 0; y < Height; y++) - { - if (LeftSky != NULL) - { - int index = MakeIndexNoCheck( 0, y, z ); - if( g_BlockSpreadLightFalloff[ m_BlockTypes[index] ] > 0 ) - { - BLOCKTYPE CurrentLight = GetNibble( a_LightBuffer, 0, y, z ); - BLOCKTYPE LeftLight = GetNibble( LeftSky, Width-1, y, z ); - if( LeftLight < CurrentLight-g_BlockSpreadLightFalloff[ m_BlockTypes[index] ] ) - { - SetNibble( LeftSky, Width - 1, y, z, MAX(0, CurrentLight-g_BlockSpreadLightFalloff[ m_BlockTypes[index] ]) ); - bCalcLeft = true; - } - } - } - if (RightSky != NULL) - { - int index = MakeIndexNoCheck( Width - 1, y, z ); - if( g_BlockSpreadLightFalloff[ m_BlockTypes[index] ] > 0 ) - { - BLOCKTYPE CurrentLight = GetNibble( a_LightBuffer, Width-1, y, z ); - BLOCKTYPE RightLight = GetNibble( RightSky, 0, y, z ); - if( RightLight < CurrentLight-g_BlockSpreadLightFalloff[ m_BlockTypes[index] ] ) - { - SetNibble( RightSky, 0, y, z, MAX(0, CurrentLight-g_BlockSpreadLightFalloff[ m_BlockTypes[index] ]) ); - bCalcRight = true; - } - } - } - } - - // Spread to neighbour chunks Z-axis - cChunkPtr FrontChunk = m_ChunkMap->GetChunkNoGen( m_PosX, m_PosY, m_PosZ - 1 ); - cChunkPtr BackChunk = m_ChunkMap->GetChunkNoGen( m_PosX, m_PosY, m_PosZ + 1 ); - NIBBLETYPE * FrontSky = NULL, * BackSky = NULL; - if (FrontChunk->IsValid()) - { - FrontSky = (a_LightBuffer == m_BlockSkyLight) ? FrontChunk->m_BlockSkyLight : FrontChunk->m_BlockLight; - } - if (BackChunk->IsValid()) - { - BackSky = (a_LightBuffer == m_BlockSkyLight) ? BackChunk->m_BlockSkyLight : BackChunk->m_BlockLight; - } - for(int x = 0; x < Width; x++) for(int y = 0; y < Height; y++) - { - if (FrontSky != NULL) - { - int index = MakeIndexNoCheck( x, y, 0 ); - if( g_BlockSpreadLightFalloff[ m_BlockTypes[index] ] > 0 ) - { - BLOCKTYPE CurrentLight = GetNibble( a_LightBuffer, x, y, 0 ); - BLOCKTYPE FrontLight = GetNibble( FrontSky, x, y, Width-1 ); - if( FrontLight < CurrentLight-g_BlockSpreadLightFalloff[ m_BlockTypes[index] ] ) - { - SetNibble( FrontSky, x, y, Width-1, MAX(0, CurrentLight-g_BlockSpreadLightFalloff[ m_BlockTypes[index] ]) ); - bCalcFront = true; - } - } - } - if (BackSky != NULL) - { - int index = MakeIndexNoCheck( x, y, Width-1 ); - if( g_BlockSpreadLightFalloff[ m_BlockTypes[index] ] > 0 ) - { - BLOCKTYPE CurrentLight = GetNibble( a_LightBuffer, x, y, Width-1 ); - BLOCKTYPE BackLight = GetNibble( BackSky, x, y, 0 ); - if ( BackLight < CurrentLight-g_BlockSpreadLightFalloff[ m_BlockTypes[index] ] ) - { - SetNibble( BackSky, x, y, 0, MAX(0, CurrentLight-g_BlockSpreadLightFalloff[ m_BlockTypes[index] ]) ); - bCalcBack = true; - } - } - } - } - - if ( bCalcLeft ) m_World->ReSpreadLighting( m_PosX - 1, m_PosY, m_PosZ ); - if ( bCalcRight ) m_World->ReSpreadLighting( m_PosX + 1, m_PosY, m_PosZ ); - if ( bCalcFront ) m_World->ReSpreadLighting( m_PosX, m_PosY, m_PosZ - 1 ); - if ( bCalcBack ) m_World->ReSpreadLighting( m_PosX, m_PosY, m_PosZ + 1 ); - // No need to set those neighbors dirty, they will recalc their light anyway so they'll get marked dirty there -} - - - - - void cChunk::SetBlock( int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) { if (a_RelX < 0 || a_RelX >= Width || a_RelY < 0 || a_RelY >= Height || a_RelZ < 0 || a_RelZ >= Width) diff --git a/source/cChunk.h b/source/cChunk.h index 5b3021397..860c5c133 100644 --- a/source/cChunk.h +++ b/source/cChunk.h @@ -108,7 +108,6 @@ public: void Stay(bool a_Stay = true); void Tick(float a_Dt, MTRand & a_TickRandom); - void TickBlocks(MTRand & a_TickRandom); int GetPosX() { return m_PosX; } int GetPosY() { return m_PosY; } @@ -172,12 +171,9 @@ public: m_IsSaving = false; } - inline void SpreadBlockSkyLight(void) {SpreadLight(m_BlockSkyLight); } - inline void SpreadBlockLight (void) {SpreadLight(m_BlockLight); } - - inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) {return cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); } - inline NIBBLETYPE GetMeta(int a_BlockIdx) {return cChunkDef::GetNibble(m_BlockMeta, a_BlockIdx); } - inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ, a_Meta); } + inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) {return cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); } + inline NIBBLETYPE GetMeta(int a_BlockIdx) {return cChunkDef::GetNibble(m_BlockMeta, a_BlockIdx); } + inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ, a_Meta); } inline NIBBLETYPE GetLight(int a_RelX, int a_RelY, int a_RelZ) {return cChunkDef::GetNibble(m_BlockLight, a_RelX, a_RelY, a_RelZ); } inline NIBBLETYPE GetSkyLight(int a_RelX, int a_RelY, int a_RelZ) {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ); } @@ -233,7 +229,17 @@ private: // Makes a copy of the list cClientHandleList GetAllClients(void) const {return m_LoadedByClient; } - void SpreadLight(NIBBLETYPE * a_LightBuffer); + void TickBlocks(MTRand & a_TickRandom); + void TickMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, int a_BlockIdx, BLOCKTYPE a_BlockType, MTRand & a_TickRandom); + + /// Same as GetBlock(), but relative coords needn't be in this chunk (uses m_ChunkMap in such a case); returns true on success; only usable in Tick() + bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); + + /// Same as SetBlock(), but relative coords needn't be in this chunk (uses m_ChunkMap in such a case); returns true on success; only usable in Tick() + bool UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_ChunkMap in such a case); returns true on success; only usable in Tick() + bool UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); }; typedef cChunk * cChunkPtr; diff --git a/source/cChunkMap.cpp b/source/cChunkMap.cpp index bddec5656..546e6f4c3 100644 --- a/source/cChunkMap.cpp +++ b/source/cChunkMap.cpp @@ -176,6 +176,65 @@ cChunkPtr cChunkMap::GetChunkNoLoad( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) +bool cChunkMap::LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + // We already have m_CSLayers locked since this can be called only from within the tick thread + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return false; + } + + int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ); + a_BlockType = Chunk->GetBlock(Index); + a_BlockMeta = Chunk->GetMeta(Index); + return true; +} + + + + + +bool cChunkMap::LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + // We already have m_CSLayers locked since this can be called only from within the tick thread + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return false; + } + + Chunk->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); + return true; +} + + + + + +bool cChunkMap::LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + // We already have m_CSLayers locked since this can be called only from within the tick thread + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return false; + } + + Chunk->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); + return true; +} + + + + + void cChunkMap::BroadcastToChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, const cPacket & a_Packet, cClientHandle * a_Exclude) { // Broadcasts a_Packet to all clients in the chunk where block [x, y, z] is, except to client a_Exclude @@ -399,21 +458,6 @@ bool cChunkMap::HasChunkAnyClients(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -void cChunkMap::SpreadChunkLighting(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); - if ((Chunk != NULL) && Chunk->IsValid()) - { - Chunk->SpreadBlockSkyLight(); - Chunk->SpreadBlockLight(); - } -} - - - - - int cChunkMap::GetHeight(int a_BlockX, int a_BlockZ) { cCSLock Lock(m_CSLayers); diff --git a/source/cChunkMap.h b/source/cChunkMap.h index 716f5ac93..4b381296d 100644 --- a/source/cChunkMap.h +++ b/source/cChunkMap.h @@ -84,7 +84,6 @@ public: bool IsChunkValid (int a_ChunkX, int a_ChunkY, int a_ChunkZ); bool HasChunkAnyClients (int a_ChunkX, int a_ChunkY, int a_ChunkZ); - void SpreadChunkLighting(int a_ChunkX, int a_ChunkY, int a_ChunkZ); int GetHeight (int a_BlockX, int a_BlockZ); void FastSetBlocks (sSetBlockList & a_BlockList); void CollectPickupsByPlayer(cPlayer * a_Player); @@ -164,7 +163,7 @@ public: private: - friend class cChunk; // Temporary (until we have a separate Lighting thread), so that cChunk's lighting calc can ask for neighbor chunks + friend class cChunk; // The chunks can manipulate neighbors while in their Tick() method, using LockedGetBlock() and LockedSetBlock() class cChunkLayer { @@ -215,6 +214,15 @@ private: cChunkPtr GetChunk (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading / generating if not valid cChunkPtr GetChunkNoGen (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading if not valid; doesn't generate cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Doesn't load, doesn't generate + + /// Gets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) + bool LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); + + /// Sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) + bool LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Fast-sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) + bool LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); }; diff --git a/source/cClientHandle.cpp b/source/cClientHandle.cpp index f64f1fe44..9920b68f8 100644 --- a/source/cClientHandle.cpp +++ b/source/cClientHandle.cpp @@ -903,7 +903,9 @@ void cClientHandle::HandleBlockPlace(cPacket_BlockPlace * a_Packet) if ((Equipped.m_ItemID != a_Packet->m_ItemType)) // Not valid { - LOGWARN("Player %s tried to place a block that was not selected! (could indicate bot)", m_Username.c_str()); + LOGWARN("Player %s tried to place a block that was not equipped (exp %d, got %d)", + m_Username.c_str(), Equipped.m_ItemID, a_Packet->m_ItemType + ); // TODO: We should probably send the current world block to the client, so that it can immediately "let the user know" that they haven't placed the block return; } @@ -1311,70 +1313,7 @@ void cClientHandle::HandleBlockPlace(cPacket_BlockPlace * a_Packet) } // Check whether selected item is allowed to be placed on specific surface - bool bIllegalSurface = false; - ENUM_BLOCK_ID SurfaceBlock = (ENUM_BLOCK_ID)m_Player->GetWorld()->GetBlock(X, Y-1, Z); - switch (a_Packet->m_ItemType) - { - case E_BLOCK_YELLOW_FLOWER: // Can ONLY be placed on dirt/grass - case E_BLOCK_RED_ROSE: - case E_BLOCK_SAPLING: - { - switch (SurfaceBlock) - { - case E_BLOCK_DIRT: - case E_BLOCK_GRASS: - { - bIllegalSurface = false; - break; - } - default: - { - bIllegalSurface = true; - break; - } - }; - break; - } - - case E_BLOCK_BROWN_MUSHROOM: // Can be placed on pretty much anything, with exceptions - case E_BLOCK_RED_MUSHROOM: - { - switch (SurfaceBlock) - { - case E_BLOCK_GLASS: - case E_BLOCK_YELLOW_FLOWER: - case E_BLOCK_RED_ROSE: - case E_BLOCK_BROWN_MUSHROOM: - case E_BLOCK_RED_MUSHROOM: - case E_BLOCK_CACTUS: - { - bIllegalSurface = true; - break; - } - } - break; - } - - case E_BLOCK_CACTUS: - { - bIllegalSurface = (SurfaceBlock != E_BLOCK_SAND) && (SurfaceBlock != E_BLOCK_CACTUS); // Cactus can only be placed on sand and itself - - // Check surroundings. Cacti may ONLY be surrounded by air - cWorld * World = m_Player->GetWorld(); - if ( - (World->GetBlock(X - 1, Y, Z) != E_BLOCK_AIR) || - (World->GetBlock(X + 1, Y, Z) != E_BLOCK_AIR) || - (World->GetBlock(X, Y, Z - 1) != E_BLOCK_AIR) || - (World->GetBlock(X, Y, Z + 1) != E_BLOCK_AIR) - ) - { - bIllegalSurface = true; - } - break; - } - } // switch (a_Packet->m_ItemType) - - if (bIllegalSurface) + if (!m_Player->GetWorld()->IsPlacingItemLegal(a_Packet->m_ItemType, X, Y, Z)) { return; } diff --git a/source/cWorld.cpp b/source/cWorld.cpp index 9abfb963e..d04ac1af5 100644 --- a/source/cWorld.cpp +++ b/source/cWorld.cpp @@ -360,6 +360,81 @@ void cWorld::CastThunderbolt ( int a_X, int a_Y, int a_Z ) +bool cWorld::IsPlacingItemLegal(Int16 a_ItemType, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + BLOCKTYPE SurfaceBlock = GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); + switch (a_ItemType) + { + case E_BLOCK_YELLOW_FLOWER: // Can ONLY be placed on dirt/grass + case E_BLOCK_RED_ROSE: + case E_BLOCK_SAPLING: + { + switch (SurfaceBlock) + { + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_FARMLAND: + { + return true; + } + } + return false; + } + + case E_BLOCK_BROWN_MUSHROOM: // Can be placed on pretty much anything, with exceptions + case E_BLOCK_RED_MUSHROOM: + { + switch (SurfaceBlock) + { + case E_BLOCK_GLASS: + case E_BLOCK_YELLOW_FLOWER: + case E_BLOCK_RED_ROSE: + case E_BLOCK_BROWN_MUSHROOM: + case E_BLOCK_RED_MUSHROOM: + case E_BLOCK_CACTUS: + { + return false; + } + } + return true; + } + + case E_BLOCK_CACTUS: + { + if ((SurfaceBlock != E_BLOCK_SAND) && (SurfaceBlock != E_BLOCK_CACTUS)) + { + // Cactus can only be placed on sand and itself + return false; + } + + // Check surroundings. Cacti may ONLY be surrounded by air + if ( + (GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) != E_BLOCK_AIR) || + (GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) != E_BLOCK_AIR) || + (GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) != E_BLOCK_AIR) || + (GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) != E_BLOCK_AIR) + ) + { + return false; + } + return true; + } + + case E_ITEM_SEEDS: + case E_ITEM_MELON_SEEDS: + case E_ITEM_PUMPKIN_SEEDS: + { + // Seeds can go only on the farmland block: + return (SurfaceBlock == E_BLOCK_FARMLAND); + } + } // switch (a_Packet->m_ItemType) + return true; +} + + + + + void cWorld::InitializeSpawn(void) { int ChunkX = 0, ChunkY = 0, ChunkZ = 0; @@ -1463,27 +1538,6 @@ void cWorld::SaveAllChunks(void) -void cWorld::ReSpreadLighting(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cCSLock Lock(m_CSLighting); - m_SpreadQueue.remove(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); - m_SpreadQueue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); -} - - - - - -void cWorld::RemoveSpread(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cCSLock Lock(m_CSLighting); - m_SpreadQueue.remove(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); -} - - - - - /************************************************************************/ /* Get and set */ /************************************************************************/ diff --git a/source/cWorld.h b/source/cWorld.h index e030f3570..5e448d79b 100644 --- a/source/cWorld.h +++ b/source/cWorld.h @@ -278,9 +278,6 @@ public: void Tick(float a_Dt); - void ReSpreadLighting(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - void RemoveSpread(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - void InitializeSpawn(); void CastThunderbolt (int a_X, int a_Y, int a_Z); //tolua_export @@ -291,6 +288,8 @@ public: cWorldStorage & GetStorage (void) { return m_Storage; } cChunkMap * GetChunkMap (void) { return m_ChunkMap; } + bool IsPlacingItemLegal(Int16 a_ItemType, int a_BlockX, int a_BlockY, int a_BlockZ); + private: friend class cRoot; @@ -344,9 +343,6 @@ private: cClientHandleList m_Clients; cPlayerList m_Players; - cCriticalSection m_CSLighting; - cChunkCoordsList m_SpreadQueue; - cCriticalSection m_CSFastSetBlock; sSetBlockList m_FastSetBlockQueue; -- cgit v1.2.3