2014-08-19 22:14:37 +02:00
2012-06-14 13:06:06 +00:00
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#ifndef _WIN32
#include <cstdlib>
#endif
2012-09-23 22:09:57 +00:00
#include "Chunk.h"
#include "World.h"
#include "ClientHandle.h"
#include "Server.h"
2013-11-26 17:14:46 +00:00
#include "zlib/zlib.h"
2012-06-14 13:06:06 +00:00
#include "Defines.h"
2014-07-30 22:19:51 +02:00
#include "BlockEntities/BeaconEntity.h"
2015-09-24 10:48:33 +02:00
#include "BlockEntities/BrewingstandEntity.h"
2013-05-28 19:12:47 +00:00
#include "BlockEntities/ChestEntity.h"
#include "BlockEntities/DispenserEntity.h"
#include "BlockEntities/DropperEntity.h"
2014-09-19 23:00:54 +02:00
#include "BlockEntities/FlowerPotEntity.h"
2013-05-28 19:12:47 +00:00
#include "BlockEntities/FurnaceEntity.h"
2013-06-13 07:36:43 +00:00
#include "BlockEntities/HopperEntity.h"
2013-05-28 19:12:47 +00:00
#include "BlockEntities/JukeboxEntity.h"
2014-09-19 23:00:54 +02:00
#include "BlockEntities/MobHeadEntity.h"
#include "BlockEntities/MobSpawnerEntity.h"
2013-05-28 19:12:47 +00:00
#include "BlockEntities/NoteEntity.h"
#include "BlockEntities/SignEntity.h"
2013-08-19 11:39:13 +02:00
#include "Entities/Pickup.h"
2012-09-23 22:09:57 +00:00
#include "Item.h"
2014-11-18 12:07:08 +01:00
#include "Noise/Noise.h"
2012-09-23 22:09:57 +00:00
#include "Root.h"
2013-08-19 11:39:13 +02:00
#include "Entities/Player.h"
2012-07-09 07:00:28 +00:00
#include "BlockArea.h"
2013-12-08 12:17:54 +01:00
#include "Bindings/PluginManager.h"
2012-09-29 13:59:32 +00:00
#include "Blocks/BlockHandler.h"
2013-03-02 15:44:31 +00:00
#include "Simulator/FluidSimulator.h"
2013-09-07 22:19:56 +02:00
#include "MobCensus.h"
2013-09-08 00:11:38 +02:00
#include "MobSpawner.h"
2014-02-02 06:49:37 -08:00
#include "BlockInServerPluginInterface.h"
2014-07-24 18:32:05 +02:00
#include "SetChunkData.h"
2014-09-03 17:00:26 +02:00
#include "BoundingBox.h"
2014-09-26 18:13:19 +01:00
#include "Blocks/ChunkInterface.h"
2012-06-14 13:06:06 +00:00
2013-11-27 09:17:25 +01:00
#include "json/json.h"
2012-06-14 13:06:06 +00:00
2012-07-02 19:22:05 +00:00
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2012-06-14 13:06:06 +00:00
// sSetBlock:
2014-12-24 07:20:17 +01:00
sSetBlock :: sSetBlock ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta ) :
m_RelX ( a_BlockX ),
m_RelY ( a_BlockY ),
m_RelZ ( a_BlockZ ),
m_BlockType ( a_BlockType ),
m_BlockMeta ( a_BlockMeta )
2012-06-14 13:06:06 +00:00
{
2014-12-24 07:20:17 +01:00
cChunkDef :: AbsoluteToRelative ( m_RelX , m_RelY , m_RelZ , m_ChunkX , m_ChunkZ );
2012-06-14 13:06:06 +00:00
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2012-06-14 13:06:06 +00:00
// cChunk:
2012-12-14 22:38:30 +00:00
cChunk :: cChunk (
2014-08-28 11:36:35 +02:00
int a_ChunkX , int a_ChunkZ ,
2012-12-14 22:38:30 +00:00
cChunkMap * a_ChunkMap , cWorld * a_World ,
2014-06-16 15:12:50 +01:00
cChunk * a_NeighborXM , cChunk * a_NeighborXP , cChunk * a_NeighborZM , cChunk * a_NeighborZP ,
cAllocationPool < cChunkData :: sChunkSection > & a_Pool
2013-12-20 19:10:07 +01:00
) :
2014-09-05 22:16:48 +02:00
m_Presence ( cpInvalid ),
2014-12-21 22:19:53 +01:00
m_ShouldGenerateIfLoadFailed ( false ),
2013-12-20 19:10:07 +01:00
m_IsLightValid ( false ),
m_IsDirty ( false ),
m_IsSaving ( false ),
m_HasLoadFailed ( false ),
m_StayCount ( 0 ),
m_PosX ( a_ChunkX ),
m_PosZ ( a_ChunkZ ),
m_World ( a_World ),
m_ChunkMap ( a_ChunkMap ),
2014-06-16 15:12:50 +01:00
m_ChunkData ( a_Pool ),
2013-12-20 19:10:07 +01:00
m_BlockTickX ( 0 ),
m_BlockTickY ( 0 ),
m_BlockTickZ ( 0 ),
m_NeighborXM ( a_NeighborXM ),
m_NeighborXP ( a_NeighborXP ),
m_NeighborZM ( a_NeighborZM ),
m_NeighborZP ( a_NeighborZP ),
m_WaterSimulatorData ( a_World -> GetWaterSimulator () -> CreateChunkData ()),
2014-07-10 18:18:32 +02:00
m_LavaSimulatorData ( a_World -> GetLavaSimulator () -> CreateChunkData ()),
2014-09-28 17:57:01 +01:00
m_RedstoneSimulatorData ( a_World -> GetRedstoneSimulator () -> CreateChunkData ()),
2014-12-21 22:19:53 +01:00
m_IsRedstoneDirty ( false ),
2014-07-10 18:18:32 +02:00
m_AlwaysTicked ( 0 )
2012-06-14 13:06:06 +00:00
{
2014-10-20 21:55:07 +01:00
if ( a_NeighborXM != nullptr )
2012-12-14 22:38:30 +00:00
{
a_NeighborXM -> m_NeighborXP = this ;
}
2014-10-20 21:55:07 +01:00
if ( a_NeighborXP != nullptr )
2012-12-14 22:38:30 +00:00
{
a_NeighborXP -> m_NeighborXM = this ;
}
2014-10-20 21:55:07 +01:00
if ( a_NeighborZM != nullptr )
2012-12-14 22:38:30 +00:00
{
a_NeighborZM -> m_NeighborZP = this ;
}
2014-10-20 21:55:07 +01:00
if ( a_NeighborZP != nullptr )
2012-12-14 22:38:30 +00:00
{
a_NeighborZP -> m_NeighborZM = this ;
}
2012-06-14 13:06:06 +00:00
}
cChunk ::~ cChunk ()
{
2014-10-15 19:01:55 +02:00
cPluginManager :: Get () -> CallHookChunkUnloaded ( * m_World , m_PosX , m_PosZ );
2013-02-05 19:57:22 +00:00
2014-07-21 15:19:48 +02:00
// LOGINFO("### delete cChunk() (%i, %i) from %p, thread 0x%x ###", m_PosX, m_PosZ, this, GetCurrentThreadId());
2012-06-14 13:06:06 +00:00
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (); itr != m_BlockEntities . end (); ++ itr )
{
delete * itr ;
}
m_BlockEntities . clear ();
// Remove and destroy all entities that are not players:
cEntityList Entities ;
2013-04-13 21:02:10 +00:00
std :: swap ( Entities , m_Entities ); // Need another list because cEntity destructors check if they've been removed from chunk
for ( cEntityList :: const_iterator itr = Entities . begin (); itr != Entities . end (); ++ itr )
2012-06-14 13:06:06 +00:00
{
2012-12-21 12:52:14 +00:00
if ( ! ( * itr ) -> IsPlayer ())
2012-06-14 13:06:06 +00:00
{
2013-06-25 06:36:59 +00:00
( * itr ) -> Destroy ( false );
2013-04-13 21:02:10 +00:00
delete * itr ;
2012-06-14 13:06:06 +00:00
}
}
2012-12-14 22:38:30 +00:00
2014-10-20 21:55:07 +01:00
if ( m_NeighborXM != nullptr )
2012-12-14 22:38:30 +00:00
{
2014-10-20 21:55:07 +01:00
m_NeighborXM -> m_NeighborXP = nullptr ;
2012-12-14 22:38:30 +00:00
}
2014-10-20 21:55:07 +01:00
if ( m_NeighborXP != nullptr )
2012-12-14 22:38:30 +00:00
{
2014-10-20 21:55:07 +01:00
m_NeighborXP -> m_NeighborXM = nullptr ;
2012-12-14 22:38:30 +00:00
}
2014-10-20 21:55:07 +01:00
if ( m_NeighborZM != nullptr )
2012-12-14 22:38:30 +00:00
{
2014-10-20 21:55:07 +01:00
m_NeighborZM -> m_NeighborZP = nullptr ;
2012-12-14 22:38:30 +00:00
}
2014-10-20 21:55:07 +01:00
if ( m_NeighborZP != nullptr )
2012-12-14 22:38:30 +00:00
{
2014-10-20 21:55:07 +01:00
m_NeighborZP -> m_NeighborZM = nullptr ;
2012-12-14 22:38:30 +00:00
}
2013-03-02 15:44:31 +00:00
delete m_WaterSimulatorData ;
2014-10-20 21:55:07 +01:00
m_WaterSimulatorData = nullptr ;
2013-03-02 15:44:31 +00:00
delete m_LavaSimulatorData ;
2014-10-20 21:55:07 +01:00
m_LavaSimulatorData = nullptr ;
2014-09-26 17:00:34 +01:00
delete m_RedstoneSimulatorData ;
2014-10-20 21:55:07 +01:00
m_RedstoneSimulatorData = nullptr ;
2012-06-14 13:06:06 +00:00
}
2014-09-05 22:16:48 +02:00
void cChunk :: SetPresence ( cChunk :: ePresence a_Presence )
2012-06-14 13:06:06 +00:00
{
2014-09-05 22:16:48 +02:00
m_Presence = a_Presence ;
if ( a_Presence == cpPresent )
{
m_World -> GetChunkMap () -> ChunkValidated ();
}
}
void cChunk :: SetShouldGenerateIfLoadFailed ( bool a_ShouldGenerateIfLoadFailed )
{
m_ShouldGenerateIfLoadFailed = a_ShouldGenerateIfLoadFailed ;
2012-06-14 13:06:06 +00:00
}
void cChunk :: MarkRegenerating ( void )
{
2014-09-05 22:16:48 +02:00
// Set as queued again:
SetPresence ( cpQueued );
2012-06-14 13:06:06 +00:00
// Tell all clients attached to this chunk that they want this chunk:
2015-09-16 17:04:05 +01:00
for ( auto ClientHandle : m_LoadedByClient )
2012-06-14 13:06:06 +00:00
{
2015-09-16 17:04:05 +01:00
ClientHandle -> AddWantedChunk ( m_PosX , m_PosZ );
2012-06-14 13:06:06 +00:00
} // for itr - m_LoadedByClient[]
}
bool cChunk :: CanUnload ( void )
{
2014-09-05 22:16:48 +02:00
return
m_LoadedByClient . empty () && // The chunk is not used by any client
! m_IsDirty && // The chunk has been saved properly or hasn't been touched since the load / gen
( m_StayCount == 0 ) && // The chunk is not in a ChunkStay
( m_Presence != cpQueued ) ; // The chunk is not queued for loading / generating (otherwise multi-load / multi-gen could occur)
2012-06-14 13:06:06 +00: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 ;
2014-09-05 22:16:48 +02:00
SetPresence ( cpPresent );
2012-06-14 13:06:06 +00:00
}
void cChunk :: MarkLoadFailed ( void )
{
2014-09-05 22:16:48 +02:00
ASSERT ( m_Presence == cpQueued );
// If the chunk is marked as needed, generate it:
if ( m_ShouldGenerateIfLoadFailed )
2012-06-14 13:06:06 +00:00
{
2014-09-05 22:16:48 +02:00
m_World -> GetGenerator (). QueueGenerateChunk ( m_PosX , m_PosZ , false );
}
else
{
m_Presence = cpInvalid ;
2012-06-14 13:06:06 +00:00
}
}
void cChunk :: GetAllData ( cChunkDataCallback & a_Callback )
{
2014-09-05 22:16:48 +02:00
ASSERT ( m_Presence == cpPresent );
2014-05-21 19:58:48 +01:00
a_Callback . HeightMap ( & m_HeightMap );
a_Callback . BiomeData ( & m_BiomeMap );
2014-03-23 14:34:19 +00:00
2014-05-21 19:58:48 +01:00
a_Callback . LightIsValid ( m_IsLightValid );
2014-04-04 23:16:52 +01:00
2014-05-21 19:58:48 +01:00
a_Callback . ChunkData ( m_ChunkData );
2012-06-14 13:06:06 +00:00
for ( cEntityList :: iterator itr = m_Entities . begin (); itr != m_Entities . end (); ++ itr )
{
a_Callback . Entity ( * itr );
}
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (); itr != m_BlockEntities . end (); ++ itr )
{
a_Callback . BlockEntity ( * itr );
}
}
2014-07-24 18:32:05 +02:00
void cChunk :: SetAllData ( cSetChunkData & a_SetChunkData )
2012-06-14 13:06:06 +00:00
{
2014-07-24 18:32:05 +02:00
ASSERT ( a_SetChunkData . IsHeightMapValid ());
ASSERT ( a_SetChunkData . AreBiomesValid ());
2014-09-05 22:16:48 +02:00
ASSERT ( IsQueued ());
2014-07-24 18:32:05 +02:00
memcpy ( m_BiomeMap , a_SetChunkData . GetBiomes (), sizeof ( m_BiomeMap ));
memcpy ( m_HeightMap , a_SetChunkData . GetHeightMap (), sizeof ( m_HeightMap ));
2014-04-02 22:53:03 +01:00
2014-07-24 18:32:05 +02:00
m_ChunkData . SetBlockTypes ( a_SetChunkData . GetBlockTypes ());
m_ChunkData . SetMetas ( a_SetChunkData . GetBlockMetas ());
if ( a_SetChunkData . IsLightValid ())
2012-06-14 13:06:06 +00:00
{
2014-07-24 18:32:05 +02:00
m_ChunkData . SetBlockLight ( a_SetChunkData . GetBlockLight ());
m_ChunkData . SetSkyLight ( a_SetChunkData . GetSkyLight ());
m_IsLightValid = true ;
2012-06-14 13:06:06 +00:00
}
2014-07-24 18:32:05 +02:00
else
2014-04-06 23:30:21 +01:00
{
2014-07-24 18:32:05 +02:00
m_IsLightValid = false ;
2014-04-07 20:57:14 +01:00
}
2014-04-06 23:30:21 +01:00
2012-06-14 13:06:06 +00:00
// Clear the block entities present - either the loader / saver has better, or we'll create empty ones:
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (); itr != m_BlockEntities . end (); ++ itr )
{
delete * itr ;
}
2014-07-24 18:32:05 +02:00
m_BlockEntities . clear ();
std :: swap ( a_SetChunkData . GetBlockEntities (), m_BlockEntities );
2014-08-29 19:19:27 +03:00
// Check that all block entities have a valid blocktype at their respective coords (DEBUG-mode only):
#ifdef _DEBUG
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (); itr != m_BlockEntities . end (); ++ itr )
{
BLOCKTYPE EntityBlockType = ( * itr ) -> GetBlockType ();
BLOCKTYPE WorldBlockType = GetBlock (( * itr ) -> GetRelX (), ( * itr ) -> GetPosY (), ( * itr ) -> GetRelZ ());
2014-10-18 19:54:34 +01:00
ASSERT ( WorldBlockType == EntityBlockType );
2014-08-29 19:19:27 +03:00
} // for itr - m_BlockEntities
#endif // _DEBUG
2012-06-14 13:06:06 +00:00
2013-04-06 21:21:57 +00:00
// Set all block entities' World variable:
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (); itr != m_BlockEntities . end (); ++ itr )
{
( * itr ) -> SetWorld ( m_World );
}
2012-06-14 13:06:06 +00:00
// Create block entities that the loader didn't load; fill them with defaults
CreateBlockEntities ();
2013-03-03 15:33:55 +00:00
2013-03-14 20:02:52 +00:00
// Set the chunk data as valid. This may be needed for some simulators that perform actions upon block adding (Vaporize)
2014-09-05 22:16:48 +02:00
SetPresence ( cpPresent );
2013-03-14 20:02:52 +00:00
2013-03-03 15:33:55 +00:00
// Wake up all simulators for their respective blocks:
WakeUpSimulators ();
2012-06-14 13:06:06 +00:00
m_HasLoadFailed = false ;
}
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 :)
2014-04-06 23:30:21 +01:00
2014-05-24 13:33:40 +01:00
m_ChunkData . SetBlockLight ( a_BlockLight );
2014-04-06 23:30:21 +01:00
2014-05-21 20:08:34 +01:00
m_ChunkData . SetSkyLight ( a_SkyLight );
2014-04-06 23:30:21 +01:00
2012-06-14 13:06:06 +00:00
m_IsLightValid = true ;
}
void cChunk :: GetBlockTypes ( BLOCKTYPE * a_BlockTypes )
{
2014-05-29 18:25:08 +02:00
m_ChunkData . CopyBlockTypes ( a_BlockTypes );
2012-06-14 13:06:06 +00:00
}
2012-10-06 16:58:31 +00:00
void cChunk :: WriteBlockArea ( cBlockArea & a_Area , int a_MinBlockX , int a_MinBlockY , int a_MinBlockZ , int a_DataTypes )
{
if (( a_DataTypes & ( cBlockArea :: baTypes | cBlockArea :: baMetas )) != ( cBlockArea :: baTypes | cBlockArea :: baMetas ))
{
LOGWARNING ( "cChunk::WriteBlockArea(): unsupported datatype request, can write only types + metas (0x%x), requested 0x%x. Ignoring." ,
( cBlockArea :: baTypes | cBlockArea :: baMetas ), a_DataTypes & ( cBlockArea :: baTypes | cBlockArea :: baMetas )
);
return ;
}
// SizeX, SizeZ are the dimensions of the block data to copy to the chunk (size of the geometric union)
int BlockStartX = std :: max ( a_MinBlockX , m_PosX * cChunkDef :: Width );
int BlockEndX = std :: min ( a_MinBlockX + a_Area . GetSizeX (), ( m_PosX + 1 ) * cChunkDef :: Width );
int BlockStartZ = std :: max ( a_MinBlockZ , m_PosZ * cChunkDef :: Width );
int BlockEndZ = std :: min ( a_MinBlockZ + a_Area . GetSizeZ (), ( m_PosZ + 1 ) * cChunkDef :: Width );
int SizeX = BlockEndX - BlockStartX ;
int SizeZ = BlockEndZ - BlockStartZ ;
int OffX = BlockStartX - m_PosX * cChunkDef :: Width ;
int OffZ = BlockStartZ - m_PosZ * cChunkDef :: Width ;
int BaseX = BlockStartX - a_MinBlockX ;
int BaseZ = BlockStartZ - a_MinBlockZ ;
2015-06-30 14:08:21 +02:00
int SizeY = std :: min ( a_Area . GetSizeY (), cChunkDef :: Height - a_MinBlockY );
2012-10-06 16:58:31 +00:00
// TODO: Improve this by not calling FastSetBlock() and doing the processing here
// so that the heightmap is touched only once for each column.
BLOCKTYPE * AreaBlockTypes = a_Area . GetBlockTypes ();
NIBBLETYPE * AreaBlockMetas = a_Area . GetBlockMetas ();
for ( int y = 0 ; y < SizeY ; y ++ )
{
int ChunkY = a_MinBlockY + y ;
int AreaY = y ;
for ( int z = 0 ; z < SizeZ ; z ++ )
{
int ChunkZ = OffZ + z ;
int AreaZ = BaseZ + z ;
for ( int x = 0 ; x < SizeX ; x ++ )
{
int ChunkX = OffX + x ;
int AreaX = BaseX + x ;
int idx = a_Area . MakeIndex ( AreaX , AreaY , AreaZ );
BLOCKTYPE BlockType = AreaBlockTypes [ idx ];
NIBBLETYPE BlockMeta = AreaBlockMetas [ idx ];
FastSetBlock ( ChunkX , ChunkY , ChunkZ , BlockType , BlockMeta );
} // for x
} // for z
} // for y
}
2012-06-14 13:06:06 +00:00
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 ;
}
void cChunk :: Stay ( bool a_Stay )
{
m_StayCount += ( a_Stay ? 1 : - 1 );
ASSERT ( m_StayCount >= 0 );
}
2015-05-09 11:16:56 +02:00
void cChunk :: CollectMobCensus ( cMobCensus & toFill )
2013-09-07 22:19:56 +02:00
{
2013-09-08 12:25:07 +02:00
toFill . CollectSpawnableChunk ( * this );
2015-09-16 17:04:05 +01:00
std :: vector < Vector3d > PlayerPositions ;
PlayerPositions . reserve ( m_LoadedByClient . size ());
for ( auto ClientHandle : m_LoadedByClient )
2013-09-07 22:19:56 +02:00
{
2015-09-16 17:04:05 +01:00
const cPlayer * currentPlayer = ClientHandle -> GetPlayer ();
PlayerPositions . push_back ( currentPlayer -> GetPosition ());
2014-07-17 16:33:09 +02:00
}
2013-09-07 22:19:56 +02:00
Vector3d currentPosition ;
2015-09-16 17:04:05 +01:00
for ( auto entity : m_Entities )
2013-09-07 22:19:56 +02:00
{
2014-07-17 19:13:23 +02:00
// LOGD("Counting entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass());
2015-09-16 17:04:05 +01:00
if ( entity -> IsMob ())
2013-09-07 22:19:56 +02:00
{
2015-09-16 17:04:05 +01:00
auto & Monster = reinterpret_cast < cMonster &> ( * entity );
2013-09-09 18:45:39 +02:00
currentPosition = Monster . GetPosition ();
2015-09-24 16:04:44 +02:00
for ( const auto & PlayerPos : PlayerPositions )
2013-09-07 22:19:56 +02:00
{
2015-09-16 17:04:05 +01:00
toFill . CollectMob ( Monster , * this , ( currentPosition - PlayerPos ). SqrLength ());
2013-09-07 22:19:56 +02:00
}
}
} // for itr - m_Entitites[]
}
2014-07-19 14:53:41 +02:00
void cChunk :: GetThreeRandomNumbers ( int & a_X , int & a_Y , int & a_Z , int a_MaxX , int a_MaxY , int a_MaxZ )
2013-09-08 00:11:38 +02:00
{
2013-09-08 12:20:19 +02:00
ASSERT ( a_MaxX * a_MaxY * a_MaxZ * 8 < 0x00ffffff );
2013-09-08 00:11:38 +02:00
int Random = m_World -> GetTickRandomNumber ( 0x00ffffff );
a_X = Random % ( a_MaxX * 2 );
a_Y = ( Random / ( a_MaxX * 2 )) % ( a_MaxY * 2 );
a_Z = (( Random / ( a_MaxX * 2 )) / ( a_MaxY * 2 )) % ( a_MaxZ * 2 );
a_X /= 2 ;
a_Y /= 2 ;
a_Z /= 2 ;
}
2014-07-10 23:04:33 +02:00
void cChunk :: GetRandomBlockCoords ( int & a_X , int & a_Y , int & a_Z )
2013-09-08 00:11:38 +02:00
{
// MG TODO : check if this kind of optimization (only one random call) is still needed
// MG TODO : if so propagate it
2014-07-10 23:04:33 +02:00
GetThreeRandomNumbers ( a_X , a_Y , a_Z , Width , Height - 2 , Width );
2013-09-08 00:11:38 +02:00
a_Y ++ ;
}
2015-05-09 11:16:56 +02:00
void cChunk :: SpawnMobs ( cMobSpawner & a_MobSpawner )
2013-09-08 00:11:38 +02:00
{
2014-07-10 23:04:33 +02:00
int CenterX , CenterY , CenterZ ;
GetRandomBlockCoords ( CenterX , CenterY , CenterZ );
2013-10-21 09:41:48 -06:00
2014-07-10 23:04:33 +02:00
BLOCKTYPE PackCenterBlock = GetBlock ( CenterX , CenterY , CenterZ );
if ( ! a_MobSpawner . CheckPackCenter ( PackCenterBlock ))
2013-09-08 02:47:02 +02:00
{
2014-07-10 23:04:33 +02:00
return ;
2013-09-08 02:47:02 +02:00
}
2014-07-10 23:04:33 +02:00
a_MobSpawner . NewPack ();
int NumberOfTries = 0 ;
int NumberOfSuccess = 0 ;
int MaxNbOfSuccess = 4 ; // This can be changed during the process for Wolves and Ghasts
while (( NumberOfTries < 12 ) && ( NumberOfSuccess < MaxNbOfSuccess ))
{
const int HorizontalRange = 20 ; // MG TODO : relocate
const int VerticalRange = 0 ; // MG TODO : relocate
int TryX , TryY , TryZ ;
GetThreeRandomNumbers ( TryX , TryY , TryZ , 2 * HorizontalRange + 1 , 2 * VerticalRange + 1 , 2 * HorizontalRange + 1 );
TryX -= HorizontalRange ;
TryY -= VerticalRange ;
TryZ -= HorizontalRange ;
TryX += CenterX ;
TryY += CenterY ;
TryZ += CenterZ ;
ASSERT ( TryY > 0 );
ASSERT ( TryY < cChunkDef :: Height - 1 );
EMCSBiome Biome = m_ChunkMap -> GetBiomeAt ( TryX , TryZ );
// MG TODO :
// Moon cycle (for slime)
// check player and playerspawn presence < 24 blocks
// check mobs presence on the block
// MG TODO : check that "Level" really means Y
/*
NIBBLETYPE SkyLight = 0;
NIBBLETYPE BlockLight = 0;
*/
2013-09-08 02:47:02 +02:00
2014-07-10 23:04:33 +02:00
NumberOfTries ++ ;
if ( ! IsLightValid ())
2013-09-08 02:47:02 +02:00
{
2014-07-10 23:04:33 +02:00
continue ;
2013-09-08 02:47:02 +02:00
}
2014-07-10 23:04:33 +02:00
cEntity * newMob = a_MobSpawner . TryToSpawnHere ( this , TryX , TryY , TryZ , Biome , MaxNbOfSuccess );
2014-10-20 21:55:07 +01:00
if ( newMob == nullptr )
2014-07-10 23:04:33 +02:00
{
continue ;
}
int WorldX , WorldY , WorldZ ;
PositionToWorldPosition ( TryX , TryY , TryZ , WorldX , WorldY , WorldZ );
double ActualX = WorldX + 0.5 ;
double ActualZ = WorldZ + 0.5 ;
newMob -> SetPosition ( ActualX , WorldY , ActualZ );
LOGD ( "Spawning %s #%i at {%d, %d, %d}" , newMob -> GetClass (), newMob -> GetUniqueID (), WorldX , WorldY , WorldZ );
NumberOfSuccess ++ ;
} // while (retry)
2013-09-08 00:11:38 +02:00
}
2012-06-14 13:06:06 +00:00
2015-01-11 21:12:26 +00:00
void cChunk :: Tick ( std :: chrono :: milliseconds a_Dt )
2012-06-14 13:06:06 +00:00
{
2012-08-25 17:52:08 +00:00
BroadcastPendingBlockChanges ();
2012-06-14 13:06:06 +00:00
2013-08-18 22:44:22 +02:00
// Set all blocks that have been queued for setting later:
ProcessQueuedSetBlocks ();
2012-06-14 13:06:06 +00:00
CheckBlocks ();
2013-03-01 19:35:29 +00:00
// Tick simulators:
m_World -> GetSimulatorManager () -> SimulateChunk ( a_Dt , m_PosX , m_PosZ , this );
2013-04-13 21:02:10 +00:00
TickBlocks ();
2012-06-14 13:06:06 +00:00
2013-04-01 20:56:25 +00:00
// Tick all block entities in this chunk:
2012-06-14 13:06:06 +00:00
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (); itr != m_BlockEntities . end (); ++ itr )
{
2013-05-28 18:50:44 +00:00
m_IsDirty = ( * itr ) -> Tick ( a_Dt , * this ) | m_IsDirty ;
2012-06-14 13:06:06 +00:00
}
2013-04-01 20:56:25 +00:00
2014-07-22 17:26:48 +01:00
for ( cEntityList :: iterator itr = m_Entities . begin (); itr != m_Entities . end ();)
2014-08-14 01:03:30 +02:00
{
2014-07-22 17:26:48 +01:00
if ( ! (( * itr ) -> IsMob ())) // Mobs are ticked inside cWorld::TickMobs() (as we don't have to tick them if they are far away from players)
2013-09-08 01:43:55 +02:00
{
2014-07-22 17:26:48 +01:00
// Tick all entities in this chunk (except mobs):
2013-09-08 01:43:55 +02:00
( * itr ) -> Tick ( a_Dt , * this );
}
2014-07-22 17:26:48 +01:00
2014-07-21 22:49:06 +01:00
if (( * itr ) -> IsDestroyed ()) // Remove all entities that were scheduled for removal:
2014-05-31 22:28:51 +01:00
{
2014-06-12 15:21:07 +01:00
LOGD ( "Destroying entity #%i (%s)" , ( * itr ) -> GetUniqueID (), ( * itr ) -> GetClass ());
2014-06-21 22:07:38 +01:00
MarkDirty ();
2014-06-12 15:21:07 +01:00
cEntity * ToDelete = * itr ;
2014-05-31 22:28:51 +01:00
itr = m_Entities . erase ( itr );
2014-06-12 15:21:07 +01:00
delete ToDelete ;
2014-05-31 22:28:51 +01:00
}
2014-08-14 01:03:30 +02:00
else if (( * itr ) -> IsWorldTravellingFrom ( m_World ))
2014-06-21 22:07:38 +01:00
{
2014-08-14 01:03:30 +02:00
// Remove all entities that are travelling to another world
2015-05-26 20:35:28 -05:00
LOGD ( "Removing entity from [%d, %d] that's travelling between worlds." , m_PosX , m_PosZ );
2014-06-21 22:07:38 +01:00
MarkDirty ();
2014-10-20 21:55:07 +01:00
( * itr ) -> SetWorldTravellingFrom ( nullptr );
2014-06-21 22:07:38 +01:00
itr = m_Entities . erase ( itr );
}
2014-08-14 01:03:30 +02:00
else if (
2013-04-13 21:02:10 +00:00
(( * itr ) -> GetChunkX () != m_PosX ) ||
(( * itr ) -> GetChunkZ () != m_PosZ )
)
{
2014-08-14 01:03:30 +02:00
// The entity moved out of the chunk, move it to the neighbor
2014-06-21 22:07:38 +01:00
MarkDirty ();
2013-04-13 21:02:10 +00:00
MoveEntityToNewChunk ( * itr );
itr = m_Entities . erase ( itr );
}
else
{
2014-06-12 15:21:07 +01:00
++ itr ;
2013-04-13 21:02:10 +00:00
}
2014-05-31 22:28:51 +01:00
} // for itr - m_Entitites[]
2013-04-13 21:02:10 +00:00
2013-04-01 20:56:25 +00:00
ApplyWeatherToTop ();
2012-06-14 13:06:06 +00:00
}
2013-11-30 15:58:27 +01:00
void cChunk :: TickBlock ( int a_RelX , int a_RelY , int a_RelZ )
{
2014-04-26 10:50:23 -07:00
cBlockHandler * Handler = BlockHandler ( GetBlock ( a_RelX , a_RelY , a_RelZ ));
2014-10-20 21:55:07 +01:00
ASSERT ( Handler != nullptr ); // Happenned on server restart, FS #243
2014-02-02 06:49:37 -08:00
cChunkInterface ChunkInterface ( this -> GetWorld () -> GetChunkMap ());
cBlockInServerPluginInterface PluginInterface ( * this -> GetWorld ());
2014-07-19 14:53:41 +02:00
Handler -> OnUpdate ( ChunkInterface , * this -> GetWorld (), PluginInterface , * this , a_RelX , a_RelY , a_RelZ );
2013-11-30 15:58:27 +01:00
}
2013-04-13 21:02:10 +00:00
void cChunk :: MoveEntityToNewChunk ( cEntity * a_Entity )
{
2013-05-19 11:49:01 +00:00
cChunk * Neighbor = GetNeighborChunk ( a_Entity -> GetChunkX () * cChunkDef :: Width , a_Entity -> GetChunkZ () * cChunkDef :: Width );
2014-10-20 21:55:07 +01:00
if ( Neighbor == nullptr )
2013-04-13 21:02:10 +00:00
{
2014-08-28 11:36:35 +02:00
Neighbor = m_ChunkMap -> GetChunkNoLoad ( a_Entity -> GetChunkX (), a_Entity -> GetChunkZ ());
2014-10-20 21:55:07 +01:00
if ( Neighbor == nullptr )
2013-05-06 18:32:22 +00:00
{
// TODO: What to do with this?
LOGWARNING ( "%s: Failed to move entity, destination chunk unreachable. Entity lost" , __FUNCTION__ );
return ;
}
2013-04-13 21:02:10 +00:00
}
2013-05-19 11:49:01 +00:00
ASSERT ( Neighbor != this ); // Moving into the same chunk? wtf?
2013-04-13 21:02:10 +00:00
Neighbor -> AddEntity ( a_Entity );
class cMover :
public cClientDiffCallback
{
virtual void Removed ( cClientHandle * a_Client ) override
{
a_Client -> SendDestroyEntity ( * m_Entity );
}
virtual void Added ( cClientHandle * a_Client ) override
{
m_Entity -> SpawnOn ( * a_Client );
}
cEntity * m_Entity ;
public :
2015-05-19 19:32:10 +01:00
cMover ( cEntity * a_CallbackEntity ) :
m_Entity ( a_CallbackEntity )
2013-04-13 21:02:10 +00:00
{}
} Mover ( a_Entity );
m_ChunkMap -> CompareChunkClients ( this , Neighbor , Mover );
}
2013-08-18 22:44:22 +02:00
void cChunk :: ProcessQueuedSetBlocks ( void )
{
Int64 CurrTick = m_World -> GetWorldAge ();
for ( sSetBlockQueueVector :: iterator itr = m_SetBlockQueue . begin (); itr != m_SetBlockQueue . end ();)
{
2013-11-18 22:30:34 +00:00
if ( itr -> m_Tick <= CurrTick )
2013-08-18 22:44:22 +02:00
{
2014-07-17 19:13:23 +02:00
if ( itr -> m_PreviousType != E_BLOCK_AIR ) // PreviousType defaults to 0 if not specified
2013-12-06 22:29:15 +00:00
{
if ( GetBlock ( itr -> m_RelX , itr -> m_RelY , itr -> m_RelZ ) == itr -> m_PreviousType )
{
2015-05-09 09:25:09 +02:00
// Current world age is bigger than / equal to target world age - delay time reached AND
2013-12-06 22:29:15 +00:00
// Previous block type was the same as current block type (to prevent duplication)
2014-07-17 19:13:23 +02:00
SetBlock ( itr -> m_RelX , itr -> m_RelY , itr -> m_RelZ , itr -> m_BlockType , itr -> m_BlockMeta ); // SetMeta doesn't send to client
2013-12-06 22:29:15 +00:00
itr = m_SetBlockQueue . erase ( itr );
LOGD ( "Successfully set queued block - previous and current types matched" );
}
else
{
itr = m_SetBlockQueue . erase ( itr );
LOGD ( "Failure setting queued block - previous and current blocktypes didn't match" );
}
}
else
{
2015-05-09 09:25:09 +02:00
// Current world age is bigger than / equal to target world age - delay time reached
2013-12-06 22:29:15 +00:00
SetBlock ( itr -> m_RelX , itr -> m_RelY , itr -> m_RelZ , itr -> m_BlockType , itr -> m_BlockMeta );
itr = m_SetBlockQueue . erase ( itr );
LOGD ( "Successfully set queued block - previous type ignored" );
}
2013-08-18 22:44:22 +02:00
}
else
{
2013-11-18 22:30:34 +00:00
// Not yet
++ itr ;
continue ;
2013-08-18 22:44:22 +02:00
}
} // for itr - m_SetBlockQueue[]
}
2012-08-25 17:52:08 +00:00
void cChunk :: BroadcastPendingBlockChanges ( void )
{
2013-08-19 21:55:03 +02:00
if ( m_PendingSendBlocks . empty ())
{
return ;
}
2015-03-09 22:32:12 +01:00
if ( m_PendingSendBlocks . size () >= 10240 )
{
// Resend the full chunk
2015-09-16 17:04:05 +01:00
for ( auto ClientHandle : m_LoadedByClient )
2015-03-09 22:32:12 +01:00
{
2015-09-16 17:04:05 +01:00
m_World -> ForceSendChunkTo ( m_PosX , m_PosZ , cChunkSender :: E_CHUNK_PRIORITY_MEDIUM , ClientHandle );
2015-03-09 22:32:12 +01:00
}
}
else
2012-08-25 17:52:08 +00:00
{
2015-03-09 22:32:12 +01:00
// Only send block changes
2015-09-16 17:04:05 +01:00
for ( auto ClientHandle : m_LoadedByClient )
2015-03-09 22:32:12 +01:00
{
2015-09-16 17:04:05 +01:00
ClientHandle -> SendBlockChanges ( m_PosX , m_PosZ , m_PendingSendBlocks );
2015-03-09 22:32:12 +01:00
}
2012-08-25 17:52:08 +00:00
}
2013-08-19 09:13:19 +02:00
m_PendingSendBlocks . clear ();
2012-08-25 17:52:08 +00:00
}
2014-01-31 15:17:41 -08:00
void cChunk :: CheckBlocks ()
2012-06-14 13:06:06 +00:00
{
2014-04-24 21:49:56 +01:00
if ( m_ToTickBlocks . empty ())
2012-06-14 13:06:06 +00:00
{
return ;
}
2014-04-26 10:50:23 -07:00
std :: vector < Vector3i > ToTickBlocks ;
2013-03-15 20:18:11 +00:00
std :: swap ( m_ToTickBlocks , ToTickBlocks );
2012-06-14 13:06:06 +00:00
2014-01-31 15:17:41 -08:00
cChunkInterface ChunkInterface ( m_World -> GetChunkMap ());
2014-02-02 06:49:37 -08:00
cBlockInServerPluginInterface PluginInterface ( * m_World );
2014-01-31 15:17:41 -08:00
2014-04-26 10:50:23 -07:00
for ( std :: vector < Vector3i >:: const_iterator itr = ToTickBlocks . begin (), end = ToTickBlocks . end (); itr != end ; ++ itr )
2012-06-14 13:06:06 +00:00
{
2014-04-26 10:50:23 -07:00
Vector3i Pos = ( * itr );
2012-06-14 13:06:06 +00:00
2014-04-26 10:50:23 -07:00
cBlockHandler * Handler = BlockHandler ( GetBlock ( Pos ));
Handler -> Check ( ChunkInterface , PluginInterface , Pos . x , Pos . y , Pos . z , * this );
2012-06-14 13:06:06 +00:00
} // for itr - ToTickBlocks[]
}
2013-04-13 21:02:10 +00:00
void cChunk :: TickBlocks ( void )
2012-06-14 13:06:06 +00:00
{
// Tick dem blocks
2014-07-21 15:19:48 +02: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
2013-04-13 21:02:10 +00:00
int RandomX = m_World -> GetTickRandomNumber ( 0x00ffffff );
int RandomY = m_World -> GetTickRandomNumber ( 0x00ffffff );
int RandomZ = m_World -> GetTickRandomNumber ( 0x00ffffff );
2012-06-14 13:06:06 +00:00
int TickX = m_BlockTickX ;
int TickY = m_BlockTickY ;
int TickZ = m_BlockTickZ ;
2014-02-02 06:49:37 -08:00
cChunkInterface ChunkInterface ( this -> GetWorld () -> GetChunkMap ());
cBlockInServerPluginInterface PluginInterface ( * this -> GetWorld ());
2012-06-14 13:06:06 +00: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 ++ ,
// This weird construct (*2, then /2) is needed,
// otherwise the blocktick distribution is too biased towards even coords!
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
)
{
if ( m_BlockTickY > cChunkDef :: GetHeight ( m_HeightMap , m_BlockTickX , m_BlockTickZ ))
{
2014-07-17 19:13:23 +02:00
continue ; // It's all air up here
2012-06-14 13:06:06 +00:00
}
2014-04-26 10:50:23 -07:00
cBlockHandler * Handler = BlockHandler ( GetBlock ( m_BlockTickX , m_BlockTickY , m_BlockTickZ ));
2014-10-20 21:55:07 +01:00
ASSERT ( Handler != nullptr ); // Happenned on server restart, FS #243
2014-02-02 06:49:37 -08:00
Handler -> OnUpdate ( ChunkInterface , * this -> GetWorld (), PluginInterface , * this , m_BlockTickX , m_BlockTickY , m_BlockTickZ );
2012-10-07 10:08:57 +00:00
} // for i - tickblocks
2012-06-14 13:06:06 +00:00
}
2012-09-04 15:07:08 +00:00
2013-04-01 20:56:25 +00:00
void cChunk :: ApplyWeatherToTop ()
2012-10-21 21:15:57 +00:00
{
if (
2013-04-01 20:56:25 +00:00
( m_World -> GetTickRandomNumber ( 100 ) != 0 ) ||
2012-10-21 21:15:57 +00:00
(
( m_World -> GetWeather () != eWeather_Rain ) &&
( m_World -> GetWeather () != eWeather_ThunderStorm )
)
)
{
// Not the right weather, or not at this tick; bail out
return ;
}
2013-04-01 20:56:25 +00:00
int X = m_World -> GetTickRandomNumber ( 15 );
int Z = m_World -> GetTickRandomNumber ( 15 );
2015-02-25 19:02:08 +01:00
// TODO: Check light levels, don't snow over when the BlockLight is higher than (7?)
int Height = GetHeight ( X , Z );
if ( GetSnowStartHeight ( GetBiomeAt ( X , Z )) > Height )
{
return ;
}
BLOCKTYPE TopBlock = GetBlock ( X , Height , Z );
NIBBLETYPE TopMeta = GetMeta ( X , Height , Z );
if ( m_World -> IsDeepSnowEnabled () && ( TopBlock == E_BLOCK_SNOW ))
{
int MaxSize = 7 ;
BLOCKTYPE BlockType [ 4 ];
NIBBLETYPE BlockMeta [ 4 ];
UnboundedRelGetBlock ( X - 1 , Height , Z , BlockType [ 0 ], BlockMeta [ 0 ]);
UnboundedRelGetBlock ( X + 1 , Height , Z , BlockType [ 1 ], BlockMeta [ 1 ]);
UnboundedRelGetBlock ( X , Height , Z - 1 , BlockType [ 2 ], BlockMeta [ 2 ]);
UnboundedRelGetBlock ( X , Height , Z + 1 , BlockType [ 3 ], BlockMeta [ 3 ]);
for ( int i = 0 ; i < 4 ; i ++ )
2012-10-21 21:15:57 +00:00
{
2015-02-25 19:02:08 +01:00
switch ( BlockType [ i ])
2012-10-21 21:15:57 +00:00
{
2015-02-25 19:02:08 +01:00
case E_BLOCK_AIR :
2012-10-21 21:15:57 +00:00
{
2015-02-25 19:02:08 +01:00
MaxSize = 0 ;
break ;
2012-10-21 21:15:57 +00:00
}
2015-02-25 19:02:08 +01:00
case E_BLOCK_SNOW :
2012-10-21 21:15:57 +00:00
{
2015-02-25 19:02:08 +01:00
MaxSize = std :: min ( BlockMeta [ i ] + 1 , MaxSize );
break ;
2012-10-21 21:15:57 +00:00
}
}
2015-02-25 19:02:08 +01:00
}
if ( TopMeta < MaxSize )
{
FastSetBlock ( X , Height , Z , E_BLOCK_SNOW , TopMeta + 1 );
}
else if ( TopMeta > MaxSize )
2013-12-09 14:05:32 +00:00
{
2015-02-25 19:02:08 +01:00
FastSetBlock ( X , Height , Z , E_BLOCK_SNOW , TopMeta - 1 );
2013-12-09 14:05:32 +00:00
}
2015-02-25 19:02:08 +01:00
}
2015-02-14 17:11:38 -05:00
else if ( cBlockInfo :: IsSnowable ( TopBlock ) && ( Height < cChunkDef :: Height - 1 ))
2015-02-25 19:02:08 +01:00
{
SetBlock ( X , Height + 1 , Z , E_BLOCK_SNOW , 0 );
}
else if ( IsBlockWater ( TopBlock ) && ( TopMeta == 0 ))
{
SetBlock ( X , Height , Z , E_BLOCK_ICE , 0 );
}
else if (
( m_World -> IsDeepSnowEnabled ()) &&
(
( TopBlock == E_BLOCK_RED_ROSE ) ||
( TopBlock == E_BLOCK_YELLOW_FLOWER ) ||
( TopBlock == E_BLOCK_RED_MUSHROOM ) ||
( TopBlock == E_BLOCK_BROWN_MUSHROOM )
)
)
{
SetBlock ( X , Height , Z , E_BLOCK_SNOW , 0 );
}
2012-10-21 21:15:57 +00:00
}
2012-06-14 13:06:06 +00:00
void cChunk :: GrowMelonPumpkin ( int a_RelX , int a_RelY , int a_RelZ , BLOCKTYPE a_BlockType , MTRand & a_TickRandom )
{
// 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 (
2014-07-17 16:33:09 +02:00
! IsValid ||
( BlockType [ 0 ] == ProduceType ) ||
( BlockType [ 1 ] == ProduceType ) ||
( BlockType [ 2 ] == ProduceType ) ||
2012-06-14 13:06:06 +00:00
( 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 ( 3 ); // 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 ;
2015-06-23 17:23:35 +02:00
VERIFY ( UnboundedRelGetBlock ( a_RelX + x , a_RelY - 1 , a_RelZ + z , Soil , BlockMeta ));
2012-06-14 13:06:06 +00:00
switch ( Soil )
{
case E_BLOCK_DIRT :
case E_BLOCK_GRASS :
case E_BLOCK_FARMLAND :
{
2015-06-23 17:23:35 +02:00
// Place a randomly-facing produce:
NIBBLETYPE Meta = ( ProduceType == E_BLOCK_MELON ) ? 0 : static_cast < NIBBLETYPE > ( a_TickRandom . randInt ( 4 ) % 4 );
LOGD ( "Growing melon / pumpkin at {%d, %d, %d} (<%d, %d> from stem), overwriting %s, growing on top of %s, meta %d" ,
a_RelX + x + m_PosX * cChunkDef :: Width , a_RelY , a_RelZ + z + m_PosZ * cChunkDef :: Width ,
x , z ,
2013-04-03 16:43:37 +00:00
ItemTypeToString ( BlockType [ CheckType ]). c_str (),
2015-06-23 17:23:35 +02:00
ItemTypeToString ( Soil ). c_str (),
Meta
2013-04-03 16:43:37 +00:00
);
2015-06-23 17:23:35 +02:00
VERIFY ( UnboundedRelFastSetBlock ( a_RelX + x , a_RelY , a_RelZ + z , ProduceType , Meta ));
2015-06-23 21:16:38 +01:00
auto Absolute = RelativeToAbsolute ( Vector3i { a_RelX + x , a_RelY , a_RelZ + z }, m_PosX , m_PosZ );
cChunkInterface ChunkInterface ( this -> GetWorld () -> GetChunkMap ());
2015-07-01 10:40:16 +02:00
cBlockHandler :: NeighborChanged ( ChunkInterface , Absolute . x , Absolute . y - 1 , Absolute . z , BLOCK_FACE_YP );
2012-06-14 13:06:06 +00:00
break ;
}
}
}
void cChunk :: GrowSugarcane ( int a_RelX , int a_RelY , int a_RelZ , int a_NumBlocks )
{
// Check the total height of the sugarcane blocks here:
int Top = a_RelY + 1 ;
while (
( Top < cChunkDef :: Height ) &&
( GetBlock ( a_RelX , Top , a_RelZ ) == E_BLOCK_SUGARCANE )
)
{
++ Top ;
}
int Bottom = a_RelY - 1 ;
while (
( Bottom > 0 ) &&
( GetBlock ( a_RelX , Bottom , a_RelZ ) == E_BLOCK_SUGARCANE )
)
{
-- Bottom ;
}
// Grow by at most a_NumBlocks, but no more than max height:
int ToGrow = std :: min ( a_NumBlocks , m_World -> GetMaxSugarcaneHeight () + 1 - ( Top - Bottom ));
for ( int i = 0 ; i < ToGrow ; i ++ )
{
BLOCKTYPE BlockType ;
NIBBLETYPE BlockMeta ;
if ( UnboundedRelGetBlock ( a_RelX , Top + i , a_RelZ , BlockType , BlockMeta ) && ( BlockType == E_BLOCK_AIR ))
{
UnboundedRelFastSetBlock ( a_RelX , Top + i , a_RelZ , E_BLOCK_SUGARCANE , 0 );
}
else
{
break ;
}
} // for i
}
void cChunk :: GrowCactus ( int a_RelX , int a_RelY , int a_RelZ , int a_NumBlocks )
{
// Check the total height of the sugarcane blocks here:
int Top = a_RelY + 1 ;
while (
( Top < cChunkDef :: Height ) &&
( GetBlock ( a_RelX , Top , a_RelZ ) == E_BLOCK_CACTUS )
)
{
++ Top ;
}
int Bottom = a_RelY - 1 ;
while (
( Bottom > 0 ) &&
( GetBlock ( a_RelX , Bottom , a_RelZ ) == E_BLOCK_CACTUS )
)
{
-- Bottom ;
}
// Grow by at most a_NumBlocks, but no more than max height:
int ToGrow = std :: min ( a_NumBlocks , m_World -> GetMaxCactusHeight () + 1 - ( Top - Bottom ));
for ( int i = 0 ; i < ToGrow ; i ++ )
{
BLOCKTYPE BlockType ;
NIBBLETYPE BlockMeta ;
if ( UnboundedRelGetBlock ( a_RelX , Top + i , a_RelZ , BlockType , BlockMeta ) && ( BlockType == E_BLOCK_AIR ))
{
// TODO: Check the surrounding blocks, if they aren't air, break the cactus block into pickups (and continue breaking blocks above in the next loop iterations)
UnboundedRelFastSetBlock ( a_RelX , Top + i , a_RelZ , E_BLOCK_CACTUS , 0 );
}
else
{
break ;
}
} // for i
}
2013-03-15 20:18:11 +00:00
bool cChunk :: UnboundedRelGetBlock ( int a_RelX , int a_RelY , int a_RelZ , BLOCKTYPE & a_BlockType , NIBBLETYPE & a_BlockMeta ) const
2012-06-14 13:06:06 +00:00
{
2013-03-15 20:18:11 +00:00
if (( a_RelY < 0 ) || ( a_RelY >= cChunkDef :: Height ))
2012-06-14 13:06:06 +00:00
{
2013-05-28 12:05:23 +00:00
LOGWARNING ( "%s: requesting a block with a_RelY out of range: %d" , __FUNCTION__ , a_RelY );
2012-06-14 13:06:06 +00:00
return false ;
}
2013-10-22 17:54:09 +02:00
cChunk * Chunk = GetRelNeighborChunkAdjustCoords ( a_RelX , a_RelZ );
2014-10-20 21:55:07 +01:00
if (( Chunk == nullptr ) || ! Chunk -> IsValid ())
2012-12-14 22:38:30 +00:00
{
2013-10-22 17:54:09 +02:00
// The chunk is not available, bail out
return false ;
2012-12-14 22:38:30 +00:00
}
2013-10-22 17:54:09 +02:00
Chunk -> GetBlockTypeMeta ( a_RelX , a_RelY , a_RelZ , a_BlockType , a_BlockMeta );
return true ;
2012-06-14 13:06:06 +00:00
}
2013-05-28 12:05:23 +00:00
bool cChunk :: UnboundedRelGetBlockType ( int a_RelX , int a_RelY , int a_RelZ , BLOCKTYPE & a_BlockType ) const
{
if (( a_RelY < 0 ) || ( a_RelY >= cChunkDef :: Height ))
{
LOGWARNING ( "%s: requesting a block with a_RelY out of range: %d" , __FUNCTION__ , a_RelY );
return false ;
}
2013-10-22 17:54:09 +02:00
cChunk * Chunk = GetRelNeighborChunkAdjustCoords ( a_RelX , a_RelZ );
2014-10-20 21:55:07 +01:00
if (( Chunk == nullptr ) || ! Chunk -> IsValid ())
2013-05-28 12:05:23 +00:00
{
2013-10-22 17:54:09 +02:00
// The chunk is not available, bail out
return false ;
2013-05-28 12:05:23 +00:00
}
2013-10-22 17:54:09 +02:00
a_BlockType = Chunk -> GetBlock ( a_RelX , a_RelY , a_RelZ );
return true ;
2013-05-28 12:05:23 +00:00
}
bool cChunk :: UnboundedRelGetBlockMeta ( int a_RelX , int a_RelY , int a_RelZ , NIBBLETYPE & a_BlockMeta ) const
{
if (( a_RelY < 0 ) || ( a_RelY >= cChunkDef :: Height ))
{
LOGWARNING ( "%s: requesting a block with a_RelY out of range: %d" , __FUNCTION__ , a_RelY );
return false ;
}
2013-10-22 17:54:09 +02:00
cChunk * Chunk = GetRelNeighborChunkAdjustCoords ( a_RelX , a_RelZ );
2014-10-20 21:55:07 +01:00
if (( Chunk == nullptr ) || ! Chunk -> IsValid ())
2013-05-28 12:05:23 +00:00
{
2013-10-22 17:54:09 +02:00
// The chunk is not available, bail out
return false ;
2013-05-28 12:05:23 +00:00
}
2013-10-22 17:54:09 +02:00
a_BlockMeta = Chunk -> GetMeta ( a_RelX , a_RelY , a_RelZ );
return true ;
}
2013-05-28 12:05:23 +00:00
2013-10-22 17:54:09 +02:00
bool cChunk :: UnboundedRelGetBlockBlockLight ( int a_RelX , int a_RelY , int a_RelZ , NIBBLETYPE & a_BlockBlockLight ) const
{
if (( a_RelY < 0 ) || ( a_RelY >= cChunkDef :: Height ))
2013-05-28 12:05:23 +00:00
{
2013-10-22 17:54:09 +02:00
LOGWARNING ( "%s: requesting a block with a_RelY out of range: %d" , __FUNCTION__ , a_RelY );
return false ;
2013-05-28 12:05:23 +00:00
}
2013-10-22 17:54:09 +02:00
cChunk * Chunk = GetRelNeighborChunkAdjustCoords ( a_RelX , a_RelZ );
2014-10-20 21:55:07 +01:00
if (( Chunk == nullptr ) || ! Chunk -> IsValid ())
2013-05-28 12:05:23 +00:00
{
2013-10-22 17:54:09 +02:00
// The chunk is not available, bail out
return false ;
2013-05-28 12:05:23 +00:00
}
2013-10-22 17:54:09 +02:00
a_BlockBlockLight = Chunk -> GetBlockLight ( a_RelX , a_RelY , a_RelZ );
return true ;
}
bool cChunk :: UnboundedRelGetBlockSkyLight ( int a_RelX , int a_RelY , int a_RelZ , NIBBLETYPE & a_BlockSkyLight ) const
{
if (( a_RelY < 0 ) || ( a_RelY >= cChunkDef :: Height ))
2013-05-28 12:05:23 +00:00
{
2013-10-22 17:54:09 +02:00
LOGWARNING ( "%s: requesting a block with a_RelY out of range: %d" , __FUNCTION__ , a_RelY );
return false ;
2013-05-28 12:05:23 +00:00
}
2013-10-22 17:54:09 +02:00
cChunk * Chunk = GetRelNeighborChunkAdjustCoords ( a_RelX , a_RelZ );
2014-10-20 21:55:07 +01:00
if (( Chunk == nullptr ) || ! Chunk -> IsValid ())
2013-05-28 12:05:23 +00:00
{
2013-10-22 17:54:09 +02:00
// The chunk is not available, bail out
return false ;
2013-05-28 12:05:23 +00:00
}
2013-10-22 17:54:09 +02:00
a_BlockSkyLight = Chunk -> GetSkyLight ( a_RelX , a_RelY , a_RelZ );
return true ;
2013-05-28 12:05:23 +00:00
}
2013-10-27 09:19:13 +01:00
bool cChunk :: UnboundedRelGetBlockLights ( int a_RelX , int a_RelY , int a_RelZ , NIBBLETYPE & a_BlockLight , NIBBLETYPE & a_SkyLight ) const
{
if (( a_RelY < 0 ) || ( a_RelY >= cChunkDef :: Height ))
{
LOGWARNING ( "%s: requesting a block with a_RelY out of range: %d" , __FUNCTION__ , a_RelY );
return false ;
}
cChunk * Chunk = GetRelNeighborChunkAdjustCoords ( a_RelX , a_RelZ );
2014-10-20 21:55:07 +01:00
if (( Chunk == nullptr ) || ! Chunk -> IsValid ())
2013-10-27 09:19:13 +01:00
{
// The chunk is not available, bail out
return false ;
}
2014-04-26 10:50:23 -07:00
a_BlockLight = Chunk -> GetBlockLight ( a_RelX , a_RelY , a_RelZ );
a_SkyLight = Chunk -> GetSkyLight ( a_RelX , a_RelY , a_RelZ );
2013-10-27 09:19:13 +01:00
return true ;
}
2012-06-14 13:06:06 +00:00
bool cChunk :: UnboundedRelSetBlock ( int a_RelX , int a_RelY , int a_RelZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta )
{
2015-02-14 17:11:38 -05:00
if (( a_RelY < 0 ) || ( a_RelY >= cChunkDef :: Height ))
2012-12-14 22:38:30 +00:00
{
LOGWARNING ( "UnboundedRelSetBlock(): requesting a block with a_RelY out of range: %d" , a_RelY );
return false ;
}
2013-10-22 17:54:09 +02:00
cChunk * Chunk = GetRelNeighborChunkAdjustCoords ( a_RelX , a_RelZ );
2014-10-20 21:55:07 +01:00
if (( Chunk == nullptr ) || ! Chunk -> IsValid ())
2012-12-14 22:38:30 +00:00
{
2013-10-22 17:54:09 +02:00
// The chunk is not available, bail out
return false ;
2012-12-14 22:38:30 +00:00
}
2013-10-22 17:54:09 +02:00
Chunk -> SetBlock ( a_RelX , a_RelY , a_RelZ , a_BlockType , a_BlockMeta );
return true ;
2014-07-17 16:33:09 +02:00
}
2012-06-14 13:06:06 +00:00
bool cChunk :: UnboundedRelFastSetBlock ( int a_RelX , int a_RelY , int a_RelZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta )
{
2015-02-14 17:11:38 -05:00
if (( a_RelY < 0 ) || ( a_RelY >= cChunkDef :: Height ))
2012-12-14 22:38:30 +00:00
{
LOGWARNING ( "UnboundedRelFastSetBlock(): requesting a block with a_RelY out of range: %d" , a_RelY );
return false ;
}
2013-10-22 17:54:09 +02:00
cChunk * Chunk = GetRelNeighborChunkAdjustCoords ( a_RelX , a_RelZ );
2014-10-20 21:55:07 +01:00
if (( Chunk == nullptr ) || ! Chunk -> IsValid ())
2012-12-14 22:38:30 +00:00
{
2013-10-22 17:54:09 +02:00
// The chunk is not available, bail out
return false ;
2012-12-14 22:38:30 +00:00
}
2013-10-22 17:54:09 +02:00
Chunk -> FastSetBlock ( a_RelX , a_RelY , a_RelZ , a_BlockType , a_BlockMeta );
return true ;
2012-06-14 13:06:06 +00:00
}
2013-03-22 16:48:45 +00:00
void cChunk :: UnboundedQueueTickBlock ( int a_RelX , int a_RelY , int a_RelZ )
{
2013-04-27 13:23:20 +00:00
if (( a_RelY < 0 ) || ( a_RelY >= cChunkDef :: Height ))
{
// Outside of chunkmap
return ;
}
2013-10-22 17:54:09 +02:00
cChunk * Chunk = GetRelNeighborChunkAdjustCoords ( a_RelX , a_RelZ );
2014-10-20 21:55:07 +01:00
if (( Chunk != nullptr ) && Chunk -> IsValid ())
2013-03-22 16:48:45 +00:00
{
2013-10-22 17:54:09 +02:00
Chunk -> QueueTickBlock ( a_RelX , a_RelY , a_RelZ );
2013-03-22 16:48:45 +00:00
}
}
2013-10-22 17:54:09 +02:00
int cChunk :: GetHeight ( int a_X , int a_Z )
2012-06-14 13:06:06 +00:00
{
ASSERT (( a_X >= 0 ) && ( a_X < Width ) && ( a_Z >= 0 ) && ( a_Z < Width ));
if (( a_X >= 0 ) && ( a_X < Width ) && ( a_Z >= 0 ) && ( a_Z < Width ))
{
return m_HeightMap [ a_X + a_Z * Width ];
}
return 0 ;
}
void cChunk :: CreateBlockEntities ( void )
{
for ( int x = 0 ; x < Width ; x ++ )
{
for ( int z = 0 ; z < Width ; z ++ )
{
for ( int y = 0 ; y < Height ; y ++ )
{
2014-03-23 14:34:19 +00:00
BLOCKTYPE BlockType = GetBlock ( x , y , z );
2013-04-06 21:21:57 +00:00
switch ( BlockType )
2012-06-14 13:06:06 +00:00
{
2014-04-12 00:01:15 +02:00
case E_BLOCK_BEACON :
2014-07-06 23:50:22 +01:00
case E_BLOCK_TRAPPED_CHEST :
2012-06-14 13:06:06 +00:00
case E_BLOCK_CHEST :
2014-01-18 15:16:47 +02:00
case E_BLOCK_COMMAND_BLOCK :
2012-12-19 21:19:36 +00:00
case E_BLOCK_DISPENSER :
2013-05-26 14:39:04 +00:00
case E_BLOCK_DROPPER :
2013-12-07 00:18:58 +00:00
case E_BLOCK_ENDER_CHEST :
2013-06-20 11:41:44 +00:00
case E_BLOCK_LIT_FURNACE :
2012-06-14 13:06:06 +00:00
case E_BLOCK_FURNACE :
2013-06-13 07:36:43 +00:00
case E_BLOCK_HOPPER :
2012-06-14 13:06:06 +00:00
case E_BLOCK_SIGN_POST :
case E_BLOCK_WALLSIGN :
2014-02-17 20:14:08 +01:00
case E_BLOCK_HEAD :
2012-08-26 17:50:42 +00:00
case E_BLOCK_NOTE_BLOCK :
2012-10-21 07:46:28 +00:00
case E_BLOCK_JUKEBOX :
2014-03-07 01:30:34 +01:00
case E_BLOCK_FLOWER_POT :
2014-09-19 23:00:54 +02:00
case E_BLOCK_MOB_SPAWNER :
2015-09-24 10:48:33 +02:00
case E_BLOCK_BREWING_STAND :
2012-10-21 07:46:28 +00:00
{
2014-09-06 19:01:30 +02:00
if ( ! HasBlockEntityAt ( x + m_PosX * Width , y , z + m_PosZ * Width ))
2012-10-21 07:46:28 +00:00
{
2013-11-14 15:37:09 +01:00
m_BlockEntities . push_back ( cBlockEntity :: CreateByBlockType (
BlockType , GetMeta ( x , y , z ),
2014-09-06 19:01:30 +02:00
x + m_PosX * Width , y , z + m_PosZ * Width , m_World
2013-11-14 15:37:09 +01:00
));
2012-10-21 07:46:28 +00:00
}
break ;
}
2012-06-14 13:06:06 +00:00
} // switch (BlockType)
} // for y
} // for z
} // for x
}
2013-03-03 15:33:55 +00:00
void cChunk :: WakeUpSimulators ( void )
{
2014-10-25 21:54:00 +01:00
cSimulator * WaterSimulator = m_World -> GetWaterSimulator ();
cSimulator * LavaSimulator = m_World -> GetLavaSimulator ();
cSimulator * RedstoneSimulator = m_World -> GetRedstoneSimulator ();
2013-03-03 15:33:55 +00:00
int BaseX = m_PosX * cChunkDef :: Width ;
int BaseZ = m_PosZ * cChunkDef :: Width ;
for ( int x = 0 ; x < Width ; x ++ )
{
int BlockX = x + BaseX ;
for ( int z = 0 ; z < Width ; z ++ )
{
int BlockZ = z + BaseZ ;
for ( int y = GetHeight ( x , z ); y >= 0 ; y -- )
{
2014-03-23 14:34:19 +00:00
BLOCKTYPE Block = GetBlock ( x , y , z );
2013-12-13 19:01:15 +00:00
// The redstone sim takes multiple blocks, use the inbuilt checker
if ( RedstoneSimulator -> IsAllowedBlock ( Block ))
{
RedstoneSimulator -> AddBlock ( BlockX , y , BlockZ , this );
continue ;
}
switch ( Block )
2013-03-03 15:33:55 +00:00
{
case E_BLOCK_WATER :
{
WaterSimulator -> AddBlock ( BlockX , y , BlockZ , this );
break ;
}
case E_BLOCK_LAVA :
{
LavaSimulator -> AddBlock ( BlockX , y , BlockZ , this );
break ;
}
2013-12-12 23:49:10 +00:00
default :
{
break ;
}
2013-03-03 15:33:55 +00:00
} // switch (BlockType)
} // for y
} // for z
} // for x
}
2014-04-07 20:57:14 +01:00
void cChunk :: CalculateHeightmap ( const BLOCKTYPE * a_BlockTypes )
2012-06-14 13:06:06 +00:00
{
for ( int x = 0 ; x < Width ; x ++ )
{
for ( int z = 0 ; z < Width ; z ++ )
{
for ( int y = Height - 1 ; y > - 1 ; y -- )
{
2014-07-21 15:19:48 +02:00
int index = MakeIndex ( x , y , z );
2014-04-07 20:57:14 +01:00
if ( a_BlockTypes [ index ] != E_BLOCK_AIR )
2012-06-14 13:06:06 +00:00
{
2015-07-29 09:04:03 -06:00
m_HeightMap [ x + z * Width ] = static_cast < HEIGHTTYPE > ( y );
2012-06-14 13:06:06 +00:00
break ;
}
} // for y
} // for z
} // for x
}
2014-05-25 13:46:34 +01:00
void cChunk :: SetBlock ( int a_RelX , int a_RelY , int a_RelZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , bool a_SendToClients )
2012-06-14 13:06:06 +00:00
{
2014-05-25 13:46:34 +01:00
FastSetBlock ( a_RelX , a_RelY , a_RelZ , a_BlockType , a_BlockMeta , a_SendToClients );
2012-06-14 13:06:06 +00:00
2013-08-19 09:19:45 +02:00
// Tick this block and its neighbors:
2014-04-26 10:50:23 -07:00
m_ToTickBlocks . push_back ( Vector3i ( a_RelX , a_RelY , a_RelZ ));
2013-02-28 07:42:45 +00:00
QueueTickBlockNeighbors ( a_RelX , a_RelY , a_RelZ );
2012-06-14 13:06:06 +00:00
2013-08-19 09:19:45 +02:00
// If there was a block entity, remove it:
2013-02-28 07:42:45 +00:00
Vector3i WorldPos = PositionToWorldPosition ( a_RelX , a_RelY , a_RelZ );
cBlockEntity * BlockEntity = GetBlockEntity ( WorldPos );
2014-10-20 21:55:07 +01:00
if ( BlockEntity != nullptr )
2012-06-14 13:06:06 +00:00
{
BlockEntity -> Destroy ();
2013-02-28 07:42:45 +00:00
RemoveBlockEntity ( BlockEntity );
2012-06-14 13:06:06 +00:00
delete BlockEntity ;
2014-10-20 21:55:07 +01:00
BlockEntity = nullptr ;
2012-06-14 13:06:06 +00:00
}
2013-02-28 07:42:45 +00:00
2013-08-19 09:19:45 +02:00
// If the new block is a block entity, create the entity object:
2013-02-28 07:42:45 +00:00
switch ( a_BlockType )
2012-06-14 13:06:06 +00:00
{
2014-04-12 00:01:15 +02:00
case E_BLOCK_BEACON :
2014-07-06 23:50:22 +01:00
case E_BLOCK_TRAPPED_CHEST :
2012-06-14 13:06:06 +00:00
case E_BLOCK_CHEST :
2014-01-18 15:16:47 +02:00
case E_BLOCK_COMMAND_BLOCK :
2012-12-19 21:19:36 +00:00
case E_BLOCK_DISPENSER :
2013-05-26 14:39:04 +00:00
case E_BLOCK_DROPPER :
2013-12-07 00:18:58 +00:00
case E_BLOCK_ENDER_CHEST :
2013-06-20 11:41:44 +00:00
case E_BLOCK_LIT_FURNACE :
2012-06-14 13:06:06 +00:00
case E_BLOCK_FURNACE :
2013-06-13 07:36:43 +00:00
case E_BLOCK_HOPPER :
2012-06-14 13:06:06 +00:00
case E_BLOCK_SIGN_POST :
case E_BLOCK_WALLSIGN :
2014-02-17 20:14:08 +01:00
case E_BLOCK_HEAD :
2012-08-26 17:50:42 +00:00
case E_BLOCK_NOTE_BLOCK :
2012-10-21 07:46:28 +00:00
case E_BLOCK_JUKEBOX :
2014-03-07 01:30:34 +01:00
case E_BLOCK_FLOWER_POT :
2014-09-19 23:00:54 +02:00
case E_BLOCK_MOB_SPAWNER :
2015-09-24 10:48:33 +02:00
case E_BLOCK_BREWING_STAND :
2012-10-21 07:46:28 +00:00
{
2013-11-14 15:37:09 +01:00
AddBlockEntity ( cBlockEntity :: CreateByBlockType ( a_BlockType , a_BlockMeta , WorldPos . x , WorldPos . y , WorldPos . z , m_World ));
2012-10-21 07:46:28 +00:00
break ;
}
2012-06-14 13:06:06 +00:00
} // switch (a_BlockType)
}
2013-12-06 22:29:15 +00:00
void cChunk :: QueueSetBlock ( int a_RelX , int a_RelY , int a_RelZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Int64 a_Tick , BLOCKTYPE a_PreviousBlockType )
2013-08-18 22:44:22 +02:00
{
2013-12-06 22:29:15 +00:00
m_SetBlockQueue . push_back ( sSetBlockQueueItem ( a_RelX , a_RelY , a_RelZ , a_BlockType , a_BlockMeta , a_Tick , a_PreviousBlockType ));
2013-08-18 22:44:22 +02:00
}
2013-02-28 07:42:45 +00:00
void cChunk :: QueueTickBlock ( int a_RelX , int a_RelY , int a_RelZ )
2012-10-14 18:30:16 +00:00
{
2013-04-27 13:23:20 +00:00
ASSERT (
( a_RelX >= 0 ) && ( a_RelX < Width ) &&
( a_RelY >= 0 ) && ( a_RelY < Height ) &&
( a_RelZ >= 0 ) && ( a_RelZ < Width )
); // Coords need to be valid
2013-02-28 07:42:45 +00:00
if ( ! IsValid ())
2012-10-14 18:30:16 +00:00
{
2013-02-28 07:42:45 +00:00
return ;
2012-10-14 18:30:16 +00:00
}
2014-04-27 06:45:33 -07:00
m_ToTickBlocks . push_back ( Vector3i ( a_RelX , a_RelY , a_RelZ ));
2012-10-14 18:30:16 +00:00
}
2013-02-28 07:42:45 +00:00
void cChunk :: QueueTickBlockNeighbors ( int a_RelX , int a_RelY , int a_RelZ )
2012-10-14 18:30:16 +00:00
{
2013-02-28 07:42:45 +00:00
struct
2012-10-14 18:30:16 +00:00
{
2013-02-28 07:42:45 +00:00
int x , y , z ;
2012-10-14 18:30:16 +00:00
}
2013-02-28 07:42:45 +00:00
Coords [] =
{
{ 1 , 0 , 0 },
{ - 1 , 0 , 0 },
{ 0 , 1 , 0 },
{ 0 , - 1 , 0 },
{ 0 , 0 , 1 },
{ 0 , 0 , - 1 },
} ;
2013-12-20 16:15:39 +01:00
for ( size_t i = 0 ; i < ARRAYCOUNT ( Coords ); i ++ )
2013-02-28 07:42:45 +00:00
{
2013-03-22 16:48:45 +00:00
UnboundedQueueTickBlock ( a_RelX + Coords [ i ]. x , a_RelY + Coords [ i ]. y , a_RelZ + Coords [ i ]. z );
2013-02-28 07:42:45 +00:00
} // for i - Coords[]
2012-10-14 18:30:16 +00:00
}
2014-05-25 13:46:34 +01:00
void cChunk :: FastSetBlock ( int a_RelX , int a_RelY , int a_RelZ , BLOCKTYPE a_BlockType , BLOCKTYPE a_BlockMeta , bool a_SendToClients )
2012-06-14 13:06:06 +00:00
{
2013-03-14 21:56:01 +00:00
ASSERT ( ! (( a_RelX < 0 ) || ( a_RelX >= Width ) || ( a_RelY < 0 ) || ( a_RelY >= Height ) || ( a_RelZ < 0 ) || ( a_RelZ >= Width )));
2012-06-14 13:06:06 +00:00
ASSERT ( IsValid ());
2014-04-26 10:50:23 -07:00
const BLOCKTYPE OldBlockType = GetBlock ( a_RelX , a_RelY , a_RelZ );
2014-05-21 19:58:48 +01:00
const BLOCKTYPE OldBlockMeta = m_ChunkData . GetMeta ( a_RelX , a_RelY , a_RelZ );
2013-03-14 21:56:01 +00:00
if (( OldBlockType == a_BlockType ) && ( OldBlockMeta == a_BlockMeta ))
2012-06-14 13:06:06 +00:00
{
return ;
}
MarkDirty ();
2014-06-04 23:23:09 +01:00
m_IsRedstoneDirty = true ;
2014-03-23 14:34:19 +00:00
2014-05-21 19:58:48 +01:00
m_ChunkData . SetBlock ( a_RelX , a_RelY , a_RelZ , a_BlockType );
2012-06-14 13:06:06 +00:00
2014-07-20 23:10:31 +02:00
// Queue block to be sent only if ...
if (
2014-07-17 19:13:23 +02:00
a_SendToClients && // ... we are told to do so AND ...
2014-06-01 20:00:11 +01:00
(
2015-01-25 18:23:30 -05:00
! ( // ... the old and new blocktypes AREN'T leaves (because the client doesn't need meta updates)
(( OldBlockType == E_BLOCK_LEAVES ) && ( a_BlockType == E_BLOCK_LEAVES )) ||
(( OldBlockType == E_BLOCK_NEW_LEAVES ) && ( a_BlockType == E_BLOCK_NEW_LEAVES ))
2015-01-25 19:13:55 -05:00
) && // ... AND ...
(
( OldBlockMeta != a_BlockMeta ) || // ... the meta value is different OR ...
! ( // ... the old and new blocktypes AREN'T liquids (because client doesn't need to distinguish betwixt them):
(( OldBlockType == E_BLOCK_STATIONARY_WATER ) && ( a_BlockType == E_BLOCK_WATER )) || // Replacing stationary water with water
(( OldBlockType == E_BLOCK_WATER ) && ( a_BlockType == E_BLOCK_STATIONARY_WATER )) || // Replacing water with stationary water
(( OldBlockType == E_BLOCK_STATIONARY_LAVA ) && ( a_BlockType == E_BLOCK_LAVA )) || // Replacing stationary water with water
(( OldBlockType == E_BLOCK_LAVA ) && ( a_BlockType == E_BLOCK_STATIONARY_LAVA )) // Replacing water with stationary water
)
2014-06-01 20:00:11 +01:00
)
2013-03-22 16:48:45 +00:00
)
)
2012-06-14 13:06:06 +00:00
{
2013-03-14 21:56:01 +00:00
m_PendingSendBlocks . push_back ( sSetBlock ( m_PosX , m_PosZ , a_RelX , a_RelY , a_RelZ , a_BlockType , a_BlockMeta ));
2012-06-14 13:06:06 +00:00
}
2014-05-21 19:58:48 +01:00
m_ChunkData . SetMeta ( a_RelX , a_RelY , a_RelZ , a_BlockMeta );
2012-06-14 13:06:06 +00:00
// ONLY recalculate lighting if it's necessary!
2014-03-01 21:34:19 +02:00
if (
( cBlockInfo :: GetLightValue ( OldBlockType ) != cBlockInfo :: GetLightValue ( a_BlockType )) ||
( cBlockInfo :: GetSpreadLightFalloff ( OldBlockType ) != cBlockInfo :: GetSpreadLightFalloff ( a_BlockType )) ||
( cBlockInfo :: IsTransparent ( OldBlockType ) != cBlockInfo :: IsTransparent ( a_BlockType ))
2012-06-14 13:06:06 +00:00
)
{
m_IsLightValid = false ;
}
// Update heightmap, if needed:
2013-03-14 21:56:01 +00:00
if ( a_RelY >= m_HeightMap [ a_RelX + a_RelZ * Width ])
2012-06-14 13:06:06 +00:00
{
if ( a_BlockType != E_BLOCK_AIR )
{
2015-07-29 09:04:03 -06:00
m_HeightMap [ a_RelX + a_RelZ * Width ] = static_cast < HEIGHTTYPE > ( a_RelY );
2012-06-14 13:06:06 +00:00
}
else
{
2013-03-14 21:56:01 +00:00
for ( int y = a_RelY - 1 ; y > 0 ; -- y )
2012-06-14 13:06:06 +00:00
{
2014-04-27 06:45:33 -07:00
if ( GetBlock ( a_RelX , y , a_RelZ ) != E_BLOCK_AIR )
2012-06-14 13:06:06 +00:00
{
2015-07-29 09:04:03 -06:00
m_HeightMap [ a_RelX + a_RelZ * Width ] = static_cast < HEIGHTTYPE > ( y );
2012-06-14 13:06:06 +00:00
break ;
}
} // for y - column in m_BlockData
}
}
}
2012-08-25 17:52:08 +00:00
void cChunk :: SendBlockTo ( int a_RelX , int a_RelY , int a_RelZ , cClientHandle * a_Client )
2012-06-14 13:06:06 +00:00
{
2014-10-20 21:55:07 +01:00
if ( a_Client == nullptr )
2012-06-14 13:06:06 +00:00
{
2012-08-25 17:52:08 +00:00
// Queue the block for all clients in the chunk (will be sent in Tick())
2014-04-27 06:45:33 -07:00
m_PendingSendBlocks . push_back ( sSetBlock ( m_PosX , m_PosZ , a_RelX , a_RelY , a_RelZ , GetBlock ( a_RelX , a_RelY , a_RelZ ), GetMeta ( a_RelX , a_RelY , a_RelZ )));
2012-08-25 17:52:08 +00:00
return ;
2012-06-14 13:06:06 +00:00
}
2012-08-25 17:52:08 +00:00
Vector3i wp = PositionToWorldPosition ( a_RelX , a_RelY , a_RelZ );
2014-04-27 06:45:33 -07:00
a_Client -> SendBlockChange ( wp . x , wp . y , wp . z , GetBlock ( a_RelX , a_RelY , a_RelZ ), GetMeta ( a_RelX , a_RelY , a_RelZ ));
2012-11-11 08:48:38 +00:00
// FS #268 - if a BlockEntity digging is cancelled by a plugin, the entire block entity must be re-sent to the client:
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), end = m_BlockEntities . end (); itr != end ; ++ itr )
{
if ((( * itr ) -> GetPosX () == wp . x ) && (( * itr ) -> GetPosY () == wp . y ) && (( * itr ) -> GetPosZ () == wp . z ))
{
( * itr ) -> SendTo ( * a_Client );
}
} // for itr - m_BlockEntities
2012-06-14 13:06:06 +00:00
}
2013-03-01 19:35:29 +00:00
void cChunk :: AddBlockEntity ( cBlockEntity * a_BlockEntity )
2012-06-14 13:06:06 +00:00
{
2013-08-19 09:13:19 +02:00
MarkDirty ();
m_BlockEntities . push_back ( a_BlockEntity );
2012-06-14 13:06:06 +00:00
}
2013-03-01 19:35:29 +00:00
cBlockEntity * cChunk :: GetBlockEntity ( int a_BlockX , int a_BlockY , int a_BlockZ )
2012-06-14 13:06:06 +00:00
{
2014-06-24 09:46:38 +02:00
// Check that the query coords are within chunk bounds:
ASSERT ( a_BlockX >= m_PosX * cChunkDef :: Width );
ASSERT ( a_BlockX < m_PosX * cChunkDef :: Width + cChunkDef :: Width );
ASSERT ( a_BlockZ >= m_PosZ * cChunkDef :: Width );
ASSERT ( a_BlockZ < m_PosZ * cChunkDef :: Width + cChunkDef :: Width );
2012-06-14 13:06:06 +00:00
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (); itr != m_BlockEntities . end (); ++ itr )
{
if (
2013-03-01 19:35:29 +00:00
(( * itr ) -> GetPosX () == a_BlockX ) &&
(( * itr ) -> GetPosY () == a_BlockY ) &&
(( * itr ) -> GetPosZ () == a_BlockZ )
2012-06-14 13:06:06 +00:00
)
{
return * itr ;
}
} // for itr - m_BlockEntities[]
2014-10-20 21:55:07 +01:00
return nullptr ;
2012-06-14 13:06:06 +00:00
}
2014-07-10 18:18:32 +02:00
bool cChunk :: ShouldBeTicked ( void ) const
{
return ( HasAnyClients () || ( m_AlwaysTicked > 0 ));
}
void cChunk :: SetAlwaysTicked ( bool a_AlwaysTicked )
{
if ( a_AlwaysTicked )
{
m_AlwaysTicked += 1 ;
}
else
{
m_AlwaysTicked -= 1 ;
}
}
2012-06-14 13:06:06 +00: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 );
2014-10-20 21:55:07 +01:00
if ( be != nullptr )
2012-06-14 13:06:06 +00:00
{
be -> UsedBy ( a_Player );
}
}
2014-02-18 13:06:18 +01:00
void cChunk :: SetBiomeAt ( int a_RelX , int a_RelZ , EMCSBiome a_Biome )
{
cChunkDef :: SetBiome ( m_BiomeMap , a_RelX , a_RelZ , a_Biome );
MarkDirty ();
}
void cChunk :: SetAreaBiome ( int a_MinRelX , int a_MaxRelX , int a_MinRelZ , int a_MaxRelZ , EMCSBiome a_Biome )
{
for ( int z = a_MinRelZ ; z <= a_MaxRelZ ; z ++ )
{
for ( int x = a_MinRelX ; x <= a_MaxRelX ; x ++ )
{
cChunkDef :: SetBiome ( m_BiomeMap , x , z , a_Biome );
}
}
MarkDirty ();
// Re-send the chunk to all clients:
2015-09-16 17:04:05 +01:00
for ( auto ClientHandle : m_LoadedByClient )
2014-02-18 13:06:18 +01:00
{
2015-09-16 17:04:05 +01:00
m_World -> ForceSendChunkTo ( m_PosX , m_PosZ , cChunkSender :: E_CHUNK_PRIORITY_MEDIUM , ClientHandle );
2014-02-18 13:06:18 +01:00
} // for itr - m_LoadedByClient[]
}
2014-10-15 19:01:55 +02:00
void cChunk :: CollectPickupsByPlayer ( cPlayer & a_Player )
2012-06-14 13:06:06 +00:00
{
2014-10-15 19:01:55 +02:00
double PosX = a_Player . GetPosX ();
double PosY = a_Player . GetPosY ();
double PosZ = a_Player . GetPosZ ();
2012-06-14 13:06:06 +00:00
for ( cEntityList :: iterator itr = m_Entities . begin (); itr != m_Entities . end (); ++ itr )
{
2013-11-12 21:43:20 +00:00
if (( ! ( * itr ) -> IsPickup ()) && ( ! ( * itr ) -> IsProjectile ()))
2012-06-14 13:06:06 +00:00
{
2014-07-17 19:13:23 +02:00
continue ; // Only pickups and projectiles can be picked up
2012-06-14 13:06:06 +00:00
}
2015-07-29 09:04:03 -06:00
float DiffX = static_cast < float > (( * itr ) -> GetPosX () - PosX );
float DiffY = static_cast < float > (( * itr ) -> GetPosY () - PosY );
float DiffZ = static_cast < float > (( * itr ) -> GetPosZ () - PosZ );
2012-06-14 13:06:06 +00:00
float SqrDist = DiffX * DiffX + DiffY * DiffY + DiffZ * DiffZ ;
if ( SqrDist < 1.5f * 1.5f ) // 1.5 block
{
2013-05-17 14:30:18 +00:00
/*
2013-05-13 20:26:45 +00:00
LOG("Pickup %d being collected by player \"%s\", distance %f",
(*itr)->GetUniqueID(), a_Player->GetName().c_str(), SqrDist
);
2013-05-17 14:30:18 +00:00
*/
2012-06-14 13:06:06 +00:00
MarkDirty ();
2013-11-12 21:43:20 +00:00
if (( * itr ) -> IsPickup ())
{
( reinterpret_cast < cPickup *> ( * itr )) -> CollectedBy ( a_Player );
}
else
{
( reinterpret_cast < cProjectileEntity *> ( * itr )) -> CollectedBy ( a_Player );
}
2012-06-14 13:06:06 +00:00
}
2013-05-13 20:26:45 +00:00
else if ( SqrDist < 5 * 5 )
{
2013-05-17 14:30:18 +00:00
/*
2013-05-13 20:26:45 +00:00
LOG("Pickup %d close to player \"%s\", but still too far to collect: %f",
(*itr)->GetUniqueID(), a_Player->GetName().c_str(), SqrDist
);
2013-05-17 14:30:18 +00:00
*/
2013-05-13 20:26:45 +00:00
}
2012-06-14 13:06:06 +00:00
}
}
2013-06-12 07:14:06 +00:00
bool cChunk :: SetSignLines ( 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 )
2012-06-14 13:06:06 +00:00
{
// 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 )
)
)
{
MarkDirty ();
2015-05-08 23:32:02 +01:00
reinterpret_cast < cSignEntity *> ( * itr ) -> SetLines ( a_Line1 , a_Line2 , a_Line3 , a_Line4 );
2012-08-24 07:58:26 +00:00
m_World -> BroadcastBlockEntity ( a_PosX , a_PosY , a_PosZ );
2013-06-12 07:14:06 +00:00
return true ;
2012-06-14 13:06:06 +00:00
}
} // for itr - m_BlockEntities[]
2013-06-12 07:14:06 +00:00
return false ;
2012-06-14 13:06:06 +00:00
}
2015-05-09 11:16:56 +02:00
void cChunk :: RemoveBlockEntity ( cBlockEntity * a_BlockEntity )
2012-06-14 13:06:06 +00:00
{
MarkDirty ();
2013-08-19 09:13:19 +02:00
m_BlockEntities . remove ( a_BlockEntity );
2012-06-14 13:06:06 +00:00
}
2014-05-31 22:28:51 +01:00
bool cChunk :: AddClient ( cClientHandle * a_Client )
2012-06-14 13:06:06 +00:00
{
2015-09-16 17:04:05 +01:00
if ( std :: find ( m_LoadedByClient . begin (), m_LoadedByClient . end (), a_Client ) != m_LoadedByClient . end ())
2012-06-14 13:06:06 +00:00
{
2015-09-16 17:04:05 +01:00
// Already there, nothing needed
return false ;
2012-06-14 13:06:06 +00:00
}
2015-09-16 17:04:05 +01:00
m_LoadedByClient . push_back ( a_Client );
2012-06-14 13:06:06 +00:00
2014-07-21 15:19:48 +02:00
for ( cEntityList :: iterator itr = m_Entities . begin (); itr != m_Entities . end (); ++ itr )
2012-06-14 13:06:06 +00:00
{
2014-01-16 20:31:06 +01:00
/*
// DEBUG:
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()
);
*/
2012-08-24 07:58:26 +00:00
( * itr ) -> SpawnOn ( * a_Client );
2012-06-14 13:06:06 +00:00
}
return true ;
}
2014-05-31 22:28:51 +01:00
void cChunk :: RemoveClient ( cClientHandle * a_Client )
2012-06-14 13:06:06 +00:00
{
2015-09-19 17:22:57 +01:00
auto itr = std :: remove ( m_LoadedByClient . begin (), m_LoadedByClient . end (), a_Client );
// We should always remove at most one client.
ASSERT ( std :: distance ( itr , m_LoadedByClient . end ()) <= 1 );
// Note: itr can equal m_LoadedByClient.end()
m_LoadedByClient . erase ( itr , m_LoadedByClient . end ());
2015-09-16 17:04:05 +01:00
if ( ! a_Client -> IsDestroyed ())
2012-06-14 13:06:06 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto Entity : m_Entities )
2012-06-14 13:06:06 +00:00
{
2015-09-16 17:04:05 +01:00
/*
// DEBUG:
LOGD("chunk [%i, %i] destroying entity #%i for player \"%s\"",
m_PosX, m_PosZ,
(*itr)->GetUniqueID(), a_Client->GetUsername().c_str()
);
*/
a_Client -> SendDestroyEntity ( * Entity );
2012-06-14 13:06:06 +00:00
}
2015-09-16 17:04:05 +01:00
}
2014-05-31 22:28:51 +01:00
2015-09-16 17:04:05 +01:00
return ;
2012-06-14 13:06:06 +00:00
}
2014-07-22 10:24:28 +01:00
bool cChunk :: HasClient ( cClientHandle * a_Client )
2012-06-14 13:06:06 +00:00
{
2015-09-16 17:04:05 +01:00
return std :: find ( m_LoadedByClient . begin (), m_LoadedByClient . end (), a_Client ) != m_LoadedByClient . end ();
2012-06-14 13:06:06 +00:00
}
2014-07-10 18:18:32 +02:00
bool cChunk :: HasAnyClients ( void ) const
2012-06-14 13:06:06 +00:00
{
return ! m_LoadedByClient . empty ();
}
2012-12-21 12:52:14 +00:00
void cChunk :: AddEntity ( cEntity * a_Entity )
2012-06-14 13:06:06 +00:00
{
2012-12-21 12:52:14 +00:00
if ( ! a_Entity -> IsPlayer ())
2012-06-14 13:06:06 +00:00
{
MarkDirty ();
}
2014-05-31 22:28:51 +01:00
2014-07-22 17:26:48 +01:00
ASSERT ( std :: find ( m_Entities . begin (), m_Entities . end (), a_Entity ) == m_Entities . end ()); // Not there already
2014-05-31 22:28:51 +01:00
2012-12-21 12:52:14 +00:00
m_Entities . push_back ( a_Entity );
2012-06-14 13:06:06 +00:00
}
void cChunk :: RemoveEntity ( cEntity * a_Entity )
{
2014-06-21 22:07:38 +01:00
m_Entities . remove ( a_Entity );
2014-05-31 22:28:51 +01:00
// Mark as dirty if it was a server-generated entity:
if ( ! a_Entity -> IsPlayer ())
2012-06-14 13:06:06 +00:00
{
2014-05-31 22:28:51 +01:00
MarkDirty ();
2012-06-14 13:06:06 +00:00
}
}
2015-03-21 15:18:17 +01:00
bool cChunk :: HasEntity ( UInt32 a_EntityID )
2013-04-13 21:02:10 +00:00
{
for ( cEntityList :: const_iterator itr = m_Entities . begin (), end = m_Entities . end (); itr != end ; ++ itr )
{
if (( * itr ) -> GetUniqueID () == a_EntityID )
{
return true ;
}
} // for itr - m_Entities[]
return false ;
}
2012-06-16 08:35:07 +00:00
bool cChunk :: ForEachEntity ( cEntityCallback & a_Callback )
{
// The entity list is locked by the parent chunkmap's CS
2012-06-19 20:31:21 +00:00
for ( cEntityList :: iterator itr = m_Entities . begin (), itr2 = itr ; itr != m_Entities . end (); itr = itr2 )
2012-06-16 08:35:07 +00:00
{
2012-06-19 20:31:21 +00:00
++ itr2 ;
2012-06-16 08:35:07 +00:00
if ( a_Callback . Item ( * itr ))
{
return false ;
}
} // for itr - m_Entitites[]
return true ;
}
2014-09-03 17:00:26 +02:00
bool cChunk :: ForEachEntityInBox ( const cBoundingBox & a_Box , cEntityCallback & a_Callback )
{
// The entity list is locked by the parent chunkmap's CS
for ( cEntityList :: iterator itr = m_Entities . begin (), itr2 = itr ; itr != m_Entities . end (); itr = itr2 )
{
++ itr2 ;
cBoundingBox EntBox (( * itr ) -> GetPosition (), ( * itr ) -> GetWidth () / 2 , ( * itr ) -> GetHeight ());
if ( ! EntBox . DoesIntersect ( a_Box ))
{
// The entity is not in the specified box
continue ;
}
if ( a_Callback . Item ( * itr ))
{
return false ;
}
} // for itr - m_Entitites[]
return true ;
}
2015-03-21 15:18:17 +01:00
bool cChunk :: DoWithEntityByID ( UInt32 a_EntityID , cEntityCallback & a_Callback , bool & a_CallbackResult )
2013-04-13 21:02:10 +00:00
{
// The entity list is locked by the parent chunkmap's CS
for ( cEntityList :: iterator itr = m_Entities . begin (), end = m_Entities . end (); itr != end ; ++ itr )
{
if (( * itr ) -> GetUniqueID () == a_EntityID )
{
a_CallbackResult = a_Callback . Item ( * itr );
return true ;
}
} // for itr - m_Entitites[]
return false ;
}
2013-11-20 21:53:29 +01:00
bool cChunk :: ForEachBlockEntity ( cBlockEntityCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
{
++ itr2 ;
if ( a_Callback . Item ( * itr ))
{
return false ;
}
} // for itr - m_BlockEntitites[]
return true ;
}
2015-09-24 10:48:33 +02:00
bool cChunk :: ForEachBrewingstand ( cBrewingstandCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
{
++ itr2 ;
if ( a_Callback . Item ( reinterpret_cast < cBrewingstandEntity *> ( * itr )))
{
return false ;
}
} // for itr - m_BlockEntitites[]
return true ;
}
2012-06-17 19:58:39 +00:00
bool cChunk :: ForEachChest ( cChestCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
2012-06-19 20:31:21 +00:00
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
2012-06-17 19:58:39 +00:00
{
2012-06-19 20:31:21 +00:00
++ itr2 ;
2012-06-17 19:58:39 +00:00
if (( * itr ) -> GetBlockType () != E_BLOCK_CHEST )
{
continue ;
}
2015-07-29 09:04:03 -06:00
if ( a_Callback . Item ( reinterpret_cast < cChestEntity *> ( * itr )))
2012-06-17 19:58:39 +00:00
{
return false ;
}
} // for itr - m_BlockEntitites[]
return true ;
}
2012-12-26 17:16:33 +00:00
bool cChunk :: ForEachDispenser ( cDispenserCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
{
++ itr2 ;
if (( * itr ) -> GetBlockType () != E_BLOCK_DISPENSER )
{
continue ;
}
2015-07-29 09:04:03 -06:00
if ( a_Callback . Item ( reinterpret_cast < cDispenserEntity *> ( * itr )))
2012-12-26 17:16:33 +00:00
{
return false ;
}
} // for itr - m_BlockEntitites[]
return true ;
}
2013-05-26 14:39:04 +00:00
bool cChunk :: ForEachDropper ( cDropperCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
{
++ itr2 ;
if (( * itr ) -> GetBlockType () != E_BLOCK_DROPPER )
{
continue ;
}
2015-07-29 09:04:03 -06:00
if ( a_Callback . Item ( reinterpret_cast < cDropperEntity *> ( * itr )))
2013-05-26 14:39:04 +00:00
{
return false ;
}
} // for itr - m_BlockEntitites[]
return true ;
}
bool cChunk :: ForEachDropSpenser ( cDropSpenserCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
{
++ itr2 ;
if ((( * itr ) -> GetBlockType () != E_BLOCK_DISPENSER ) && (( * itr ) -> GetBlockType () != E_BLOCK_DROPPER ))
{
continue ;
}
2015-07-29 09:04:03 -06:00
if ( a_Callback . Item ( reinterpret_cast < cDropSpenserEntity *> ( * itr )))
2013-05-26 14:39:04 +00:00
{
return false ;
}
} // for itr - m_BlockEntitites[]
return true ;
}
2012-06-17 19:58:39 +00:00
bool cChunk :: ForEachFurnace ( cFurnaceCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
2012-06-19 20:31:21 +00:00
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
2012-06-17 19:58:39 +00:00
{
2012-06-19 20:31:21 +00:00
++ itr2 ;
2012-06-17 19:58:39 +00:00
switch (( * itr ) -> GetBlockType ())
{
case E_BLOCK_FURNACE :
case E_BLOCK_LIT_FURNACE :
{
break ;
}
default :
{
continue ;
}
}
2015-07-29 09:04:03 -06:00
if ( a_Callback . Item ( reinterpret_cast < cFurnaceEntity *> ( * itr )))
2012-06-17 19:58:39 +00:00
{
return false ;
}
} // for itr - m_BlockEntitites[]
return true ;
}
2013-11-20 21:53:29 +01:00
bool cChunk :: DoWithBlockEntityAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cBlockEntityCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
{
++ itr2 ;
if ((( * itr ) -> GetPosX () != a_BlockX ) || (( * itr ) -> GetPosY () != a_BlockY ) || (( * itr ) -> GetPosZ () != a_BlockZ ))
{
continue ;
}
if ( a_Callback . Item ( * itr ))
{
return false ;
}
return true ;
} // for itr - m_BlockEntitites[]
// Not found:
return false ;
}
2014-08-05 21:48:23 +01:00
bool cChunk :: DoWithRedstonePoweredEntityAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cRedstonePoweredCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
{
++ itr2 ;
if ((( * itr ) -> GetPosX () != a_BlockX ) || (( * itr ) -> GetPosY () != a_BlockY ) || (( * itr ) -> GetPosZ () != a_BlockZ ))
{
continue ;
}
switch (( * itr ) -> GetBlockType ())
{
case E_BLOCK_DROPPER :
case E_BLOCK_DISPENSER :
2014-08-05 22:54:36 +01:00
case E_BLOCK_NOTE_BLOCK :
2014-08-14 01:03:30 +02:00
{
2014-08-06 13:19:22 +01:00
break ;
2014-08-14 01:03:30 +02:00
}
2014-08-05 21:48:23 +01:00
default :
2014-08-14 01:03:30 +02:00
{
2014-08-06 13:19:22 +01:00
// There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
return false ;
2014-08-14 01:03:30 +02:00
}
2014-08-05 21:48:23 +01:00
}
2014-09-11 10:28:48 +02:00
if ( a_Callback . Item ( dynamic_cast < cRedstonePoweredEntity *> ( * itr ))) // Needs dynamic_cast due to multiple inheritance
2014-08-05 21:48:23 +01:00
{
return false ;
}
return true ;
} // for itr - m_BlockEntitites[]
// Not found:
return false ;
}
2014-07-30 22:19:51 +02:00
bool cChunk :: DoWithBeaconAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cBeaconCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
{
++ itr2 ;
if ((( * itr ) -> GetPosX () != a_BlockX ) || (( * itr ) -> GetPosY () != a_BlockY ) || (( * itr ) -> GetPosZ () != a_BlockZ ))
{
continue ;
}
if (( * itr ) -> GetBlockType () != E_BLOCK_BEACON )
{
// There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
return false ;
}
// The correct block entity is here
2015-07-29 09:04:03 -06:00
if ( a_Callback . Item ( reinterpret_cast < cBeaconEntity *> ( * itr )))
2014-07-30 22:19:51 +02:00
{
return false ;
}
return true ;
} // for itr - m_BlockEntitites[]
// Not found:
return false ;
}
2015-09-24 10:48:33 +02:00
bool cChunk :: DoWithBrewingstandAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cBrewingstandCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
{
++ itr2 ;
if ((( * itr ) -> GetPosX () != a_BlockX ) || (( * itr ) -> GetPosY () != a_BlockY ) || (( * itr ) -> GetPosZ () != a_BlockZ ))
{
continue ;
}
if (( * itr ) -> GetBlockType () != E_BLOCK_BREWING_STAND )
{
// There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
return false ;
}
// The correct block entity is here
if ( a_Callback . Item ( reinterpret_cast < cBrewingstandEntity *> ( * itr )))
{
return false ;
}
return true ;
} // for itr - m_BlockEntitites[]
// Not found:
return false ;
}
2012-06-17 19:58:39 +00:00
bool cChunk :: DoWithChestAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cChestCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
2012-06-19 20:31:21 +00:00
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
2012-06-17 19:58:39 +00:00
{
2012-06-19 20:31:21 +00:00
++ itr2 ;
2012-06-17 19:58:39 +00:00
if ((( * itr ) -> GetPosX () != a_BlockX ) || (( * itr ) -> GetPosY () != a_BlockY ) || (( * itr ) -> GetPosZ () != a_BlockZ ))
{
continue ;
}
2014-07-17 19:13:23 +02:00
if ((( * itr ) -> GetBlockType () != E_BLOCK_CHEST ) && (( * itr ) -> GetBlockType () != E_BLOCK_TRAPPED_CHEST )) // Trapped chests use normal chests' handlers
2012-06-17 19:58:39 +00:00
{
// There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
return false ;
}
// The correct block entity is here
2015-07-29 09:04:03 -06:00
if ( a_Callback . Item ( reinterpret_cast < cChestEntity *> ( * itr )))
2012-06-17 19:58:39 +00:00
{
return false ;
}
return true ;
} // for itr - m_BlockEntitites[]
// Not found:
return false ;
}
2012-12-26 17:16:33 +00:00
bool cChunk :: DoWithDispenserAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cDispenserCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
{
++ itr2 ;
if ((( * itr ) -> GetPosX () != a_BlockX ) || (( * itr ) -> GetPosY () != a_BlockY ) || (( * itr ) -> GetPosZ () != a_BlockZ ))
{
continue ;
}
if (( * itr ) -> GetBlockType () != E_BLOCK_DISPENSER )
{
// There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
return false ;
}
// The correct block entity is here
2015-07-29 09:04:03 -06:00
if ( a_Callback . Item ( reinterpret_cast < cDispenserEntity *> ( * itr )))
2012-12-26 17:16:33 +00:00
{
return false ;
}
return true ;
} // for itr - m_BlockEntitites[]
// Not found:
return false ;
}
2013-05-26 14:39:04 +00:00
bool cChunk :: DoWithDropperAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cDropperCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
{
++ itr2 ;
if ((( * itr ) -> GetPosX () != a_BlockX ) || (( * itr ) -> GetPosY () != a_BlockY ) || (( * itr ) -> GetPosZ () != a_BlockZ ))
{
continue ;
}
if (( * itr ) -> GetBlockType () != E_BLOCK_DROPPER )
{
// There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
return false ;
}
// The correct block entity is here
2015-07-29 09:04:03 -06:00
if ( a_Callback . Item ( reinterpret_cast < cDropperEntity *> ( * itr )))
2013-05-26 14:39:04 +00:00
{
return false ;
}
return true ;
} // for itr - m_BlockEntitites[]
// Not found:
return false ;
}
bool cChunk :: DoWithDropSpenserAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cDropSpenserCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
{
++ itr2 ;
if ((( * itr ) -> GetPosX () != a_BlockX ) || (( * itr ) -> GetPosY () != a_BlockY ) || (( * itr ) -> GetPosZ () != a_BlockZ ))
{
continue ;
}
if ((( * itr ) -> GetBlockType () != E_BLOCK_DISPENSER ) && (( * itr ) -> GetBlockType () != E_BLOCK_DROPPER ))
{
// There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
return false ;
}
// The correct block entity is here
2015-07-29 09:04:03 -06:00
if ( a_Callback . Item ( reinterpret_cast < cDropSpenserEntity *> ( * itr )))
2013-05-26 14:39:04 +00:00
{
return false ;
}
return true ;
} // for itr - m_BlockEntitites[]
// Not found:
return false ;
}
2012-06-17 19:58:39 +00:00
bool cChunk :: DoWithFurnaceAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cFurnaceCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
2012-06-19 20:31:21 +00:00
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
2012-06-17 19:58:39 +00:00
{
2012-06-19 20:31:21 +00:00
++ itr2 ;
2012-06-17 19:58:39 +00:00
if ((( * itr ) -> GetPosX () != a_BlockX ) || (( * itr ) -> GetPosY () != a_BlockY ) || (( * itr ) -> GetPosZ () != a_BlockZ ))
{
continue ;
}
switch (( * itr ) -> GetBlockType ())
{
case E_BLOCK_FURNACE :
case E_BLOCK_LIT_FURNACE :
{
break ;
}
default :
{
// There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
return false ;
}
} // switch (BlockType)
2014-07-17 16:33:09 +02:00
// The correct block entity is here,
2015-07-29 09:04:03 -06:00
if ( a_Callback . Item ( reinterpret_cast < cFurnaceEntity *> ( * itr )))
2012-06-17 19:58:39 +00:00
{
return false ;
}
return true ;
} // for itr - m_BlockEntitites[]
// Not found:
return false ;
}
2013-12-14 16:52:22 +00:00
bool cChunk :: DoWithNoteBlockAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cNoteBlockCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
{
++ itr2 ;
if ((( * itr ) -> GetPosX () != a_BlockX ) || (( * itr ) -> GetPosY () != a_BlockY ) || (( * itr ) -> GetPosZ () != a_BlockZ ))
{
continue ;
}
if (( * itr ) -> GetBlockType () != E_BLOCK_NOTE_BLOCK )
{
// There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
return false ;
}
2014-07-17 16:33:09 +02:00
// The correct block entity is here
2015-07-29 09:04:03 -06:00
if ( a_Callback . Item ( reinterpret_cast < cNoteEntity *> ( * itr )))
2013-12-14 16:52:22 +00:00
{
return false ;
}
return true ;
} // for itr - m_BlockEntitites[]
// Not found:
return false ;
}
2014-01-18 15:16:47 +02:00
bool cChunk :: DoWithCommandBlockAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cCommandBlockCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
{
++ itr2 ;
if ((( * itr ) -> GetPosX () != a_BlockX ) || (( * itr ) -> GetPosY () != a_BlockY ) || (( * itr ) -> GetPosZ () != a_BlockZ ))
{
continue ;
}
if (( * itr ) -> GetBlockType () != E_BLOCK_COMMAND_BLOCK )
{
// There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
return false ;
}
2014-07-17 16:33:09 +02:00
// The correct block entity is here,
2015-07-29 09:04:03 -06:00
if ( a_Callback . Item ( reinterpret_cast < cCommandBlockEntity *> ( * itr )))
2014-01-18 15:16:47 +02:00
{
return false ;
}
return true ;
} // for itr - m_BlockEntitites[]
// Not found:
return false ;
}
2014-03-07 11:44:16 +01:00
bool cChunk :: DoWithMobHeadAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cMobHeadCallback & a_Callback )
2014-02-18 21:40:02 +01:00
{
// The blockentity list is locked by the parent chunkmap's CS
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
{
++ itr2 ;
if ((( * itr ) -> GetPosX () != a_BlockX ) || (( * itr ) -> GetPosY () != a_BlockY ) || (( * itr ) -> GetPosZ () != a_BlockZ ))
{
continue ;
}
if (( * itr ) -> GetBlockType () != E_BLOCK_HEAD )
{
// There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
return false ;
}
2014-07-17 16:33:09 +02:00
// The correct block entity is here,
2015-07-29 09:04:03 -06:00
if ( a_Callback . Item ( reinterpret_cast < cMobHeadEntity *> ( * itr )))
2014-02-18 21:40:02 +01:00
{
return false ;
}
return true ;
} // for itr - m_BlockEntitites[]
// Not found:
return false ;
}
2014-03-07 01:30:34 +01:00
bool cChunk :: DoWithFlowerPotAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cFlowerPotCallback & a_Callback )
{
// The blockentity list is locked by the parent chunkmap's CS
for ( cBlockEntityList :: iterator itr = m_BlockEntities . begin (), itr2 = itr ; itr != m_BlockEntities . end (); itr = itr2 )
{
++ itr2 ;
if ((( * itr ) -> GetPosX () != a_BlockX ) || (( * itr ) -> GetPosY () != a_BlockY ) || (( * itr ) -> GetPosZ () != a_BlockZ ))
{
continue ;
}
if (( * itr ) -> GetBlockType () != E_BLOCK_FLOWER_POT )
{
// There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
return false ;
}
2014-07-17 16:33:09 +02:00
// The correct block entity is here
2015-07-29 09:04:03 -06:00
if ( a_Callback . Item ( reinterpret_cast < cFlowerPotEntity *> ( * itr )))
2014-03-07 01:30:34 +01:00
{
return false ;
}
return true ;
} // for itr - m_BlockEntitites[]
// Not found:
return false ;
}
2012-06-17 19:58:39 +00:00
bool cChunk :: GetSignLines ( int a_BlockX , int a_BlockY , int a_BlockZ , AString & a_Line1 , AString & a_Line2 , AString & a_Line3 , AString & a_Line4 )
{
// The blockentity list is locked by the parent chunkmap's CS
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 ))
{
continue ;
}
switch (( * itr ) -> GetBlockType ())
{
case E_BLOCK_WALLSIGN :
case E_BLOCK_SIGN_POST :
{
2015-07-29 09:04:03 -06:00
a_Line1 = reinterpret_cast < cSignEntity *> ( * itr ) -> GetLine ( 0 );
a_Line2 = reinterpret_cast < cSignEntity *> ( * itr ) -> GetLine ( 1 );
a_Line3 = reinterpret_cast < cSignEntity *> ( * itr ) -> GetLine ( 2 );
a_Line4 = reinterpret_cast < cSignEntity *> ( * itr ) -> GetLine ( 3 );
2012-06-17 19:58:39 +00:00
return true ;
}
} // switch (BlockType)
// There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
return false ;
} // for itr - m_BlockEntitites[]
// Not found:
return false ;
}
2013-03-15 20:18:11 +00:00
BLOCKTYPE cChunk :: GetBlock ( int a_RelX , int a_RelY , int a_RelZ ) const
2012-06-14 13:06:06 +00:00
{
2013-03-15 20:18:11 +00:00
if (
2014-07-17 16:33:09 +02:00
( a_RelX < 0 ) || ( a_RelX >= Width ) ||
2013-03-15 20:18:11 +00:00
( a_RelY < 0 ) || ( a_RelY >= Height ) ||
( a_RelZ < 0 ) || ( a_RelZ >= Width )
)
{
ASSERT ( ! "GetBlock(x, y, z) out of bounds!" );
2014-07-17 19:13:23 +02:00
return 0 ; // Clip
2013-03-15 20:18:11 +00:00
}
2012-06-14 13:06:06 +00:00
2014-05-21 19:58:48 +01:00
return m_ChunkData . GetBlock ( a_RelX , a_RelY , a_RelZ );
2012-06-14 13:06:06 +00:00
}
2014-10-19 15:11:53 +02:00
void cChunk :: GetBlockTypeMeta ( int a_RelX , int a_RelY , int a_RelZ , BLOCKTYPE & a_BlockType , NIBBLETYPE & a_BlockMeta ) const
2012-06-14 13:06:06 +00:00
{
2014-04-26 10:50:23 -07:00
a_BlockType = GetBlock ( a_RelX , a_RelY , a_RelZ );
2014-05-21 19:58:48 +01:00
a_BlockMeta = m_ChunkData . GetMeta ( a_RelX , a_RelY , a_RelZ );
2012-06-14 13:06:06 +00:00
}
2012-10-20 11:40:34 +00:00
void cChunk :: GetBlockInfo ( int a_RelX , int a_RelY , int a_RelZ , BLOCKTYPE & a_BlockType , NIBBLETYPE & a_Meta , NIBBLETYPE & a_SkyLight , NIBBLETYPE & a_BlockLight )
{
2014-04-27 06:45:33 -07:00
a_BlockType = GetBlock ( a_RelX , a_RelY , a_RelZ );
2014-05-21 19:58:48 +01:00
a_Meta = m_ChunkData . GetMeta ( a_RelX , a_RelY , a_RelZ );
a_SkyLight = m_ChunkData . GetSkyLight ( a_RelX , a_RelY , a_RelZ );
a_BlockLight = m_ChunkData . GetBlockLight ( a_RelX , a_RelY , a_RelZ );
2012-10-20 11:40:34 +00:00
}
2013-03-01 19:35:29 +00:00
cChunk * cChunk :: GetNeighborChunk ( int a_BlockX , int a_BlockZ )
2013-02-28 07:42:45 +00:00
{
// Convert coords to relative, then call the relative version:
a_BlockX -= m_PosX * cChunkDef :: Width ;
a_BlockZ -= m_PosZ * cChunkDef :: Width ;
2013-03-01 19:35:29 +00:00
return GetRelNeighborChunk ( a_BlockX , a_BlockZ );
2013-02-28 07:42:45 +00:00
}
2013-03-01 19:35:29 +00:00
cChunk * cChunk :: GetRelNeighborChunk ( int a_RelX , int a_RelZ )
2013-02-28 07:42:45 +00:00
{
2014-03-27 18:13:52 +01:00
// If the relative coords are too far away, use the parent's chunk lookup instead:
2014-04-08 08:57:35 -07:00
if (( a_RelX < - 128 ) || ( a_RelX > 128 ) || ( a_RelZ < - 128 ) || ( a_RelZ > 128 ))
2014-03-27 18:13:52 +01:00
{
int BlockX = m_PosX * cChunkDef :: Width + a_RelX ;
int BlockZ = m_PosZ * cChunkDef :: Width + a_RelZ ;
2014-07-06 23:50:22 +01:00
int ChunkX , ChunkZ ;
BlockToChunk ( BlockX , BlockZ , ChunkX , ChunkZ );
2014-08-28 11:36:35 +02:00
return m_ChunkMap -> GetChunkNoLoad ( ChunkX , ChunkZ );
2014-03-27 18:13:52 +01:00
}
// Walk the neighbors:
2013-02-28 07:42:45 +00:00
bool ReturnThis = true ;
if ( a_RelX < 0 )
{
2014-10-20 21:55:07 +01:00
if ( m_NeighborXM != nullptr )
2013-02-28 07:42:45 +00:00
{
2013-03-01 19:35:29 +00:00
cChunk * Candidate = m_NeighborXM -> GetRelNeighborChunk ( a_RelX + cChunkDef :: Width , a_RelZ );
2014-10-20 21:55:07 +01:00
if ( Candidate != nullptr )
2013-02-28 07:42:45 +00:00
{
return Candidate ;
}
}
// Going X first failed, but if the request is crossing Z as well, let's try the Z first later on.
ReturnThis = false ;
}
else if ( a_RelX >= cChunkDef :: Width )
{
2014-10-20 21:55:07 +01:00
if ( m_NeighborXP != nullptr )
2013-02-28 07:42:45 +00:00
{
2013-03-01 19:35:29 +00:00
cChunk * Candidate = m_NeighborXP -> GetRelNeighborChunk ( a_RelX - cChunkDef :: Width , a_RelZ );
2014-10-20 21:55:07 +01:00
if ( Candidate != nullptr )
2013-02-28 07:42:45 +00:00
{
return Candidate ;
}
}
// Going X first failed, but if the request is crossing Z as well, let's try the Z first later on.
ReturnThis = false ;
}
if ( a_RelZ < 0 )
{
2014-10-20 21:55:07 +01:00
if ( m_NeighborZM != nullptr )
2013-02-28 07:42:45 +00:00
{
2013-03-01 19:35:29 +00:00
return m_NeighborZM -> GetRelNeighborChunk ( a_RelX , a_RelZ + cChunkDef :: Width );
2013-02-28 07:42:45 +00:00
// For requests crossing both X and Z, the X-first way has been already tried
}
2014-10-20 21:55:07 +01:00
return nullptr ;
2013-03-03 19:05:11 +00:00
}
else if ( a_RelZ >= cChunkDef :: Width )
{
2014-10-20 21:55:07 +01:00
if ( m_NeighborZP != nullptr )
2013-02-28 07:42:45 +00:00
{
2013-03-01 19:35:29 +00:00
return m_NeighborZP -> GetRelNeighborChunk ( a_RelX , a_RelZ - cChunkDef :: Width );
2013-02-28 07:42:45 +00:00
// For requests crossing both X and Z, the X-first way has been already tried
}
2014-10-20 21:55:07 +01:00
return nullptr ;
2013-02-28 07:42:45 +00:00
}
2014-10-20 21:55:07 +01:00
return ( ReturnThis ? this : nullptr );
2013-02-28 07:42:45 +00:00
}
2013-10-22 17:54:09 +02:00
cChunk * cChunk :: GetRelNeighborChunkAdjustCoords ( int & a_RelX , int & a_RelZ ) const
2013-05-28 12:18:03 +00:00
{
2013-10-22 17:54:09 +02:00
cChunk * ToReturn = const_cast < cChunk *> ( this );
// The most common case: inside this chunk:
if (
( a_RelX >= 0 ) && ( a_RelX < Width ) &&
( a_RelZ >= 0 ) && ( a_RelZ < Width )
)
{
return ToReturn ;
}
// Request for a different chunk, calculate chunk offset:
int RelX = a_RelX ; // Make a local copy of the coords (faster access)
2013-05-28 12:18:03 +00:00
int RelZ = a_RelZ ;
2014-10-20 21:55:07 +01:00
while (( RelX >= Width ) && ( ToReturn != nullptr ))
2013-05-28 12:18:03 +00:00
{
2013-10-22 17:54:09 +02:00
RelX -= Width ;
ToReturn = ToReturn -> m_NeighborXP ;
2013-05-28 12:18:03 +00:00
}
2014-10-20 21:55:07 +01:00
while (( RelX < 0 ) && ( ToReturn != nullptr ))
2013-05-28 12:18:03 +00:00
{
2013-10-22 17:54:09 +02:00
RelX += Width ;
ToReturn = ToReturn -> m_NeighborXM ;
2013-05-28 12:18:03 +00:00
}
2014-10-20 21:55:07 +01:00
while (( RelZ >= Width ) && ( ToReturn != nullptr ))
2013-05-28 12:18:03 +00:00
{
2013-10-22 17:54:09 +02:00
RelZ -= Width ;
ToReturn = ToReturn -> m_NeighborZP ;
2013-05-28 12:18:03 +00:00
}
2014-10-20 21:55:07 +01:00
while (( RelZ < 0 ) && ( ToReturn != nullptr ))
2013-05-28 12:18:03 +00:00
{
2013-10-22 17:54:09 +02:00
RelZ += Width ;
ToReturn = ToReturn -> m_NeighborZM ;
}
2014-10-20 21:55:07 +01:00
if ( ToReturn != nullptr )
2013-10-22 17:54:09 +02:00
{
a_RelX = RelX ;
a_RelZ = RelZ ;
return ToReturn ;
2013-05-28 12:18:03 +00:00
}
2013-10-22 17:54:09 +02:00
// The chunk cannot be walked through neighbors, find it through the chunkmap:
int AbsX = a_RelX + m_PosX * Width ;
int AbsZ = a_RelZ + m_PosZ * Width ;
int DstChunkX , DstChunkZ ;
BlockToChunk ( AbsX , AbsZ , DstChunkX , DstChunkZ );
ToReturn = m_ChunkMap -> FindChunk ( DstChunkX , DstChunkZ );
a_RelX = AbsX - DstChunkX * Width ;
a_RelZ = AbsZ - DstChunkZ * Width ;
return ToReturn ;
2013-05-28 12:18:03 +00:00
}
2013-03-03 19:05:11 +00:00
void cChunk :: BroadcastAttachEntity ( const cEntity & a_Entity , const cEntity * a_Vehicle )
{
2015-09-16 17:04:05 +01:00
for ( auto ClientHandle : m_LoadedByClient )
2013-03-03 19:05:11 +00:00
{
2015-09-16 17:04:05 +01:00
ClientHandle -> SendAttachEntity ( a_Entity , a_Vehicle );
2013-03-03 19:05:11 +00:00
} // for itr - LoadedByClient[]
}
2013-07-07 13:06:06 +00:00
void cChunk :: BroadcastBlockAction ( int a_BlockX , int a_BlockY , int a_BlockZ , char a_Byte1 , char a_Byte2 , BLOCKTYPE a_BlockType , const cClientHandle * a_Exclude )
2012-08-19 11:51:17 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-08-19 11:51:17 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2013-07-07 13:06:06 +00:00
( * itr ) -> SendBlockAction ( a_BlockX , a_BlockY , a_BlockZ , a_Byte1 , a_Byte2 , a_BlockType );
2012-08-19 11:51:17 +00:00
} // for itr - LoadedByClient[]
}
2015-03-21 16:11:57 +01:00
void cChunk :: BroadcastBlockBreakAnimation ( UInt32 a_EntityID , int a_BlockX , int a_BlockY , int a_BlockZ , char a_Stage , const cClientHandle * a_Exclude )
2012-08-19 11:51:17 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-08-19 11:51:17 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2015-03-21 16:11:57 +01:00
( * itr ) -> SendBlockBreakAnim ( a_EntityID , a_BlockX , a_BlockY , a_BlockZ , a_Stage );
2012-08-19 11:51:17 +00:00
} // for itr - LoadedByClient[]
2013-03-18 02:51:55 +00:00
}
2013-07-07 13:06:06 +00:00
void cChunk :: BroadcastBlockEntity ( int a_BlockX , int a_BlockY , int a_BlockZ , const cClientHandle * a_Exclude )
2013-03-18 02:51:55 +00:00
{
2013-07-07 13:06:06 +00:00
// We can operate on entity pointers, we're inside the ChunkMap's CS lock which guards the list
cBlockEntity * Entity = GetBlockEntity ( a_BlockX , a_BlockY , a_BlockZ );
2014-10-20 21:55:07 +01:00
if ( Entity == nullptr )
2013-07-07 13:06:06 +00:00
{
return ;
}
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2013-03-18 02:51:55 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2013-07-07 13:06:06 +00:00
Entity -> SendTo ( * ( * itr ));
2013-03-18 02:51:55 +00:00
} // for itr - LoadedByClient[]
2012-08-19 11:51:17 +00:00
}
2014-06-27 20:56:29 +02:00
void cChunk :: BroadcastCollectEntity ( const cEntity & a_Entity , const cPlayer & a_Player , const cClientHandle * a_Exclude )
2012-08-19 19:42:32 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-08-19 19:42:32 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2014-06-27 20:56:29 +02:00
( * itr ) -> SendCollectEntity ( a_Entity , a_Player );
2012-08-19 19:42:32 +00:00
} // for itr - LoadedByClient[]
}
2013-07-07 13:06:06 +00:00
void cChunk :: BroadcastDestroyEntity ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-19 19:42:32 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-08-19 19:42:32 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2013-07-07 13:06:06 +00:00
( * itr ) -> SendDestroyEntity ( a_Entity );
2012-08-19 19:42:32 +00:00
} // for itr - LoadedByClient[]
}
2013-12-15 10:51:46 +01:00
void cChunk :: BroadcastEntityEffect ( const cEntity & a_Entity , int a_EffectID , int a_Amplifier , short a_Duration , const cClientHandle * a_Exclude )
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2013-12-15 10:51:46 +01:00
{
if ( * itr == a_Exclude )
{
continue ;
}
( * itr ) -> SendEntityEffect ( a_Entity , a_EffectID , a_Amplifier , a_Duration );
} // for itr - LoadedByClient[]
}
2013-07-07 13:06:06 +00:00
void cChunk :: BroadcastEntityEquipment ( const cEntity & a_Entity , short a_SlotNum , const cItem & a_Item , const cClientHandle * a_Exclude )
2012-08-19 19:42:32 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-08-19 19:42:32 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2013-07-07 13:06:06 +00:00
( * itr ) -> SendEntityEquipment ( a_Entity , a_SlotNum , a_Item );
2012-08-19 19:42:32 +00:00
} // for itr - LoadedByClient[]
}
2013-07-07 13:06:06 +00:00
void cChunk :: BroadcastEntityHeadLook ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-19 19:42:32 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-08-19 19:42:32 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2013-07-07 13:06:06 +00:00
( * itr ) -> SendEntityHeadLook ( a_Entity );
2012-08-19 19:42:32 +00:00
} // for itr - LoadedByClient[]
}
2013-07-07 13:06:06 +00:00
void cChunk :: BroadcastEntityLook ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-19 19:42:32 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-08-19 19:42:32 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2013-07-07 13:06:06 +00:00
( * itr ) -> SendEntityLook ( a_Entity );
2012-08-19 19:42:32 +00:00
} // for itr - LoadedByClient[]
}
2013-07-07 13:06:06 +00:00
void cChunk :: BroadcastEntityMetadata ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-19 21:14:45 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-08-19 21:14:45 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2013-07-07 13:06:06 +00:00
( * itr ) -> SendEntityMetadata ( a_Entity );
2012-08-19 21:14:45 +00:00
} // for itr - LoadedByClient[]
}
2013-07-07 13:06:06 +00:00
void cChunk :: BroadcastEntityRelMove ( const cEntity & a_Entity , char a_RelX , char a_RelY , char a_RelZ , const cClientHandle * a_Exclude )
2012-08-19 21:14:45 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-08-19 21:14:45 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2013-07-07 13:06:06 +00:00
( * itr ) -> SendEntityRelMove ( a_Entity , a_RelX , a_RelY , a_RelZ );
2012-08-19 21:14:45 +00:00
} // for itr - LoadedByClient[]
}
2013-07-07 13:06:06 +00:00
void cChunk :: BroadcastEntityRelMoveLook ( const cEntity & a_Entity , char a_RelX , char a_RelY , char a_RelZ , const cClientHandle * a_Exclude )
2012-08-24 07:58:26 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-08-24 07:58:26 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2013-07-07 13:06:06 +00:00
( * itr ) -> SendEntityRelMoveLook ( a_Entity , a_RelX , a_RelY , a_RelZ );
2012-08-24 07:58:26 +00:00
} // for itr - LoadedByClient[]
}
2013-07-07 13:06:06 +00:00
void cChunk :: BroadcastEntityStatus ( const cEntity & a_Entity , char a_Status , const cClientHandle * a_Exclude )
2012-08-24 09:49:00 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-08-24 09:49:00 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2013-07-07 13:06:06 +00:00
( * itr ) -> SendEntityStatus ( a_Entity , a_Status );
2012-08-24 09:49:00 +00:00
} // for itr - LoadedByClient[]
}
2013-07-07 13:06:06 +00:00
void cChunk :: BroadcastEntityVelocity ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-25 21:46:18 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-08-25 21:46:18 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2013-07-07 13:06:06 +00:00
( * itr ) -> SendEntityVelocity ( a_Entity );
2012-08-25 21:46:18 +00:00
} // for itr - LoadedByClient[]
}
2013-12-06 23:47:07 +00:00
void cChunk :: BroadcastEntityAnimation ( const cEntity & a_Entity , char a_Animation , const cClientHandle * a_Exclude )
2012-09-11 12:01:34 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-09-11 12:01:34 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2013-12-06 23:47:07 +00:00
( * itr ) -> SendEntityAnimation ( a_Entity , a_Animation );
2012-09-11 12:01:34 +00:00
} // for itr - LoadedByClient[]
}
2014-09-11 17:03:09 +02:00
void cChunk :: BroadcastParticleEffect ( const AString & a_ParticleName , float a_SrcX , float a_SrcY , float a_SrcZ , float a_OffsetX , float a_OffsetY , float a_OffsetZ , float a_ParticleData , int a_ParticleAmount , cClientHandle * a_Exclude )
2013-12-22 14:45:25 +01:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2013-12-22 14:45:25 +01:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2014-09-11 17:03:09 +02:00
( * itr ) -> SendParticleEffect ( a_ParticleName , a_SrcX , a_SrcY , a_SrcZ , a_OffsetX , a_OffsetY , a_OffsetZ , a_ParticleData , a_ParticleAmount );
2013-12-22 14:45:25 +01:00
} // for itr - LoadedByClient[]
}
2013-12-15 10:51:46 +01:00
void cChunk :: BroadcastRemoveEntityEffect ( const cEntity & a_Entity , int a_EffectID , const cClientHandle * a_Exclude )
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2013-12-15 10:51:46 +01:00
{
if ( * itr == a_Exclude )
{
continue ;
}
( * itr ) -> SendRemoveEntityEffect ( a_Entity , a_EffectID );
} // for itr - LoadedByClient[]
}
2014-07-13 02:08:02 +02:00
void cChunk :: BroadcastSoundEffect ( const AString & a_SoundName , double a_X , double a_Y , double a_Z , float a_Volume , float a_Pitch , const cClientHandle * a_Exclude )
2012-10-21 07:46:28 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-10-21 07:46:28 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2014-07-13 02:08:02 +02:00
( * itr ) -> SendSoundEffect ( a_SoundName , a_X , a_Y , a_Z , a_Volume , a_Pitch );
2012-10-21 07:46:28 +00:00
} // for itr - LoadedByClient[]
}
2013-07-07 13:06:06 +00:00
void cChunk :: BroadcastSoundParticleEffect ( int a_EffectID , int a_SrcX , int a_SrcY , int a_SrcZ , int a_Data , const cClientHandle * a_Exclude )
2012-09-25 09:54:36 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-09-25 09:54:36 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2013-07-07 13:06:06 +00:00
( * itr ) -> SendSoundParticleEffect ( a_EffectID , a_SrcX , a_SrcY , a_SrcZ , a_Data );
2012-09-25 09:54:36 +00:00
} // for itr - LoadedByClient[]
}
2013-07-07 13:06:06 +00:00
void cChunk :: BroadcastSpawnEntity ( cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-09-29 20:43:42 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-09-29 20:43:42 +00:00
{
2013-07-07 13:06:06 +00:00
if ( * itr == a_Exclude )
{
continue ;
}
a_Entity . SpawnOn ( * ( * itr ));
2012-09-29 20:43:42 +00:00
} // for itr - LoadedByClient[]
}
2013-07-07 13:06:06 +00:00
void cChunk :: BroadcastThunderbolt ( int a_BlockX , int a_BlockY , int a_BlockZ , const cClientHandle * a_Exclude )
2012-08-26 21:01:07 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-08-26 21:01:07 +00:00
{
if ( * itr == a_Exclude )
{
continue ;
}
2013-07-07 13:06:06 +00:00
( * itr ) -> SendThunderbolt ( a_BlockX , a_BlockY , a_BlockZ );
2012-08-26 21:01:07 +00:00
} // for itr - LoadedByClient[]
}
2014-07-21 15:19:48 +02:00
void cChunk :: BroadcastUseBed ( const cEntity & a_Entity , int a_BlockX , int a_BlockY , int a_BlockZ )
2012-08-24 07:58:26 +00:00
{
2015-09-16 17:04:05 +01:00
for ( auto itr = m_LoadedByClient . begin (); itr != m_LoadedByClient . end (); ++ itr )
2012-08-24 07:58:26 +00:00
{
2013-07-07 13:06:06 +00:00
( * itr ) -> SendUseBed ( a_Entity , a_BlockX , a_BlockY , a_BlockZ );
2012-08-24 07:58:26 +00:00
} // for itr - LoadedByClient[]
}
void cChunk :: SendBlockEntity ( int a_BlockX , int a_BlockY , int a_BlockZ , cClientHandle & a_Client )
{
cBlockEntity * Entity = GetBlockEntity ( a_BlockX , a_BlockY , a_BlockZ );
2014-10-20 21:55:07 +01:00
if ( Entity == nullptr )
2012-08-24 07:58:26 +00:00
{
return ;
}
Entity -> SendTo ( a_Client );
}
2012-08-25 17:52:08 +00:00
void cChunk :: PositionToWorldPosition ( int a_RelX , int a_RelY , int a_RelZ , int & a_BlockX , int & a_BlockY , int & a_BlockZ )
2012-06-14 13:06:06 +00:00
{
2012-08-25 17:52:08 +00:00
a_BlockY = a_RelY ;
a_BlockX = m_PosX * Width + a_RelX ;
a_BlockZ = m_PosZ * Width + a_RelZ ;
2012-06-14 13:06:06 +00:00
}
2012-08-25 17:52:08 +00:00
Vector3i cChunk :: PositionToWorldPosition ( int a_RelX , int a_RelY , int a_RelZ )
2012-06-14 13:06:06 +00:00
{
2014-09-06 19:01:30 +02:00
return Vector3i ( m_PosX * Width + a_RelX , a_RelY , m_PosZ * Width + a_RelZ );
2012-06-14 13:06:06 +00:00
}
2013-10-29 10:44:51 -06:00
NIBBLETYPE cChunk :: GetTimeAlteredLight ( NIBBLETYPE a_Skylight ) const
{
a_Skylight -= m_World -> GetSkyDarkness ();
2013-10-30 16:14:42 -06:00
// Because NIBBLETYPE is unsigned, we clamp it to 0 .. 15 by checking for values above 15
2013-10-29 10:44:51 -06:00
return ( a_Skylight < 16 ) ? a_Skylight : 0 ;
}
2012-06-14 13:06:06 +00:00