2011-12-26 18:23:05 -05:00
2012-01-29 14:28:19 -05:00
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2011-10-03 14:41:19 -04:00
# ifndef _WIN32
2012-02-01 07:46:44 -05:00
# include <cstdlib>
2011-10-03 14:41:19 -04:00
# endif
2011-12-22 16:36:24 -05:00
2011-10-03 14:41:19 -04:00
# include "cChunk.h"
# include "cWorld.h"
2011-12-22 16:36:24 -05:00
# include "cWaterSimulator.h"
# include "cLavaSimulator.h"
2011-10-03 14:41:19 -04:00
# include "cClientHandle.h"
# include "cServer.h"
# include "zlib.h"
# include "Defines.h"
# include "cChestEntity.h"
# include "cFurnaceEntity.h"
# include "cSignEntity.h"
# include "cTorch.h"
# include "cLadder.h"
# include "cPickup.h"
2011-11-07 17:59:29 -05:00
# include "cRedstone.h"
2011-10-03 14:41:19 -04:00
# include "cItem.h"
# include "cNoise.h"
# include "cRoot.h"
2011-11-07 17:59:29 -05:00
# include "cBlockToPickup.h"
2011-12-26 04:09:47 -05:00
# include "MersenneTwister.h"
2012-02-13 16:47:03 -05:00
# include "cPlayer.h"
2011-10-03 14:41:19 -04:00
# include "packets/cPacket_DestroyEntity.h"
# include "packets/cPacket_PreChunk.h"
# include "packets/cPacket_BlockChange.h"
# include "packets/cPacket_MultiBlock.h"
# include <json/json.h>
2012-01-29 14:28:19 -05:00
2011-10-21 17:25:29 -04:00
2011-10-03 14:41:19 -04:00
extern bool g_bWaterPhysics ;
2012-01-29 14:28:19 -05:00
2012-01-30 11:01:45 -05:00
2012-02-18 15:10:57 -05:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// sSetBlock:
2012-05-25 03:18:52 -04:00
sSetBlock : : sSetBlock ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta ) // absolute block position
: x ( a_BlockX )
, y ( a_BlockY )
, z ( a_BlockZ )
2012-02-18 15:10:57 -05:00
, BlockType ( a_BlockType )
, BlockMeta ( a_BlockMeta )
{
2012-03-14 16:56:09 -04:00
cChunkDef : : AbsoluteToRelative ( x , y , z , ChunkX , ChunkZ ) ;
2012-02-18 15:10:57 -05:00
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cChunk:
2012-04-10 07:22:11 -04:00
cChunk : : cChunk ( int a_ChunkX , int a_ChunkY , int a_ChunkZ , cChunkMap * a_ChunkMap , cWorld * a_World )
2012-05-25 03:18:52 -04:00
: m_PosX ( a_ChunkX )
2012-04-10 07:22:11 -04:00
, m_PosY ( a_ChunkY )
, m_PosZ ( a_ChunkZ )
2012-01-30 11:01:45 -05:00
, m_BlockTickX ( 0 )
, m_BlockTickY ( 0 )
, m_BlockTickZ ( 0 )
, m_World ( a_World )
2012-02-21 11:27:30 -05:00
, m_ChunkMap ( a_ChunkMap )
2012-02-13 16:47:03 -05:00
, m_IsValid ( false )
2012-05-25 03:18:52 -04:00
, m_IsLightValid ( false )
2012-02-16 08:42:35 -05:00
, m_IsDirty ( false )
, m_IsSaving ( false )
2012-02-26 11:15:09 -05:00
, m_StayCount ( 0 )
2012-01-30 11:01:45 -05:00
{
2012-02-13 16:47:03 -05:00
// LOGINFO("### new cChunk (%i, %i) at %p, thread 0x%x ###", a_X, a_Z, this, GetCurrentThreadId());
2012-01-30 11:01:45 -05:00
}
2011-10-03 14:41:19 -04:00
cChunk : : ~ cChunk ( )
{
2012-02-13 16:47:03 -05:00
// LOGINFO("### delete cChunk() (%i, %i) from %p, thread 0x%x ###", m_PosX, m_PosZ, this, GetCurrentThreadId() );
for ( cBlockEntityList : : iterator itr = m_BlockEntities . begin ( ) ; itr ! = m_BlockEntities . end ( ) ; + + itr )
2011-10-03 14:41:19 -04:00
{
delete * itr ;
}
2012-02-13 16:47:03 -05:00
m_BlockEntities . clear ( ) ;
2011-10-03 14:41:19 -04:00
2012-02-13 16:47:03 -05:00
// Remove and destroy all entities that are not players:
cEntityList Entities ;
for ( cEntityList : : const_iterator itr = m_Entities . begin ( ) ; itr ! = m_Entities . end ( ) ; + + itr )
2011-10-03 14:41:19 -04:00
{
2012-03-09 08:42:28 -05:00
if ( ( * itr ) - > GetEntityType ( ) ! = cEntity : : eEntityType_Player )
2011-10-03 14:41:19 -04:00
{
2012-02-13 16:47:03 -05:00
Entities . push_back ( * itr ) ;
2011-10-03 14:41:19 -04:00
}
}
2012-02-13 16:47:03 -05:00
for ( cEntityList : : iterator itr = Entities . begin ( ) ; itr ! = Entities . end ( ) ; + + itr )
2011-10-03 14:41:19 -04:00
{
2012-02-13 16:47:03 -05:00
( * itr ) - > RemoveFromChunk ( ) ;
( * itr ) - > Destroy ( ) ;
2011-10-03 14:41:19 -04:00
}
2012-02-13 16:47:03 -05:00
m_Entities . clear ( ) ;
2011-10-03 14:41:19 -04:00
}
2012-01-30 11:01:45 -05:00
2011-10-03 14:41:19 -04:00
2012-04-10 07:22:11 -04:00
void cChunk : : SetValid ( void )
2011-10-03 14:41:19 -04:00
{
2012-02-13 16:47:03 -05:00
m_IsValid = true ;
2012-02-18 12:53:22 -05:00
m_World - > GetChunkMap ( ) - > ChunkValidated ( ) ;
2012-04-10 07:22:11 -04:00
}
void cChunk : : MarkRegenerating ( void )
{
// Tell all clients attached to this chunk that they want this chunk:
2012-02-13 16:47:03 -05:00
for ( cClientHandleList : : iterator itr = m_LoadedByClient . begin ( ) ; itr ! = m_LoadedByClient . end ( ) ; + + itr )
2011-10-03 14:41:19 -04:00
{
2012-04-10 07:22:11 -04:00
( * itr ) - > AddWantedChunk ( m_PosX , m_PosZ ) ;
2012-02-13 16:47:03 -05:00
} // for itr - m_LoadedByClient[]
}
2011-10-03 14:41:19 -04:00
2011-12-24 18:34:30 -05:00
2012-02-13 16:47:03 -05:00
bool cChunk : : CanUnload ( void )
{
2012-02-26 11:15:09 -05:00
return m_LoadedByClient . empty ( ) & & ! m_IsDirty & & ( m_StayCount = = 0 ) ;
2012-02-16 08:42:35 -05:00
}
void cChunk : : MarkSaving ( void )
{
m_IsSaving = true ;
}
void cChunk : : MarkSaved ( void )
{
if ( ! m_IsSaving )
{
return ;
}
m_IsDirty = false ;
}
void cChunk : : MarkLoaded ( void )
{
m_IsDirty = false ;
2012-02-18 12:53:22 -05:00
SetValid ( ) ;
2012-02-16 08:42:35 -05:00
}
2012-02-28 07:11:14 -05:00
void cChunk : : MarkLoadFailed ( void )
{
if ( m_IsValid )
{
return ;
}
m_HasLoadFailed = true ;
}
2012-03-09 08:42:28 -05:00
void cChunk : : GetAllData ( cChunkDataCallback & a_Callback )
2012-02-16 08:42:35 -05:00
{
2012-03-14 16:56:09 -04:00
a_Callback . HeightMap ( & m_HeightMap ) ;
2012-05-25 03:18:52 -04:00
a_Callback . BiomeData ( & m_BiomeMap ) ;
2012-03-14 16:56:09 -04:00
a_Callback . BlockTypes ( m_BlockTypes ) ;
a_Callback . BlockMeta ( m_BlockMeta ) ;
2012-05-25 03:18:52 -04:00
a_Callback . LightIsValid ( m_IsLightValid ) ;
2012-03-14 16:56:09 -04:00
a_Callback . BlockLight ( m_BlockLight ) ;
a_Callback . BlockSkyLight ( m_BlockSkyLight ) ;
2012-02-16 08:42:35 -05:00
for ( cEntityList : : iterator itr = m_Entities . begin ( ) ; itr ! = m_Entities . end ( ) ; + + itr )
{
2012-03-09 08:42:28 -05:00
a_Callback . Entity ( * itr ) ;
2012-02-16 08:42:35 -05:00
}
for ( cBlockEntityList : : iterator itr = m_BlockEntities . begin ( ) ; itr ! = m_BlockEntities . end ( ) ; + + itr )
{
2012-03-09 08:42:28 -05:00
a_Callback . BlockEntity ( * itr ) ;
2012-02-16 08:42:35 -05:00
}
}
2012-03-14 16:56:09 -04:00
void cChunk : : SetAllData (
2012-05-25 03:18:52 -04:00
const BLOCKTYPE * a_BlockTypes ,
const NIBBLETYPE * a_BlockMeta ,
const NIBBLETYPE * a_BlockLight ,
const NIBBLETYPE * a_BlockSkyLight ,
const HeightMap * a_HeightMap ,
const BiomeMap & a_BiomeMap ,
2012-03-14 16:56:09 -04:00
cEntityList & a_Entities ,
cBlockEntityList & a_BlockEntities
)
2012-02-16 08:42:35 -05:00
{
2012-05-25 03:18:52 -04:00
memcpy ( m_BiomeMap , a_BiomeMap , sizeof ( m_BiomeMap ) ) ;
2012-03-14 16:56:09 -04:00
if ( a_HeightMap ! = NULL )
{
memcpy ( m_HeightMap , a_HeightMap , sizeof ( m_HeightMap ) ) ;
}
2012-05-25 03:18:52 -04:00
memcpy ( m_BlockTypes , a_BlockTypes , sizeof ( m_BlockTypes ) ) ;
memcpy ( m_BlockMeta , a_BlockMeta , sizeof ( m_BlockMeta ) ) ;
if ( a_BlockLight ! = NULL )
{
memcpy ( m_BlockLight , a_BlockLight , sizeof ( m_BlockLight ) ) ;
}
if ( a_BlockSkyLight ! = NULL )
{
memcpy ( m_BlockSkyLight , a_BlockSkyLight , sizeof ( m_BlockSkyLight ) ) ;
}
m_IsLightValid = ( a_BlockLight ! = NULL ) & & ( a_BlockSkyLight ! = NULL ) ;
2012-03-14 16:56:09 -04:00
if ( a_HeightMap = = NULL )
{
CalculateHeightmap ( ) ;
}
2012-02-16 08:42:35 -05:00
2012-05-30 09:11:58 -04:00
// Append entities to current entity list:
m_Entities . splice ( m_Entities . end ( ) , a_Entities ) ;
// Clear the block entities present - either the loader / saver has better, or we'll create empty ones:
2012-02-16 08:42:35 -05:00
for ( cBlockEntityList : : iterator itr = m_BlockEntities . begin ( ) ; itr ! = m_BlockEntities . end ( ) ; + + itr )
{
delete * itr ;
}
std : : swap ( a_BlockEntities , m_BlockEntities ) ;
// Create block entities that the loader didn't load; fill them with defaults
CreateBlockEntities ( ) ;
2012-02-16 10:40:38 -05:00
2012-02-28 07:11:14 -05:00
m_HasLoadFailed = false ;
2012-02-16 08:42:35 -05:00
}
2012-05-25 03:18:52 -04:00
void cChunk : : SetLight (
const cChunkDef : : BlockNibbles & a_BlockLight ,
const cChunkDef : : BlockNibbles & a_SkyLight
)
{
// TODO: We might get cases of wrong lighting when a chunk changes in the middle of a lighting calculation.
// Postponing until we see how bad it is :)
memcpy ( m_BlockLight , a_BlockLight , sizeof ( m_BlockLight ) ) ;
memcpy ( m_BlockSkyLight , a_SkyLight , sizeof ( m_BlockSkyLight ) ) ;
m_IsLightValid = true ;
}
2012-03-14 16:56:09 -04:00
void cChunk : : GetBlockTypes ( BLOCKTYPE * a_BlockTypes )
2012-02-18 14:18:16 -05:00
{
2012-03-14 16:56:09 -04:00
memcpy ( a_BlockTypes , m_BlockTypes , NumBlocks ) ;
2012-02-18 14:18:16 -05:00
}
2012-03-14 16:56:09 -04:00
void cChunk : : GetBlockData ( BLOCKTYPE * a_BlockData )
2012-03-05 11:41:57 -05:00
{
2012-03-14 16:56:09 -04:00
memcpy ( a_BlockData , m_BlockTypes , NumBlocks ) ;
memcpy ( a_BlockData + MetaOffset , m_BlockMeta , NumBlocks / 2 ) ;
memcpy ( a_BlockData + LightOffset , m_BlockLight , NumBlocks / 2 ) ;
memcpy ( a_BlockData + SkyLightOffset , m_BlockSkyLight , NumBlocks / 2 ) ;
2012-03-05 11:41:57 -05:00
}
2012-02-16 08:42:35 -05:00
/// Returns true if there is a block entity at the coords specified
bool cChunk : : HasBlockEntityAt ( int a_BlockX , int a_BlockY , int a_BlockZ )
{
for ( cBlockEntityList : : iterator itr = m_BlockEntities . begin ( ) ; itr ! = m_BlockEntities . end ( ) ; + + itr )
{
if (
( ( * itr ) - > GetPosX ( ) = = a_BlockX ) & &
( ( * itr ) - > GetPosY ( ) = = a_BlockY ) & &
( ( * itr ) - > GetPosZ ( ) = = a_BlockZ )
)
{
return true ;
}
} // for itr - m_BlockEntities[]
return false ;
2011-10-03 14:41:19 -04:00
}
2012-01-30 11:01:45 -05:00
2012-02-26 11:15:09 -05:00
/// Sets or resets the internal flag that prevents chunk from being unloaded
void cChunk : : Stay ( bool a_Stay )
{
m_StayCount + = ( a_Stay ? 1 : - 1 ) ;
ASSERT ( m_StayCount > = 0 ) ;
}
2012-02-08 07:36:54 -05:00
void cChunk : : Tick ( float a_Dt , MTRand & a_TickRandom )
2011-10-03 14:41:19 -04:00
{
2012-02-13 16:47:03 -05:00
cCSLock Lock ( m_CSBlockLists ) ;
unsigned int PendingSendBlocks = m_PendingSendBlocks . size ( ) ;
2012-01-26 15:39:46 -05:00
if ( PendingSendBlocks > 1 )
{
cPacket_MultiBlock MultiBlock ;
MultiBlock . m_ChunkX = m_PosX ;
MultiBlock . m_ChunkZ = m_PosZ ;
MultiBlock . m_NumBlocks = ( short ) PendingSendBlocks ;
2012-03-01 20:22:06 -05:00
MultiBlock . m_Data = new cPacket_MultiBlock : : sBlockChange [ PendingSendBlocks ] ;
MultiBlock . m_DataSize = PendingSendBlocks * sizeof ( cPacket_MultiBlock : : sBlockChange ) ;
2012-01-26 15:39:46 -05:00
//LOG("Sending multiblock packet for %i blocks", PendingSendBlocks );
for ( unsigned int i = 0 ; i < PendingSendBlocks ; i + + )
{
2012-02-13 16:47:03 -05:00
unsigned int index = m_PendingSendBlocks [ i ] ;
2012-03-09 19:25:05 -05:00
Vector3i BlockPos = IndexToCoordinate ( index ) ;
2012-01-26 15:39:46 -05:00
2012-03-14 16:56:09 -04:00
unsigned int Coords = BlockPos . y | ( BlockPos . z < < 8 ) | ( BlockPos . x < < 12 ) ;
unsigned int Blocks = GetNibble ( m_BlockMeta , index ) | ( m_BlockTypes [ index ] < < 4 ) ;
2012-03-02 08:41:42 -05:00
MultiBlock . m_Data [ i ] . Data = Coords < < 16 | Blocks ;
2012-01-26 15:39:46 -05:00
}
2012-02-13 16:47:03 -05:00
m_PendingSendBlocks . clear ( ) ;
PendingSendBlocks = m_PendingSendBlocks . size ( ) ;
2012-01-26 15:39:46 -05:00
Broadcast ( MultiBlock ) ;
}
2011-10-03 14:41:19 -04:00
if ( PendingSendBlocks > 0 )
{
for ( unsigned int i = 0 ; i < PendingSendBlocks ; i + + )
{
2012-02-13 16:47:03 -05:00
unsigned int index = m_PendingSendBlocks [ i ] ;
2012-03-09 19:25:05 -05:00
Vector3i WorldPos = PositionToWorldPosition ( IndexToCoordinate ( index ) ) ;
2011-10-03 14:41:19 -04:00
cPacket_BlockChange BlockChange ;
2012-03-09 19:25:05 -05:00
BlockChange . m_PosX = WorldPos . x ;
BlockChange . m_PosY = ( unsigned char ) WorldPos . y ;
BlockChange . m_PosZ = WorldPos . z ;
2012-03-14 16:56:09 -04:00
BlockChange . m_BlockType = m_BlockTypes [ index ] ;
2012-03-05 11:41:57 -05:00
BlockChange . m_BlockMeta = GetNibble ( m_BlockMeta , index ) ;
2011-10-03 14:41:19 -04:00
Broadcast ( BlockChange ) ;
}
2012-02-13 16:47:03 -05:00
m_PendingSendBlocks . clear ( ) ;
2011-10-03 14:41:19 -04:00
}
2012-01-30 16:54:40 -05:00
Lock . Unlock ( ) ;
2011-10-03 14:41:19 -04:00
2012-02-13 16:47:03 -05:00
while ( ! m_UnloadQuery . empty ( ) )
2011-10-03 14:41:19 -04:00
{
cPacket_PreChunk UnloadPacket ;
UnloadPacket . m_PosX = GetPosX ( ) ;
UnloadPacket . m_PosZ = GetPosZ ( ) ;
UnloadPacket . m_bLoad = false ; // Unload
2012-02-13 16:47:03 -05:00
( * m_UnloadQuery . begin ( ) ) - > Send ( UnloadPacket ) ;
m_UnloadQuery . remove ( * m_UnloadQuery . begin ( ) ) ;
2011-10-03 14:41:19 -04:00
}
2012-02-13 16:47:03 -05:00
cCSLock Lock2 ( m_CSBlockLists ) ;
2012-02-28 09:22:03 -05:00
unsigned int NumTickBlocks = m_ToTickBlocks . size ( ) ;
2012-01-30 16:54:40 -05:00
Lock2 . Unlock ( ) ;
2011-10-03 14:41:19 -04:00
2012-05-25 03:18:52 -04:00
if ( NumTickBlocks > 0 )
2012-02-28 09:22:03 -05:00
{
Lock2 . Lock ( ) ;
std : : deque < unsigned int > ToTickBlocks = m_ToTickBlocks ;
m_ToTickBlocks . clear ( ) ;
Lock2 . Unlock ( ) ;
bool isRedstone = false ;
2012-05-25 03:18:52 -04:00
for ( std : : deque < unsigned int > : : iterator itr = ToTickBlocks . begin ( ) ; itr ! = ToTickBlocks . end ( ) ; + + itr )
2011-10-03 14:41:19 -04:00
{
2012-02-28 09:22:03 -05:00
unsigned int index = ( * itr ) ;
2012-03-09 19:25:05 -05:00
Vector3i BlockPos = IndexToCoordinate ( index ) ;
2012-02-28 09:22:03 -05:00
2012-06-06 16:18:50 -04:00
BLOCKTYPE BlockID = GetBlock ( index ) ;
NIBBLETYPE BlockMeta = GetMeta ( index ) ;
2012-05-25 03:18:52 -04:00
switch ( BlockID )
2011-10-03 14:41:19 -04:00
{
2012-05-25 03:18:52 -04:00
case E_BLOCK_REDSTONE_REPEATER_OFF :
case E_BLOCK_REDSTONE_REPEATER_ON :
case E_BLOCK_REDSTONE_WIRE :
2012-02-28 09:22:03 -05:00
{
isRedstone = true ;
2012-05-25 03:18:52 -04:00
// fallthrough
2012-02-28 09:22:03 -05:00
}
2012-05-25 03:18:52 -04:00
case E_BLOCK_CACTUS :
case E_BLOCK_REEDS :
case E_BLOCK_WOODEN_PRESSURE_PLATE :
case E_BLOCK_STONE_PRESSURE_PLATE :
case E_BLOCK_MINECART_TRACKS :
case E_BLOCK_SIGN_POST :
case E_BLOCK_CROPS :
case E_BLOCK_SAPLING :
case E_BLOCK_YELLOW_FLOWER :
case E_BLOCK_RED_ROSE :
case E_BLOCK_RED_MUSHROOM :
case E_BLOCK_BROWN_MUSHROOM : // Stuff that drops when block below is destroyed
2011-10-03 14:41:19 -04:00
{
2012-06-06 16:18:50 -04:00
if ( GetBlock ( BlockPos . x , BlockPos . y - 1 , BlockPos . z ) = = E_BLOCK_AIR )
2012-02-28 09:22:03 -05:00
{
2012-03-09 19:25:05 -05:00
SetBlock ( BlockPos , E_BLOCK_AIR , 0 ) ;
2011-12-22 16:36:24 -05:00
2012-03-09 19:25:05 -05:00
Vector3i WorldPos = PositionToWorldPosition ( BlockPos ) ;
2011-12-22 16:36:24 -05:00
2012-03-09 19:25:05 -05:00
m_World - > GetSimulatorManager ( ) - > WakeUp ( WorldPos . x , WorldPos . y , WorldPos . z ) ;
2012-06-06 16:18:50 -04:00
cItems Pickups ;
cBlockToPickup : : ToPickup ( BlockID , BlockMeta , E_ITEM_EMPTY , Pickups ) ;
m_World - > SpawnItemPickups ( Pickups , WorldPos . x , WorldPos . y , WorldPos . z ) ;
2011-11-07 17:59:29 -05:00
}
2012-05-25 03:18:52 -04:00
break ;
2011-10-03 14:41:19 -04:00
}
2012-05-25 03:18:52 -04:00
case E_BLOCK_REDSTONE_TORCH_OFF :
case E_BLOCK_REDSTONE_TORCH_ON :
{
2012-02-28 09:22:03 -05:00
isRedstone = true ;
2012-05-25 03:18:52 -04:00
// fallthrough
}
case E_BLOCK_TORCH :
2011-10-03 14:41:19 -04:00
{
2012-03-09 19:25:05 -05:00
char Dir = cTorch : : MetaDataToDirection ( GetNibble ( m_BlockMeta , BlockPos ) ) ;
Vector3i WorldPos = PositionToWorldPosition ( BlockPos ) ;
Vector3i AttachedTo = WorldPos ;
AddDirection ( AttachedTo . x , AttachedTo . y , AttachedTo . z , Dir , true ) ;
if ( m_World - > GetBlock ( AttachedTo ) = = E_BLOCK_AIR )
2012-02-28 09:22:03 -05:00
{
2012-03-09 19:25:05 -05:00
SetBlock ( BlockPos , E_BLOCK_AIR , 0 ) ;
2012-03-01 10:18:59 -05:00
2012-03-09 19:25:05 -05:00
m_World - > GetSimulatorManager ( ) - > WakeUp ( WorldPos . x , WorldPos . y , WorldPos . z ) ;
2012-03-01 10:18:59 -05:00
2012-06-06 16:18:50 -04:00
cItems Pickups ;
cBlockToPickup : : ToPickup ( BlockID , BlockMeta , E_ITEM_EMPTY , Pickups ) ;
m_World - > SpawnItemPickups ( Pickups , WorldPos . x , WorldPos . y , WorldPos . z ) ;
2011-11-07 17:59:29 -05:00
}
2012-05-25 03:18:52 -04:00
break ;
2011-10-03 14:41:19 -04:00
}
2012-05-25 03:18:52 -04:00
case E_BLOCK_LADDER :
2011-10-03 14:41:19 -04:00
{
2012-03-09 19:25:05 -05:00
char Dir = cLadder : : MetaDataToDirection ( GetNibble ( m_BlockMeta , BlockPos ) ) ;
Vector3i WorldPos = PositionToWorldPosition ( BlockPos ) ;
Vector3i AttachedTo = WorldPos ;
AddDirection ( AttachedTo . x , AttachedTo . y , AttachedTo . z , Dir , true ) ;
if ( m_World - > GetBlock ( AttachedTo ) = = E_BLOCK_AIR )
2012-02-28 09:22:03 -05:00
{
2012-03-09 19:25:05 -05:00
SetBlock ( BlockPos , E_BLOCK_AIR , 0 ) ;
2012-06-06 16:18:50 -04:00
cItems Pickups ;
cBlockToPickup : : ToPickup ( BlockID , BlockMeta , E_ITEM_EMPTY , Pickups ) ;
m_World - > SpawnItemPickups ( Pickups , WorldPos . x , WorldPos . y , WorldPos . z ) ;
2012-02-28 09:22:03 -05:00
}
2012-05-25 03:18:52 -04:00
break ;
2011-10-03 14:41:19 -04:00
}
2012-05-25 03:18:52 -04:00
} // switch (BlockType)
} // for itr - ToTickBlocks[]
}
TickBlocks ( a_TickRandom ) ;
// Tick block entities (furnaces)
for ( cBlockEntityList : : iterator itr = m_BlockEntities . begin ( ) ; itr ! = m_BlockEntities . end ( ) ; + + itr )
{
if ( ( * itr ) - > GetBlockType ( ) = = E_BLOCK_FURNACE )
{
2012-05-28 12:47:57 -04:00
m_IsDirty = ( ( cFurnaceEntity * ) ( * itr ) ) - > Tick ( a_Dt ) | m_IsDirty ;
2012-02-28 09:22:03 -05:00
}
2011-10-03 14:41:19 -04:00
}
2012-05-25 03:18:52 -04:00
}
2011-12-28 09:52:31 -05:00
2012-05-25 03:18:52 -04:00
void cChunk : : TickBlocks ( MTRand & a_TickRandom )
{
2011-10-03 14:41:19 -04:00
// Tick dem blocks
2012-06-01 02:25:30 -04:00
// _X: We must limit the random number or else we get a nasty int overflow bug ( http://forum.mc-server.org/showthread.php?tid=457 )
int RandomX = a_TickRandom . randInt ( 0x00ffffff ) ;
int RandomY = a_TickRandom . randInt ( 0x00ffffff ) ;
int RandomZ = a_TickRandom . randInt ( 0x00ffffff ) ;
2012-05-30 15:35:57 -04:00
int TickX = m_BlockTickX ;
int TickY = m_BlockTickY ;
int TickZ = m_BlockTickZ ;
2011-10-03 14:41:19 -04:00
2012-05-30 17:29:51 -04:00
// This for loop looks disgusting, but it actually does a simple thing - first processes m_BlockTick, then adds random to it
// This is so that SetNextBlockTick() works
for ( int i = 0 ; i < 50 ; i + + ,
2012-05-30 15:35:57 -04:00
// This weird construct (*2, then /2) is needed,
// otherwise the blocktick distribution is too biased towards even coords!
2012-05-30 17:29:51 -04:00
TickX = ( TickX + RandomX ) % ( Width * 2 ) ,
TickY = ( TickY + RandomY ) % ( Height * 2 ) ,
TickZ = ( TickZ + RandomZ ) % ( Width * 2 ) ,
m_BlockTickX = TickX / 2 ,
m_BlockTickY = TickY / 2 ,
m_BlockTickZ = TickZ / 2
)
{
2012-05-30 15:35:57 -04:00
if ( m_BlockTickY > cChunkDef : : GetHeight ( m_HeightMap , m_BlockTickX , m_BlockTickZ ) )
2012-05-29 10:59:43 -04:00
{
continue ; // It's all air up here
}
2011-10-03 14:41:19 -04:00
2012-03-09 19:25:05 -05:00
unsigned int Index = MakeIndexNoCheck ( m_BlockTickX , m_BlockTickY , m_BlockTickZ ) ;
2012-05-29 10:59:43 -04:00
BLOCKTYPE ID = m_BlockTypes [ Index ] ;
2011-10-03 14:41:19 -04:00
switch ( ID )
{
2012-05-29 10:59:43 -04:00
case E_BLOCK_GRASS :
2011-10-03 14:41:19 -04:00
{
2012-05-29 10:59:43 -04:00
// 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 ] ) ) )
2011-11-08 20:31:19 -05:00
{
2012-05-29 10:59:43 -04:00
FastSetBlock ( m_BlockTickX , m_BlockTickY , m_BlockTickZ , E_BLOCK_DIRT , GetNibble ( m_BlockMeta , Index ) ) ;
2011-11-08 20:31:19 -05:00
}
2012-05-29 10:59:43 -04:00
// 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
2012-02-13 16:47:03 -05:00
break ;
2011-10-03 14:41:19 -04:00
}
2012-02-13 16:47:03 -05:00
2012-05-29 10:59:43 -04:00
case E_BLOCK_CROPS :
2011-10-03 14:41:19 -04:00
{
2012-05-29 10:59:43 -04:00
NIBBLETYPE Meta = GetMeta ( Index ) ;
if ( Meta < 7 )
2011-10-03 14:41:19 -04:00
{
2012-05-29 10:59:43 -04:00
FastSetBlock ( m_BlockTickX , m_BlockTickY , m_BlockTickZ , E_BLOCK_CROPS , + + Meta ) ;
2011-10-03 14:41:19 -04:00
}
2012-05-25 03:18:52 -04:00
break ;
2011-10-03 14:41:19 -04:00
}
2012-05-25 03:18:52 -04:00
2012-05-29 10:59:43 -04:00
case E_BLOCK_PUMPKIN_STEM :
case E_BLOCK_MELON_STEM : TickMelonPumpkin ( m_BlockTickX , m_BlockTickY , m_BlockTickZ , Index , ID , a_TickRandom ) ; break ;
2012-05-30 11:40:53 -04:00
case E_BLOCK_FARMLAND : TickFarmland ( m_BlockTickX , m_BlockTickY , m_BlockTickZ ) ; break ;
2012-05-29 10:59:43 -04:00
case E_BLOCK_SAPLING :
2011-10-03 14:41:19 -04:00
{
2012-05-25 03:18:52 -04:00
// 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 ) ;
if ( ( Meta & 0x08 ) ! = 0 )
{
m_World - > GrowTree ( m_BlockTickX + m_PosX * Width , m_BlockTickY , m_BlockTickZ + m_PosZ * Width ) ;
}
else
{
SetMeta ( m_BlockTickX , m_BlockTickY , m_BlockTickZ , Meta | 0x08 ) ;
}
break ;
2011-10-03 14:41:19 -04:00
}
2012-05-25 03:18:52 -04:00
case E_BLOCK_LEAVES : //todo, http://www.minecraftwiki.net/wiki/Data_values#Leaves
2011-11-09 22:35:46 -05:00
{
2012-05-25 03:18:52 -04:00
break ;
}
default :
{
break ;
2011-11-09 22:35:46 -05:00
}
2011-10-03 14:41:19 -04:00
}
}
}
2012-02-08 07:36:54 -05:00
2012-05-29 10:59:43 -04:00
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 ;
2012-05-31 12:46:05 -04:00
int CheckType = a_TickRandom . randInt ( 3 ) ; // The index to the neighbors array which should be checked for emptiness
2012-05-29 10:59:43 -04:00
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 ;
2012-05-30 16:31:31 -04:00
UnboundedRelGetBlock ( a_RelX + x , a_RelY - 1 , a_RelZ + z , Soil , BlockMeta ) ;
2012-05-29 10:59:43 -04:00
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 ;
}
}
}
2012-05-30 11:40:53 -04:00
void cChunk : : TickFarmland ( int a_RelX , int a_RelY , int a_RelZ )
{
// TODO: Rain hydrates blocks, too. Check world weather, don't search for water if raining.
// Search for water in a close proximity:
// Ref.: http://www.minecraftwiki.net/wiki/Farmland#Hydrated_Farmland_Tiles
bool Found = false ;
for ( int y = a_RelY ; y < = a_RelY + 1 ; y + + )
{
for ( int z = a_RelZ - 4 ; z < = a_RelZ + 4 ; z + + )
{
for ( int x = a_RelX - 4 ; x < = a_RelX + 4 ; x + + )
{
BLOCKTYPE BlockType ;
NIBBLETYPE Meta ; // unused
if ( ! UnboundedRelGetBlock ( x , y , z , BlockType , Meta ) )
{
// Too close to an unloaded chunk, we might miss a water block there, so don't tick at all
return ;
}
if (
( BlockType = = E_BLOCK_WATER ) | |
( BlockType = = E_BLOCK_STATIONARY_WATER )
)
{
Found = true ;
break ;
}
} // for x
if ( Found )
{
break ;
}
} // for z
if ( Found )
{
break ;
}
} // for y
NIBBLETYPE BlockMeta = GetMeta ( a_RelX , a_RelY , a_RelZ ) ;
if ( Found )
{
// Water was found, hydrate the block until hydration reaches 7:
if ( BlockMeta < 7 )
{
FastSetBlock ( a_RelX , a_RelY , a_RelZ , E_BLOCK_FARMLAND , + + BlockMeta ) ;
}
return ;
}
// Water wasn't found, de-hydrate block:
if ( BlockMeta > 0 )
{
FastSetBlock ( a_RelX , a_RelY , a_RelZ , E_BLOCK_FARMLAND , - - BlockMeta ) ;
return ;
}
// Farmland too dry. Turn back to dirt:
FastSetBlock ( a_RelX , a_RelY , a_RelZ , E_BLOCK_DIRT , 0 ) ;
// TODO: Uproot whatever was growing on top:
}
2012-05-29 10:59:43 -04:00
bool cChunk : : UnboundedRelGetBlock ( int a_RelX , int a_RelY , int a_RelZ , BLOCKTYPE & a_BlockType , NIBBLETYPE & a_BlockMeta )
{
2012-05-30 11:40:53 -04:00
if ( ( a_RelX > = 0 ) & & ( a_RelX < cChunkDef : : Width ) & & ( a_RelZ > = 0 ) & & ( a_RelZ < cChunkDef : : Width ) )
2012-05-29 10:59:43 -04:00
{
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 )
{
2012-05-30 11:40:53 -04:00
if ( ( a_RelX > = 0 ) & & ( a_RelX < cChunkDef : : Width ) & & ( a_RelZ > = 0 ) & & ( a_RelZ < cChunkDef : : Width ) )
2012-05-29 10:59:43 -04:00
{
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 )
{
2012-05-30 11:40:53 -04:00
if ( ( a_RelX > = 0 ) & & ( a_RelX < cChunkDef : : Width ) & & ( a_RelZ > = 0 ) & & ( a_RelZ < cChunkDef : : Width ) )
2012-05-29 10:59:43 -04:00
{
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
) ;
}
2012-02-18 12:53:22 -05:00
int cChunk : : GetHeight ( int a_X , int a_Z )
2011-10-03 14:41:19 -04:00
{
2012-03-14 16:56:09 -04:00
ASSERT ( ( a_X > = 0 ) & & ( a_X < Width ) & & ( a_Z > = 0 ) & & ( a_Z < Width ) ) ;
2012-02-21 08:44:06 -05:00
2012-03-14 16:56:09 -04:00
if ( ( a_X > = 0 ) & & ( a_X < Width ) & & ( a_Z > = 0 ) & & ( a_Z < Width ) )
2012-02-13 16:47:03 -05:00
{
2012-03-14 16:56:09 -04:00
return m_HeightMap [ a_X + a_Z * Width ] ;
2012-02-13 16:47:03 -05:00
}
2011-10-03 14:41:19 -04:00
return 0 ;
}
2012-02-08 14:49:57 -05:00
2012-02-16 08:42:35 -05:00
void cChunk : : CreateBlockEntities ( void )
2011-10-03 14:41:19 -04:00
{
2012-03-14 16:56:09 -04:00
for ( int x = 0 ; x < Width ; x + + )
2011-10-03 14:41:19 -04:00
{
2012-03-14 16:56:09 -04:00
for ( int z = 0 ; z < Width ; z + + )
2011-10-03 14:41:19 -04:00
{
2012-03-14 16:56:09 -04:00
for ( int y = 0 ; y < Height ; y + + )
2011-10-03 14:41:19 -04:00
{
2012-05-25 03:18:52 -04:00
ENUM_BLOCK_ID BlockType = ( ENUM_BLOCK_ID ) m_BlockTypes [ MakeIndex ( x , y , z ) ] ;
2012-02-13 16:47:03 -05:00
switch ( BlockType )
2011-10-03 14:41:19 -04:00
{
2012-02-13 16:47:03 -05:00
case E_BLOCK_CHEST :
2011-10-03 14:41:19 -04:00
{
2012-03-14 16:56:09 -04:00
if ( ! HasBlockEntityAt ( x + m_PosX * Width , y + m_PosY * Height , z + m_PosZ * Width ) )
2012-02-16 08:42:35 -05:00
{
2012-03-14 16:56:09 -04:00
m_BlockEntities . push_back ( new cChestEntity ( x + m_PosX * Width , y + m_PosY * Height , z + m_PosZ * Width , m_World ) ) ;
2012-02-16 08:42:35 -05:00
}
2012-02-13 16:47:03 -05:00
break ;
2011-10-03 14:41:19 -04:00
}
2012-02-13 16:47:03 -05:00
case E_BLOCK_FURNACE :
2011-10-03 14:41:19 -04:00
{
2012-03-14 16:56:09 -04:00
if ( ! HasBlockEntityAt ( x + m_PosX * Width , y + m_PosY * Height , z + m_PosZ * Width ) )
2012-02-16 08:42:35 -05:00
{
2012-03-14 16:56:09 -04:00
m_BlockEntities . push_back ( new cFurnaceEntity ( x + m_PosX * Width , y + m_PosY * Height , z + m_PosZ * Width , m_World ) ) ;
2012-02-16 08:42:35 -05:00
}
2012-02-13 16:47:03 -05:00
break ;
2011-10-03 14:41:19 -04:00
}
2012-02-13 16:47:03 -05:00
case E_BLOCK_SIGN_POST :
case E_BLOCK_WALLSIGN :
2011-10-03 14:41:19 -04:00
{
2012-03-14 16:56:09 -04:00
if ( ! HasBlockEntityAt ( x + m_PosX * Width , y + m_PosY * Height , z + m_PosZ * Width ) )
2012-02-16 08:42:35 -05:00
{
2012-03-14 16:56:09 -04:00
m_BlockEntities . push_back ( new cSignEntity ( BlockType , x + m_PosX * Width , y + m_PosY * Height , z + m_PosZ * Width , m_World ) ) ;
2012-02-16 08:42:35 -05:00
}
2012-02-13 16:47:03 -05:00
break ;
2011-10-03 14:41:19 -04:00
}
2012-02-13 16:47:03 -05:00
} // switch (BlockType)
} // for y
} // for z
} // for x
2011-10-03 14:41:19 -04:00
}
2012-02-08 14:49:57 -05:00
2011-10-03 14:41:19 -04:00
void cChunk : : CalculateHeightmap ( )
{
2012-03-14 16:56:09 -04:00
for ( int x = 0 ; x < Width ; x + + )
2011-10-03 14:41:19 -04:00
{
2012-03-14 16:56:09 -04:00
for ( int z = 0 ; z < Width ; z + + )
2011-10-03 14:41:19 -04:00
{
2012-03-14 16:56:09 -04:00
for ( int y = Height - 1 ; y > - 1 ; y - - )
2011-10-03 14:41:19 -04:00
{
2012-05-25 03:18:52 -04:00
int index = MakeIndex ( x , y , z ) ;
2012-03-14 16:56:09 -04:00
if ( m_BlockTypes [ index ] ! = E_BLOCK_AIR )
2011-10-03 14:41:19 -04:00
{
2012-03-14 16:56:09 -04:00
m_HeightMap [ x + z * Width ] = ( unsigned char ) y ;
2011-10-03 14:41:19 -04:00
break ;
}
2012-02-13 16:47:03 -05:00
} // for y
} // for z
} // for x
2011-10-03 14:41:19 -04:00
}
2012-02-08 14:49:57 -05:00
2012-05-25 03:18:52 -04:00
void cChunk : : SetBlock ( int a_RelX , int a_RelY , int a_RelZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta )
2011-10-03 14:41:19 -04:00
{
2012-05-25 03:18:52 -04:00
if ( a_RelX < 0 | | a_RelX > = Width | | a_RelY < 0 | | a_RelY > = Height | | a_RelZ < 0 | | a_RelZ > = Width )
2011-10-03 14:41:19 -04:00
{
2012-02-08 14:49:57 -05:00
return ; // Clip
2011-10-03 14:41:19 -04:00
}
2012-02-19 18:00:00 -05:00
ASSERT ( IsValid ( ) ) ; // Is this chunk loaded / generated?
2012-02-13 16:47:03 -05:00
2012-05-25 03:18:52 -04:00
int index = MakeIndexNoCheck ( a_RelX , a_RelY , a_RelZ ) ;
2012-03-14 16:56:09 -04:00
BLOCKTYPE OldBlockMeta = GetNibble ( m_BlockMeta , index ) ;
BLOCKTYPE OldBlockType = m_BlockTypes [ index ] ;
m_BlockTypes [ index ] = a_BlockType ;
2011-10-03 14:41:19 -04:00
2012-03-05 11:41:57 -05:00
SetNibble ( m_BlockMeta , index , a_BlockMeta ) ;
2011-10-03 14:41:19 -04:00
2012-02-08 14:49:57 -05:00
if ( ( OldBlockType = = a_BlockType ) & & ( OldBlockMeta = = a_BlockMeta ) )
{
return ;
}
2012-02-16 12:45:26 -05:00
MarkDirty ( ) ;
2012-02-21 08:44:06 -05:00
{
cCSLock Lock ( m_CSBlockLists ) ;
m_PendingSendBlocks . push_back ( index ) ;
}
// ONLY recalculate lighting if it's necessary!
if (
( g_BlockLightValue [ OldBlockType ] ! = g_BlockLightValue [ a_BlockType ] ) | |
( g_BlockSpreadLightFalloff [ OldBlockType ] ! = g_BlockSpreadLightFalloff [ a_BlockType ] ) | |
( g_BlockTransparent [ OldBlockType ] ! = g_BlockTransparent [ a_BlockType ] )
)
{
2012-05-25 03:18:52 -04:00
m_IsLightValid = false ;
2012-02-21 08:44:06 -05:00
}
// Update heightmap, if needed:
2012-05-25 03:18:52 -04:00
if ( a_RelY > = m_HeightMap [ a_RelX + a_RelZ * Width ] )
2012-02-21 08:44:06 -05:00
{
2012-02-21 10:18:02 -05:00
if ( a_BlockType ! = E_BLOCK_AIR )
{
2012-05-25 03:18:52 -04:00
SetHeight ( m_HeightMap , a_RelX , a_RelZ , a_RelY ) ;
2012-02-21 10:18:02 -05:00
}
else
{
2012-05-25 03:18:52 -04:00
for ( int y = a_RelY - 1 ; y > 0 ; - - y )
2012-02-21 10:18:02 -05:00
{
2012-05-25 03:18:52 -04:00
if ( cChunkDef : : GetBlock ( m_BlockTypes , a_RelX , y , a_RelZ ) ! = E_BLOCK_AIR )
2012-02-21 10:18:02 -05:00
{
2012-05-25 03:18:52 -04:00
SetHeight ( m_HeightMap , a_RelX , a_RelZ , y ) ;
2012-02-21 10:18:02 -05:00
break ;
}
} // for y - column in m_BlockData
}
2012-02-21 08:44:06 -05:00
}
2012-02-08 14:49:57 -05:00
2012-05-25 03:18:52 -04:00
m_ToTickBlocks . push_back ( MakeIndex ( a_RelX , a_RelY , a_RelZ ) ) ;
m_ToTickBlocks . push_back ( MakeIndex ( a_RelX + 1 , a_RelY , a_RelZ ) ) ;
m_ToTickBlocks . push_back ( MakeIndex ( a_RelX - 1 , a_RelY , a_RelZ ) ) ;
m_ToTickBlocks . push_back ( MakeIndex ( a_RelX , a_RelY + 1 , a_RelZ ) ) ;
m_ToTickBlocks . push_back ( MakeIndex ( a_RelX , a_RelY - 1 , a_RelZ ) ) ;
m_ToTickBlocks . push_back ( MakeIndex ( a_RelX , a_RelY , a_RelZ + 1 ) ) ;
m_ToTickBlocks . push_back ( MakeIndex ( a_RelX , a_RelY , a_RelZ - 1 ) ) ;
2012-02-08 14:49:57 -05:00
2012-05-25 03:18:52 -04:00
Vector3i WorldPos = PositionToWorldPosition ( a_RelX , a_RelY , a_RelZ ) ;
2012-03-09 19:25:05 -05:00
cBlockEntity * BlockEntity = GetBlockEntity ( WorldPos ) ;
2012-02-08 14:49:57 -05:00
if ( BlockEntity )
{
BlockEntity - > Destroy ( ) ;
RemoveBlockEntity ( BlockEntity ) ;
delete BlockEntity ;
}
switch ( a_BlockType )
2011-10-03 14:41:19 -04:00
{
case E_BLOCK_CHEST :
2012-02-08 14:49:57 -05:00
{
2012-03-09 19:25:05 -05:00
AddBlockEntity ( new cChestEntity ( WorldPos . x , WorldPos . y , WorldPos . z , m_World ) ) ;
2011-10-03 14:41:19 -04:00
break ;
2012-02-08 14:49:57 -05:00
}
2011-10-03 14:41:19 -04:00
case E_BLOCK_FURNACE :
2012-02-08 14:49:57 -05:00
{
2012-03-09 19:25:05 -05:00
AddBlockEntity ( new cFurnaceEntity ( WorldPos . x , WorldPos . y , WorldPos . z , m_World ) ) ;
2011-10-03 14:41:19 -04:00
break ;
2012-02-08 14:49:57 -05:00
}
2011-10-03 14:41:19 -04:00
case E_BLOCK_SIGN_POST :
case E_BLOCK_WALLSIGN :
2012-02-08 14:49:57 -05:00
{
2012-03-09 19:25:05 -05:00
AddBlockEntity ( new cSignEntity ( ( ENUM_BLOCK_ID ) a_BlockType , WorldPos . x , WorldPos . y , WorldPos . z , m_World ) ) ;
2011-10-03 14:41:19 -04:00
break ;
2012-02-08 14:49:57 -05:00
}
} // switch (a_BlockType)
}
2011-12-28 09:52:31 -05:00
2011-10-03 14:41:19 -04:00
2012-03-14 16:56:09 -04:00
void cChunk : : FastSetBlock ( int a_X , int a_Y , int a_Z , BLOCKTYPE a_BlockType , BLOCKTYPE a_BlockMeta )
2011-10-03 14:41:19 -04:00
{
2012-03-14 16:56:09 -04:00
ASSERT ( ! ( ( a_X < 0 | | a_X > = Width | | a_Y < 0 | | a_Y > = Height | | a_Z < 0 | | a_Z > = Width ) ) ) ;
2011-10-03 14:41:19 -04:00
2012-02-19 18:00:00 -05:00
ASSERT ( IsValid ( ) ) ;
2012-02-13 16:47:03 -05:00
2012-03-09 19:25:05 -05:00
const int index = MakeIndexNoCheck ( a_X , a_Y , a_Z ) ;
2012-03-14 16:56:09 -04:00
const BLOCKTYPE OldBlock = m_BlockTypes [ index ] ;
const BLOCKTYPE OldBlockMeta = GetNibble ( m_BlockMeta , index ) ;
if ( ( OldBlock = = a_BlockType ) & & ( OldBlockMeta = = a_BlockMeta ) )
2012-02-13 16:47:03 -05:00
{
return ;
}
2012-02-16 12:45:26 -05:00
MarkDirty ( ) ;
2012-03-14 16:56:09 -04:00
m_BlockTypes [ index ] = a_BlockType ;
2011-12-28 09:52:31 -05:00
2012-02-13 16:47:03 -05:00
{
cCSLock Lock ( m_CSBlockLists ) ;
m_PendingSendBlocks . push_back ( index ) ;
}
2012-03-05 11:41:57 -05:00
SetNibble ( m_BlockMeta , index , a_BlockMeta ) ;
2011-10-03 14:41:19 -04:00
2011-12-24 18:34:30 -05:00
// ONLY recalculate lighting if it's necessary!
2012-02-18 15:10:57 -05:00
if (
( g_BlockLightValue [ OldBlock ] ! = g_BlockLightValue [ a_BlockType ] ) | |
( g_BlockSpreadLightFalloff [ OldBlock ] ! = g_BlockSpreadLightFalloff [ a_BlockType ] ) | |
( g_BlockTransparent [ OldBlock ] ! = g_BlockTransparent [ a_BlockType ] )
)
2011-10-03 14:41:19 -04:00
{
2012-05-25 03:18:52 -04:00
m_IsLightValid = false ;
2011-10-03 14:41:19 -04:00
}
2012-02-21 08:44:06 -05:00
// Update heightmap, if needed:
2012-03-14 16:56:09 -04:00
if ( a_Y > = m_HeightMap [ a_X + a_Z * Width ] )
2012-02-21 08:44:06 -05:00
{
2012-02-21 10:18:02 -05:00
if ( a_BlockType ! = E_BLOCK_AIR )
{
2012-03-14 16:56:09 -04:00
m_HeightMap [ a_X + a_Z * Width ] = ( unsigned char ) a_Y ;
2012-02-21 10:18:02 -05:00
}
else
{
for ( int y = a_Y - 1 ; y > 0 ; - - y )
{
2012-03-14 16:56:09 -04:00
if ( m_BlockTypes [ MakeIndexNoCheck ( a_X , y , a_Z ) ] ! = E_BLOCK_AIR )
2012-02-21 10:18:02 -05:00
{
2012-03-14 16:56:09 -04:00
m_HeightMap [ a_X + a_Z * Width ] = ( unsigned char ) y ;
2012-02-21 10:18:02 -05:00
break ;
}
} // for y - column in m_BlockData
}
2012-02-21 08:44:06 -05:00
}
2011-10-03 14:41:19 -04:00
}
2012-02-08 14:49:57 -05:00
2011-10-03 14:41:19 -04:00
void cChunk : : SendBlockTo ( int a_X , int a_Y , int a_Z , cClientHandle * a_Client )
{
if ( a_Client = = 0 )
{
2012-02-13 16:47:03 -05:00
cCSLock Lock ( m_CSBlockLists ) ;
2012-03-04 08:54:33 -05:00
unsigned int index = MakeIndex ( a_X , a_Y , a_Z ) ;
if ( index ! = INDEX_OUT_OF_RANGE )
{
2012-03-09 19:25:05 -05:00
m_PendingSendBlocks . push_back ( index ) ;
}
else
{
LOGWARN ( " cChunk::SendBlockTo Index out of range! " ) ;
2012-03-04 08:54:33 -05:00
}
2011-10-03 14:41:19 -04:00
return ;
}
2012-02-13 16:47:03 -05:00
for ( cClientHandleList : : iterator itr = m_LoadedByClient . begin ( ) ; itr ! = m_LoadedByClient . end ( ) ; + + itr )
2011-10-03 14:41:19 -04:00
{
2012-02-13 16:47:03 -05:00
if ( * itr = = a_Client )
2011-10-03 14:41:19 -04:00
{
unsigned int index = MakeIndex ( a_X , a_Y , a_Z ) ;
2012-03-09 19:25:05 -05:00
Vector3i WorldPos = PositionToWorldPosition ( a_X , a_Y , a_Z ) ;
2011-10-03 14:41:19 -04:00
cPacket_BlockChange BlockChange ;
2012-03-09 19:25:05 -05:00
BlockChange . m_PosX = WorldPos . x ;
BlockChange . m_PosY = ( unsigned char ) WorldPos . y ;
BlockChange . m_PosZ = WorldPos . z ;
2012-03-04 08:54:33 -05:00
if ( index ! = INDEX_OUT_OF_RANGE )
{
2012-03-14 16:56:09 -04:00
BlockChange . m_BlockType = m_BlockTypes [ index ] ;
2012-03-05 11:41:57 -05:00
BlockChange . m_BlockMeta = GetNibble ( m_BlockMeta , index ) ;
2012-03-04 08:54:33 -05:00
} // else it's both 0
2011-10-03 14:41:19 -04:00
a_Client - > Send ( BlockChange ) ;
break ;
}
}
}
2012-02-08 14:49:57 -05:00
2011-10-03 14:41:19 -04:00
void cChunk : : AddBlockEntity ( cBlockEntity * a_BlockEntity )
{
2012-02-13 16:47:03 -05:00
cCSLock Lock ( m_CSBlockLists ) ;
m_BlockEntities . push_back ( a_BlockEntity ) ;
}
cBlockEntity * cChunk : : GetBlockEntity ( int a_X , int a_Y , int a_Z )
{
// Assumes that the m_CSBlockList is already locked, we're being called from SetBlock()
for ( cBlockEntityList : : iterator itr = m_BlockEntities . begin ( ) ; itr ! = m_BlockEntities . end ( ) ; + + itr )
{
if (
( ( * itr ) - > GetPosX ( ) = = a_X ) & &
( ( * itr ) - > GetPosY ( ) = = a_Y ) & &
( ( * itr ) - > GetPosZ ( ) = = a_Z )
)
{
return * itr ;
}
} // for itr - m_BlockEntities[]
return NULL ;
}
2012-02-15 09:22:44 -05:00
void cChunk : : UseBlockEntity ( cPlayer * a_Player , int a_X , int a_Y , int a_Z )
{
cBlockEntity * be = GetBlockEntity ( a_X , a_Y , a_Z ) ;
if ( be ! = NULL )
{
be - > UsedBy ( a_Player ) ;
}
}
2012-02-13 16:47:03 -05:00
void cChunk : : CollectPickupsByPlayer ( cPlayer * a_Player )
{
double PosX = a_Player - > GetPosX ( ) ;
double PosY = a_Player - > GetPosY ( ) ;
double PosZ = a_Player - > GetPosZ ( ) ;
for ( cEntityList : : iterator itr = m_Entities . begin ( ) ; itr ! = m_Entities . end ( ) ; + + itr )
{
2012-03-09 08:42:28 -05:00
if ( ( * itr ) - > GetEntityType ( ) ! = cEntity : : eEntityType_Pickup )
2012-02-13 16:47:03 -05:00
{
continue ; // Only pickups
}
float DiffX = ( float ) ( ( * itr ) - > GetPosX ( ) - PosX ) ;
float DiffY = ( float ) ( ( * itr ) - > GetPosY ( ) - PosY ) ;
float DiffZ = ( float ) ( ( * itr ) - > GetPosZ ( ) - PosZ ) ;
float SqrDist = DiffX * DiffX + DiffY * DiffY + DiffZ * DiffZ ;
if ( SqrDist < 1.5f * 1.5f ) // 1.5 block
{
2012-02-16 08:42:35 -05:00
MarkDirty ( ) ;
2012-02-13 16:47:03 -05:00
( reinterpret_cast < cPickup * > ( * itr ) ) - > CollectedBy ( a_Player ) ;
}
}
}
void cChunk : : UpdateSign ( int a_PosX , int a_PosY , int a_PosZ , const AString & a_Line1 , const AString & a_Line2 , const AString & a_Line3 , const AString & a_Line4 )
{
// Also sends update packets to all clients in the chunk
for ( cBlockEntityList : : iterator itr = m_BlockEntities . begin ( ) ; itr ! = m_BlockEntities . end ( ) ; + + itr )
{
if (
( ( * itr ) - > GetPosX ( ) = = a_PosX ) & &
( ( * itr ) - > GetPosY ( ) = = a_PosY ) & &
( ( * itr ) - > GetPosZ ( ) = = a_PosZ ) & &
(
( ( * itr ) - > GetBlockType ( ) = = E_BLOCK_WALLSIGN ) | |
( ( * itr ) - > GetBlockType ( ) = = E_BLOCK_SIGN_POST )
)
)
{
2012-02-16 08:42:35 -05:00
MarkDirty ( ) ;
2012-02-13 16:47:03 -05:00
( reinterpret_cast < cSignEntity * > ( * itr ) ) - > SetLines ( a_Line1 , a_Line2 , a_Line3 , a_Line4 ) ;
( * itr ) - > SendTo ( NULL ) ;
}
} // for itr - m_BlockEntities[]
2011-10-03 14:41:19 -04:00
}
2012-02-08 14:49:57 -05:00
2011-10-03 14:41:19 -04:00
void cChunk : : RemoveBlockEntity ( cBlockEntity * a_BlockEntity )
{
2012-02-13 16:47:03 -05:00
cCSLock Lock ( m_CSBlockLists ) ;
2012-02-16 08:42:35 -05:00
MarkDirty ( ) ;
2012-02-13 16:47:03 -05:00
m_BlockEntities . remove ( a_BlockEntity ) ;
2011-10-03 14:41:19 -04:00
}
2012-02-08 14:49:57 -05:00
2012-02-21 10:18:02 -05:00
bool cChunk : : AddClient ( cClientHandle * a_Client )
2011-10-03 14:41:19 -04:00
{
2012-02-23 16:21:37 -05:00
for ( cClientHandleList : : iterator itr = m_LoadedByClient . begin ( ) ; itr ! = m_LoadedByClient . end ( ) ; + + itr )
2012-02-13 16:47:03 -05:00
{
2012-02-23 16:21:37 -05:00
if ( a_Client = = * itr )
2012-02-21 10:18:02 -05:00
{
2012-02-23 16:21:37 -05:00
// Already there, nothing needed
return false ;
2012-02-21 10:18:02 -05:00
}
2012-02-13 16:47:03 -05:00
}
2012-02-23 16:21:37 -05:00
m_LoadedByClient . push_back ( a_Client ) ;
2011-10-03 14:41:19 -04:00
2012-02-13 16:47:03 -05:00
for ( cEntityList : : iterator itr = m_Entities . begin ( ) ; itr ! = m_Entities . end ( ) ; + + itr )
2011-10-03 14:41:19 -04:00
{
2012-02-21 10:18:02 -05:00
LOGD ( " cChunk: Entity #%d (%s) at [%i, %i, %i] spawning for player \" %s \" " , ( * itr ) - > GetUniqueID ( ) , ( * itr ) - > GetClass ( ) , m_PosX , m_PosY , m_PosZ , a_Client - > GetUsername ( ) . c_str ( ) ) ;
2011-10-03 14:41:19 -04:00
( * itr ) - > SpawnOn ( a_Client ) ;
}
2012-02-21 10:18:02 -05:00
return true ;
2011-10-03 14:41:19 -04:00
}
2012-02-08 14:49:57 -05:00
2011-10-03 14:41:19 -04:00
void cChunk : : RemoveClient ( cClientHandle * a_Client )
{
2012-03-22 11:53:40 -04:00
for ( cClientHandleList : : iterator itr = m_LoadedByClient . begin ( ) ; itr ! = m_LoadedByClient . end ( ) ; + + itr )
2011-10-03 14:41:19 -04:00
{
2012-03-22 11:53:40 -04:00
if ( * itr ! = a_Client )
2011-11-02 16:19:57 -04:00
{
2012-03-22 11:53:40 -04:00
continue ;
2011-11-02 16:19:57 -04:00
}
2012-03-22 11:53:40 -04:00
m_LoadedByClient . erase ( itr ) ;
if ( ! a_Client - > IsDestroyed ( ) )
{
for ( cEntityList : : iterator itr = m_Entities . begin ( ) ; itr ! = m_Entities . end ( ) ; + + itr )
{
LOGD ( " chunk [%i, %i] destroying entity #%i for player \" %s \" " , m_PosX , m_PosZ , ( * itr ) - > GetUniqueID ( ) , a_Client - > GetUsername ( ) . c_str ( ) ) ;
cPacket_DestroyEntity DestroyEntity ( * itr ) ;
a_Client - > Send ( DestroyEntity ) ;
}
}
return ;
} // for itr - m_LoadedByClient[]
2011-10-03 14:41:19 -04:00
}
2012-02-08 14:49:57 -05:00
2012-02-13 16:47:03 -05:00
bool cChunk : : HasClient ( cClientHandle * a_Client )
2011-10-03 14:41:19 -04:00
{
2012-02-13 16:47:03 -05:00
for ( cClientHandleList : : const_iterator itr = m_LoadedByClient . begin ( ) ; itr ! = m_LoadedByClient . end ( ) ; + + itr )
{
if ( ( * itr ) = = a_Client )
{
return true ;
}
}
return false ;
2011-10-03 14:41:19 -04:00
}
2012-02-08 14:49:57 -05:00
2012-02-16 12:45:26 -05:00
bool cChunk : : HasAnyClients ( void )
2011-10-03 14:41:19 -04:00
{
2012-02-13 16:47:03 -05:00
return ! m_LoadedByClient . empty ( ) ;
2011-10-03 14:41:19 -04:00
}
2012-02-08 14:49:57 -05:00
2012-02-20 11:39:00 -05:00
void cChunk : : AddEntity ( cEntity * a_Entity )
2011-10-03 14:41:19 -04:00
{
2012-03-09 08:42:28 -05:00
if ( a_Entity - > GetEntityType ( ) ! = cEntity : : eEntityType_Player )
2012-02-16 08:42:35 -05:00
{
MarkDirty ( ) ;
}
2012-02-13 16:47:03 -05:00
m_Entities . push_back ( a_Entity ) ;
2011-10-03 14:41:19 -04:00
}
2012-02-08 14:49:57 -05:00
2012-02-13 16:47:03 -05:00
void cChunk : : RemoveEntity ( cEntity * a_Entity )
2011-10-03 14:41:19 -04:00
{
2012-02-23 16:21:37 -05:00
size_t SizeBefore = m_Entities . size ( ) ;
m_Entities . remove ( a_Entity ) ;
size_t SizeAfter = m_Entities . size ( ) ;
2012-02-20 11:39:00 -05:00
if ( SizeBefore ! = SizeAfter )
2012-02-16 08:42:35 -05:00
{
2012-02-20 11:39:00 -05:00
// Mark as dirty if it was a server-generated entity:
2012-03-09 08:42:28 -05:00
if ( a_Entity - > GetEntityType ( ) ! = cEntity : : eEntityType_Player )
2012-02-20 11:39:00 -05:00
{
MarkDirty ( ) ;
}
2012-02-16 08:42:35 -05:00
}
2011-10-03 14:41:19 -04:00
}
2012-02-08 14:49:57 -05:00
2012-03-14 16:56:09 -04:00
BLOCKTYPE cChunk : : GetBlock ( int a_X , int a_Y , int a_Z )
2011-10-03 14:41:19 -04:00
{
2012-03-14 16:56:09 -04:00
if ( ( a_X < 0 ) | | ( a_X > = Width ) | | ( a_Y < 0 ) | | ( a_Y > = Height ) | | ( a_Z < 0 ) | | ( a_Z > = Width ) ) return 0 ; // Clip
2011-10-03 14:41:19 -04:00
2012-03-14 16:56:09 -04:00
return m_BlockTypes [ MakeIndexNoCheck ( a_X , a_Y , a_Z ) ] ;
2011-10-03 14:41:19 -04:00
}
2012-02-08 14:49:57 -05:00
2012-03-14 16:56:09 -04:00
BLOCKTYPE cChunk : : GetBlock ( int a_BlockIdx )
2011-10-03 14:41:19 -04:00
{
2012-03-14 16:56:09 -04:00
if ( a_BlockIdx < 0 | | a_BlockIdx > = NumBlocks ) return 0 ;
return m_BlockTypes [ a_BlockIdx ] ;
2011-10-03 14:41:19 -04:00
}
2012-02-08 14:49:57 -05:00
2012-02-23 16:21:37 -05:00
/*
// _X 2012_02_23: Loading in old format not supported anymore
2012-01-30 11:01:45 -05:00
/// Loads the chunk from the old-format disk file, erases the file afterwards. Returns true if successful
2011-10-03 14:41:19 -04:00
bool cChunk : : LoadFromDisk ( )
{
2012-02-01 08:43:47 -05:00
AString SourceFile ;
Printf ( SourceFile , " world/X%i_Y%i_Z%i.bin " , m_PosX , m_PosY , m_PosZ ) ;
2012-01-30 11:01:45 -05:00
cFile f ;
if ( ! f . Open ( SourceFile , cFile : : fmRead ) )
2011-10-03 14:41:19 -04:00
{
2012-01-30 11:01:45 -05:00
return false ;
}
2011-10-03 14:41:19 -04:00
2012-01-30 11:01:45 -05:00
if ( f . Read ( m_BlockData , sizeof ( m_BlockData ) ) ! = sizeof ( m_BlockData ) )
{
2012-02-01 08:50:09 -05:00
LOGERROR ( " ERROR READING FROM FILE %s " , SourceFile . c_str ( ) ) ;
2012-01-30 11:01:45 -05:00
return false ;
}
2012-02-23 16:21:37 -05:00
// Now load Block Entities:
2012-01-30 11:01:45 -05:00
ENUM_BLOCK_ID BlockType ;
while ( f . Read ( & BlockType , sizeof ( ENUM_BLOCK_ID ) ) = = sizeof ( ENUM_BLOCK_ID ) )
{
switch ( BlockType )
2011-10-03 14:41:19 -04:00
{
case E_BLOCK_CHEST :
2012-01-30 11:01:45 -05:00
{
2012-02-13 16:47:03 -05:00
cChestEntity * ChestEntity = new cChestEntity ( 0 , 0 , 0 , m_World ) ;
2012-01-30 11:01:45 -05:00
if ( ! ChestEntity - > LoadFromFile ( f ) )
2011-10-03 14:41:19 -04:00
{
2012-02-01 08:50:09 -05:00
LOGERROR ( " ERROR READING CHEST FROM FILE %s " , SourceFile . c_str ( ) ) ;
2012-01-30 11:01:45 -05:00
delete ChestEntity ;
return false ;
2011-10-03 14:41:19 -04:00
}
2012-02-13 16:47:03 -05:00
m_BlockEntities . push_back ( ChestEntity ) ;
2011-10-03 14:41:19 -04:00
break ;
2012-01-30 11:01:45 -05:00
}
2011-10-03 14:41:19 -04:00
case E_BLOCK_FURNACE :
2012-01-30 11:01:45 -05:00
{
2012-02-13 16:47:03 -05:00
cFurnaceEntity * FurnaceEntity = new cFurnaceEntity ( 0 , 0 , 0 , m_World ) ;
2012-01-30 11:01:45 -05:00
if ( ! FurnaceEntity - > LoadFromFile ( f ) )
2011-10-03 14:41:19 -04:00
{
2012-02-01 08:50:09 -05:00
LOGERROR ( " ERROR READING FURNACE FROM FILE %s " , SourceFile . c_str ( ) ) ;
2012-01-30 11:01:45 -05:00
delete FurnaceEntity ;
return false ;
2011-10-03 14:41:19 -04:00
}
2012-02-13 16:47:03 -05:00
m_BlockEntities . push_back ( FurnaceEntity ) ;
2011-10-03 14:41:19 -04:00
break ;
2012-01-30 11:01:45 -05:00
}
2011-10-03 14:41:19 -04:00
case E_BLOCK_SIGN_POST :
case E_BLOCK_WALLSIGN :
2012-01-30 11:01:45 -05:00
{
2012-02-13 16:47:03 -05:00
cSignEntity * SignEntity = new cSignEntity ( BlockType , 0 , 0 , 0 , m_World ) ;
2012-01-30 11:01:45 -05:00
if ( ! SignEntity - > LoadFromFile ( f ) )
2011-10-03 14:41:19 -04:00
{
2012-02-01 08:50:09 -05:00
LOGERROR ( " ERROR READING SIGN FROM FILE %s " , SourceFile . c_str ( ) ) ;
2012-01-30 11:01:45 -05:00
delete SignEntity ;
return false ;
2011-10-03 14:41:19 -04:00
}
2012-02-13 16:47:03 -05:00
m_BlockEntities . push_back ( SignEntity ) ;
2011-10-03 14:41:19 -04:00
break ;
2012-01-30 11:01:45 -05:00
}
2011-10-03 14:41:19 -04:00
default :
2012-01-30 11:01:45 -05:00
{
2012-02-19 18:00:00 -05:00
ASSERT ( ! " Unhandled block entity in file " ) ;
2011-10-03 14:41:19 -04:00
break ;
}
}
2012-01-30 11:01:45 -05:00
}
f . Close ( ) ;
2011-10-03 14:41:19 -04:00
2012-01-30 11:01:45 -05:00
// Delete old format file
2012-02-01 08:43:47 -05:00
if ( std : : remove ( SourceFile . c_str ( ) ) ! = 0 )
2012-01-30 11:01:45 -05:00
{
2012-02-01 08:50:09 -05:00
LOGERROR ( " Could not delete file %s " , SourceFile . c_str ( ) ) ;
2011-10-03 14:41:19 -04:00
}
else
{
2012-02-01 08:50:09 -05:00
LOGINFO ( " Successfully deleted old format file \" %s \" " , SourceFile . c_str ( ) ) ;
2011-10-03 14:41:19 -04:00
}
2012-02-16 08:42:35 -05:00
m_IsDirty = false ;
2012-01-30 11:01:45 -05:00
return true ;
2011-10-03 14:41:19 -04:00
}
2012-02-23 16:21:37 -05:00
*/
2011-10-03 14:41:19 -04:00
2012-01-30 11:01:45 -05:00
2012-02-13 16:47:03 -05:00
void cChunk : : Broadcast ( const cPacket * a_Packet , cClientHandle * a_Exclude )
2011-10-03 14:41:19 -04:00
{
2012-02-13 16:47:03 -05:00
for ( cClientHandleList : : const_iterator itr = m_LoadedByClient . begin ( ) ; itr ! = m_LoadedByClient . end ( ) ; + + itr )
2011-10-03 14:41:19 -04:00
{
2012-01-30 11:01:45 -05:00
if ( * itr = = a_Exclude )
{
continue ;
}
2012-03-10 16:34:47 -05:00
( * itr ) - > Send ( * a_Packet ) ;
2012-01-30 11:01:45 -05:00
} // for itr - LoadedByClient[]
2011-10-03 14:41:19 -04:00
}
2012-01-30 11:01:45 -05:00
2011-12-22 16:36:24 -05:00
void cChunk : : PositionToWorldPosition ( int a_ChunkX , int a_ChunkY , int a_ChunkZ , int & a_X , int & a_Y , int & a_Z )
{
a_Y = a_ChunkY ;
2012-03-14 16:56:09 -04:00
a_X = m_PosX * Width + a_ChunkX ;
a_Z = m_PosZ * Width + a_ChunkZ ;
2011-12-31 16:08:23 -05:00
}
2012-01-30 11:01:45 -05:00
2012-03-09 19:25:05 -05:00
Vector3i cChunk : : PositionToWorldPosition ( int a_ChunkX , int a_ChunkY , int a_ChunkZ )
2012-03-01 10:18:59 -05:00
{
2012-03-14 16:56:09 -04:00
return Vector3i ( m_PosX * Width + a_ChunkX , m_PosY * Height + a_ChunkY , m_PosZ * Width + a_ChunkZ ) ;
2012-03-01 10:18:59 -05:00
}
2011-12-31 16:08:23 -05:00
# if !C_CHUNK_USE_INLINE
2012-03-09 19:25:05 -05:00
# include "cChunk.inl.h"
2012-01-30 11:01:45 -05:00
# endif