2013-08-11 15:05:44 -04:00
2012-06-14 09:06:06 -04:00
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
# include "BlockID.h"
2012-09-23 18:09:57 -04:00
# include "World.h"
2012-06-14 09:06:06 -04:00
# include "ChunkDef.h"
2012-09-23 18:09:57 -04:00
# include "ClientHandle.h"
# include "Server.h"
# include "Item.h"
# include "Root.h"
2012-06-14 09:06:06 -04:00
# include "../iniFile/iniFile.h"
2012-09-23 18:09:57 -04:00
# include "ChunkMap.h"
2013-08-19 16:23:25 -04:00
# include "OSSupport/Timer.h"
2012-10-13 05:53:28 -04:00
2013-08-22 02:55:58 -04:00
// Entities (except mobs):
# include "Entities/Pickup.h"
# include "Entities/Player.h"
# include "Entities/TNTEntity.h"
2012-10-13 05:53:28 -04:00
// Simulators:
# include "Simulator/SimulatorManager.h"
2012-10-14 13:06:21 -04:00
# include "Simulator/FloodyFluidSimulator.h"
2012-10-13 05:53:28 -04:00
# include "Simulator/FluidSimulator.h"
# include "Simulator/FireSimulator.h"
2013-03-14 16:03:42 -04:00
# include "Simulator/NoopFluidSimulator.h"
2012-10-13 05:53:28 -04:00
# include "Simulator/SandSimulator.h"
# include "Simulator/RedstoneSimulator.h"
2013-03-14 15:44:27 -04:00
# include "Simulator/VaporizeFluidSimulator.h"
2012-09-23 16:53:08 -04:00
// Mobs:
2013-09-07 14:07:56 -04:00
# include "Mobs/IncludeAllMonsters.h"
2013-09-07 16:19:56 -04:00
# include "MobCensus.h"
2013-09-07 20:47:02 -04:00
# include "MobSpawner.h"
2012-09-23 16:53:08 -04:00
2012-06-14 09:06:06 -04:00
# include "MersenneTwister.h"
2012-09-23 16:14:04 -04:00
# include "Generating/Trees.h"
2012-09-23 18:09:57 -04:00
# include "PluginManager.h"
2012-09-29 09:59:32 -04:00
# include "Blocks/BlockHandler.h"
2012-06-14 09:06:06 -04:00
# include "Vector3d.h"
2013-08-19 05:39:13 -04:00
# include "Tracer.h"
2012-06-14 09:06:06 -04:00
# include "tolua++.h"
2013-08-04 10:08:09 -04:00
// DEBUG: Test out the cLineBlockTracer class by tracing a few lines:
# include "LineBlockTracer.h"
2012-06-14 09:06:06 -04:00
# ifndef _WIN32
# include <stdlib.h>
# endif
/// Up to this many m_SpreadQueue elements are handled each world tick
const int MAX_LIGHTING_SPREAD_PER_TICK = 10 ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorldLoadProgress:
/// A simple thread that displays the progress of world loading / saving in cWorld::InitializeSpawn()
class cWorldLoadProgress :
public cIsThread
{
public :
cWorldLoadProgress ( cWorld * a_World ) :
cIsThread ( " cWorldLoadProgress " ) ,
m_World ( a_World )
{
Start ( ) ;
}
void Stop ( void )
{
m_ShouldTerminate = true ;
Wait ( ) ;
}
protected :
cWorld * m_World ;
virtual void Execute ( void ) override
{
for ( ; ; )
{
LOG ( " %d chunks to load, %d chunks to generate " ,
m_World - > GetStorage ( ) . GetLoadQueueLength ( ) ,
m_World - > GetGenerator ( ) . GetQueueLength ( )
) ;
// Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish
for ( int i = 0 ; i < 20 ; i + + )
{
cSleep : : MilliSleep ( 100 ) ;
if ( m_ShouldTerminate )
{
return ;
}
}
} // for (-ever)
}
} ;
2013-08-11 13:18:06 -04:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorldLightingProgress:
2012-06-14 09:06:06 -04:00
/// A simple thread that displays the progress of world lighting in cWorld::InitializeSpawn()
class cWorldLightingProgress :
public cIsThread
{
public :
cWorldLightingProgress ( cLightingThread * a_Lighting ) :
cIsThread ( " cWorldLightingProgress " ) ,
m_Lighting ( a_Lighting )
{
Start ( ) ;
}
void Stop ( void )
{
m_ShouldTerminate = true ;
Wait ( ) ;
}
protected :
cLightingThread * m_Lighting ;
virtual void Execute ( void ) override
{
for ( ; ; )
{
LOG ( " %d chunks remaining to light " , m_Lighting - > GetQueueLength ( )
) ;
// Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish
for ( int i = 0 ; i < 20 ; i + + )
{
cSleep : : MilliSleep ( 100 ) ;
if ( m_ShouldTerminate )
{
return ;
}
}
} // for (-ever)
}
} ;
2013-06-15 11:29:20 -04:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorld::cLock:
cWorld : : cLock : : cLock ( cWorld & a_World ) :
super ( & ( a_World . m_ChunkMap - > GetCS ( ) ) )
{
}
2012-06-14 09:06:06 -04:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2013-08-11 13:18:06 -04:00
// cWorld::cTickThread:
2012-06-14 09:06:06 -04:00
2013-08-11 13:18:06 -04:00
cWorld : : cTickThread : : cTickThread ( cWorld & a_World ) :
super ( Printf ( " WorldTickThread: %s " , a_World . GetName ( ) . c_str ( ) ) ) ,
m_World ( a_World )
2012-06-14 09:06:06 -04:00
{
2013-08-11 13:18:06 -04:00
}
2012-06-14 09:06:06 -04:00
2013-08-11 13:18:06 -04:00
void cWorld : : cTickThread : : Execute ( void )
{
2013-08-19 16:23:25 -04:00
cTimer Timer ;
long long msPerTick = 50 ;
long long LastTime = Timer . GetNowTime ( ) ;
2013-08-11 13:18:06 -04:00
while ( ! m_ShouldTerminate )
2013-03-11 13:15:34 -04:00
{
2013-08-19 16:23:25 -04:00
long long NowTime = Timer . GetNowTime ( ) ;
float DeltaTime = ( float ) ( NowTime - LastTime ) ;
m_World . Tick ( DeltaTime ) ;
long long TickTime = Timer . GetNowTime ( ) - NowTime ;
if ( TickTime < msPerTick )
2013-03-11 13:15:34 -04:00
{
2013-08-19 16:23:25 -04:00
// Stretch tick time until it's at least msPerTick
cSleep : : MilliSleep ( ( unsigned int ) ( msPerTick - TickTime ) ) ;
2013-03-11 13:15:34 -04:00
}
2013-08-19 16:23:25 -04:00
LastTime = NowTime ;
2012-06-14 09:06:06 -04:00
}
2013-08-11 13:18:06 -04:00
}
2012-06-14 09:06:06 -04:00
2012-07-15 16:36:34 -04:00
2012-06-14 09:06:06 -04:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorld:
2012-10-14 13:06:21 -04:00
2012-10-25 15:20:29 -04:00
cWorld : : cWorld ( const AString & a_WorldName ) :
2013-08-11 13:18:06 -04:00
m_WorldName ( a_WorldName ) ,
m_IniFileName ( m_WorldName + " /world.ini " ) ,
2013-08-11 14:16:41 -04:00
m_StorageSchema ( " Default " ) ,
2012-11-01 17:38:20 -04:00
m_WorldAgeSecs ( 0 ) ,
m_TimeOfDaySecs ( 0 ) ,
m_WorldAge ( 0 ) ,
m_TimeOfDay ( 0 ) ,
m_LastTimeUpdate ( 0 ) ,
2012-10-25 15:20:29 -04:00
m_RSList ( 0 ) ,
m_Weather ( eWeather_Sunny ) ,
2013-08-11 13:18:06 -04:00
m_WeatherInterval ( 24000 ) , // Guaranteed 1 day of sunshine at server start :)
m_TickThread ( * this )
2012-06-14 09:06:06 -04:00
{
2013-08-11 15:05:44 -04:00
LOGD ( " cWorld::cWorld( \" %s \" ) " , a_WorldName . c_str ( ) ) ;
2012-06-14 09:06:06 -04:00
2013-10-09 03:57:48 -04:00
cFile : : CreateFolder ( FILE_IO_PREFIX + m_WorldName ) ;
2012-06-14 09:06:06 -04:00
}
2013-01-11 23:46:01 -05:00
cWorld : : ~ cWorld ( )
{
delete m_SimulatorManager ;
delete m_SandSimulator ;
delete m_WaterSimulator ;
delete m_LavaSimulator ;
delete m_FireSimulator ;
delete m_RedstoneSimulator ;
UnloadUnusedChunks ( ) ;
m_Storage . WaitForFinish ( ) ;
delete m_ChunkMap ;
}
2013-02-13 14:22:08 -05:00
void cWorld : : CastThunderbolt ( int a_BlockX , int a_BlockY , int a_BlockZ )
{
BroadcastThunderbolt ( a_BlockX , a_BlockY , a_BlockZ ) ;
}
void cWorld : : SetWeather ( eWeather a_NewWeather )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
// Do the plugins agree? Do they want a different weather?
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookWeatherChanging ( * this , a_NewWeather ) ;
// Set new period for the selected weather:
switch ( a_NewWeather )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
case eWeather_Sunny : m_WeatherInterval = 14400 + ( m_TickRand . randInt ( ) % 4800 ) ; break ; // 12 - 16 minutes
case eWeather_Rain : m_WeatherInterval = 9600 + ( m_TickRand . randInt ( ) % 7200 ) ; break ; // 8 - 14 minutes
case eWeather_ThunderStorm : m_WeatherInterval = 2400 + ( m_TickRand . randInt ( ) % 4800 ) ; break ; // 2 - 6 minutes
2012-08-25 17:46:18 -04:00
default :
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
LOGWARNING ( " Requested unknown weather %d, setting sunny for a minute instead. " , a_NewWeather ) ;
a_NewWeather = eWeather_Sunny ;
m_WeatherInterval = 1200 ;
2012-08-25 17:46:18 -04:00
break ;
2012-06-14 09:06:06 -04:00
}
2013-02-13 14:22:08 -05:00
} // switch (NewWeather)
m_Weather = a_NewWeather ;
BroadcastWeather ( m_Weather ) ;
// Let the plugins know about the change:
cPluginManager : : Get ( ) - > CallHookWeatherChanged ( * this ) ;
2012-06-14 09:06:06 -04:00
}
2013-02-13 14:22:08 -05:00
void cWorld : : ChangeWeather ( void )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
// In the next tick the weather will be changed
m_WeatherInterval = 0 ;
2012-06-14 09:06:06 -04:00
}
2012-08-25 17:46:18 -04:00
2012-06-14 09:06:06 -04:00
void cWorld : : SetNextBlockTick ( int a_BlockX , int a_BlockY , int a_BlockZ )
{
return m_ChunkMap - > SetNextBlockTick ( a_BlockX , a_BlockY , a_BlockZ ) ;
}
2012-08-25 17:46:18 -04:00
2012-06-14 09:06:06 -04:00
void cWorld : : InitializeSpawn ( void )
{
int ChunkX = 0 , ChunkY = 0 , ChunkZ = 0 ;
2013-03-03 14:05:11 -05:00
BlockToChunk ( ( int ) m_SpawnX , ( int ) m_SpawnY , ( int ) m_SpawnZ , ChunkX , ChunkY , ChunkZ ) ;
2012-06-14 09:06:06 -04:00
// For the debugging builds, don't make the server build too much world upon start:
2012-11-16 17:06:12 -05:00
# if defined(_DEBUG) || defined(ANDROID_NDK)
2012-06-14 09:06:06 -04:00
int ViewDist = 9 ;
# else
int ViewDist = 20 ; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is
# endif // _DEBUG
LOG ( " Preparing spawn area in world \" %s \" ... " , m_WorldName . c_str ( ) ) ;
for ( int x = 0 ; x < ViewDist ; x + + )
{
for ( int z = 0 ; z < ViewDist ; z + + )
{
2013-03-03 14:05:11 -05:00
m_ChunkMap - > TouchChunk ( x + ChunkX - ( ViewDist - 1 ) / 2 , ZERO_CHUNK_Y , z + ChunkZ - ( ViewDist - 1 ) / 2 ) ; // Queue the chunk in the generator / loader
2012-06-14 09:06:06 -04:00
}
}
{
// Display progress during this process:
cWorldLoadProgress Progress ( this ) ;
// Wait for the loader to finish loading
m_Storage . WaitForQueuesEmpty ( ) ;
// Wait for the generator to finish generating
m_Generator . WaitForQueueEmpty ( ) ;
Progress . Stop ( ) ;
}
// Light all chunks that have been newly generated:
LOG ( " Lighting spawn area in world \" %s \" ... " , m_WorldName . c_str ( ) ) ;
for ( int x = 0 ; x < ViewDist ; x + + )
{
int ChX = x + ChunkX - ( ViewDist - 1 ) / 2 ;
for ( int z = 0 ; z < ViewDist ; z + + )
{
int ChZ = z + ChunkZ - ( ViewDist - 1 ) / 2 ;
if ( ! m_ChunkMap - > IsChunkLighted ( ChX , ChZ ) )
{
m_Lighting . QueueChunk ( ChX , ChZ ) ; // Queue the chunk in the lighting thread
}
} // for z
} // for x
{
cWorldLightingProgress Progress ( & m_Lighting ) ;
m_Lighting . WaitForQueueEmpty ( ) ;
Progress . Stop ( ) ;
}
// TODO: Better spawn detection - move spawn out of the water if it isn't set in the INI already
2013-03-03 14:05:11 -05:00
m_SpawnY = ( double ) GetHeight ( ( int ) m_SpawnX , ( int ) m_SpawnZ ) + 1.6f ; // +1.6f eye height
2013-08-04 10:08:09 -04:00
# ifdef TEST_LINEBLOCKTRACER
// DEBUG: Test out the cLineBlockTracer class by tracing a few lines:
class cTracerCallbacks :
public cBlockTracer : : cCallbacks
{
virtual bool OnNextBlock ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta ) override
{
LOGD ( " Block {%d, %d, %d}: %d:%d (%s) " ,
a_BlockX , a_BlockY , a_BlockZ , a_BlockType , a_BlockMeta ,
ItemToString ( cItem ( a_BlockType , 1 , a_BlockMeta ) ) . c_str ( )
) ;
return false ;
}
virtual bool OnNextBlockNoData ( int a_BlockX , int a_BlockY , int a_BlockZ ) override
{
LOGD ( " Block {%d, %d, %d}: no data available " ,
a_BlockX , a_BlockY , a_BlockZ
) ;
return false ;
}
virtual bool OnOutOfWorld ( double a_BlockX , double a_BlockY , double a_BlockZ ) override
{
LOGD ( " Out of world at {%f, %f, %f} " , a_BlockX , a_BlockY , a_BlockZ ) ;
return false ;
}
virtual bool OnIntoWorld ( double a_BlockX , double a_BlockY , double a_BlockZ ) override
{
LOGD ( " Into world at {%f, %f, %f} " , a_BlockX , a_BlockY , a_BlockZ ) ;
return false ;
}
virtual void OnNoMoreHits ( void ) override
{
LOGD ( " No more hits " ) ;
}
} Callbacks ;
LOGD ( " Spawn is at {%f, %f, %f} " , m_SpawnX , m_SpawnY , m_SpawnZ ) ;
LOGD ( " Tracing a line along +X: " ) ;
cLineBlockTracer : : Trace ( * this , Callbacks , m_SpawnX - 10 , m_SpawnY , m_SpawnZ , m_SpawnX + 10 , m_SpawnY , m_SpawnZ ) ;
LOGD ( " Tracing a line along -Z: " ) ;
cLineBlockTracer : : Trace ( * this , Callbacks , m_SpawnX , m_SpawnY , m_SpawnZ + 10 , m_SpawnX , m_SpawnY , m_SpawnZ - 10 ) ;
LOGD ( " Tracing a line along -Y, out of world: " ) ;
cLineBlockTracer : : Trace ( * this , Callbacks , m_SpawnX , 260 , m_SpawnZ , m_SpawnX , - 5 , m_SpawnZ ) ;
LOGD ( " Tracing a line along XY: " ) ;
cLineBlockTracer : : Trace ( * this , Callbacks , m_SpawnX - 10 , m_SpawnY - 10 , m_SpawnZ , m_SpawnX + 10 , m_SpawnY + 10 , m_SpawnZ ) ;
LOGD ( " Tracing a line in generic direction: " ) ;
cLineBlockTracer : : Trace ( * this , Callbacks , m_SpawnX - 15 , m_SpawnY - 5 , m_SpawnZ + 7.5 , m_SpawnX + 13 , m_SpawnY - 10 , m_SpawnZ + 8.5 ) ;
LOGD ( " Tracing tests done " ) ;
# endif // TEST_LINEBLOCKTRACER
2012-06-14 09:06:06 -04:00
}
2013-08-11 14:16:41 -04:00
void cWorld : : Start ( void )
2012-07-15 16:07:38 -04:00
{
2013-08-11 14:16:41 -04:00
// TODO: Find a proper spawn location, based on the biomes (not in ocean)
m_SpawnX = ( double ) ( ( m_TickRand . randInt ( ) % 1000 ) - 500 ) ;
m_SpawnY = cChunkDef : : Height ;
m_SpawnZ = ( double ) ( ( m_TickRand . randInt ( ) % 1000 ) - 500 ) ;
m_GameMode = eGameMode_Creative ;
cIniFile IniFile ( m_IniFileName ) ;
if ( ! IniFile . ReadFile ( ) )
{
LOGWARNING ( " Cannot read world settings from \" %s \" , defaults will be used. " , m_IniFileName . c_str ( ) ) ;
}
AString Dimension = IniFile . GetValueSet ( " General " , " Dimension " , " Overworld " ) ;
m_Dimension = StringToDimension ( Dimension ) ;
switch ( m_Dimension )
{
case dimNether :
case dimOverworld :
case dimEnd :
{
break ;
}
default :
{
LOGWARNING ( " Unknown dimension: \" %s \" . Setting to Overworld " , Dimension . c_str ( ) ) ;
m_Dimension = dimOverworld ;
break ;
}
} // switch (m_Dimension)
m_SpawnX = IniFile . GetValueSetF ( " SpawnPosition " , " X " , m_SpawnX ) ;
m_SpawnY = IniFile . GetValueSetF ( " SpawnPosition " , " Y " , m_SpawnY ) ;
m_SpawnZ = IniFile . GetValueSetF ( " SpawnPosition " , " Z " , m_SpawnZ ) ;
m_StorageSchema = IniFile . GetValueSet ( " Storage " , " Schema " , m_StorageSchema ) ;
m_MaxCactusHeight = IniFile . GetValueSetI ( " Plants " , " MaxCactusHeight " , 3 ) ;
m_MaxSugarcaneHeight = IniFile . GetValueSetI ( " Plants " , " MaxSugarcaneHeight " , 3 ) ;
m_IsCactusBonemealable = IniFile . GetValueSetB ( " Plants " , " IsCactusBonemealable " , false ) ;
m_IsCarrotsBonemealable = IniFile . GetValueSetB ( " Plants " , " IsCarrotsBonemealable " , true ) ;
m_IsCropsBonemealable = IniFile . GetValueSetB ( " Plants " , " IsCropsBonemealable " , true ) ;
m_IsGrassBonemealable = IniFile . GetValueSetB ( " Plants " , " IsGrassBonemealable " , true ) ;
m_IsMelonStemBonemealable = IniFile . GetValueSetB ( " Plants " , " IsMelonStemBonemealable " , true ) ;
m_IsMelonBonemealable = IniFile . GetValueSetB ( " Plants " , " IsMelonBonemealable " , false ) ;
m_IsPotatoesBonemealable = IniFile . GetValueSetB ( " Plants " , " IsPotatoesBonemealable " , true ) ;
m_IsPumpkinStemBonemealable = IniFile . GetValueSetB ( " Plants " , " IsPumpkinStemBonemealable " , true ) ;
m_IsPumpkinBonemealable = IniFile . GetValueSetB ( " Plants " , " IsPumpkinBonemealable " , false ) ;
m_IsSaplingBonemealable = IniFile . GetValueSetB ( " Plants " , " IsSaplingBonemealable " , true ) ;
m_IsSugarcaneBonemealable = IniFile . GetValueSetB ( " Plants " , " IsSugarcaneBonemealable " , false ) ;
m_bEnabledPVP = IniFile . GetValueSetB ( " PVP " , " Enabled " , true ) ;
m_IsDeepSnowEnabled = IniFile . GetValueSetB ( " Physics " , " DeepSnow " , false ) ;
m_GameMode = ( eGameMode ) IniFile . GetValueSetI ( " GameMode " , " GameMode " , m_GameMode ) ;
2013-10-20 04:23:30 -04:00
// Load allowed mobs:
const char * DefaultMonsters = " " ;
switch ( m_Dimension )
{
case dimOverworld : DefaultMonsters = " bat, cavespider, chicken, cow, creeper, enderman, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie " ; break ;
case dimNether : DefaultMonsters = " blaze, ghast, magmacube, skeleton, zombie, zombiepigman " ; break ;
case dimEnd : DefaultMonsters = " enderman " ; break ;
default :
{
ASSERT ( ! " Unhandled world dimension " ) ;
DefaultMonsters = " wither " ;
break ;
}
}
2013-10-14 17:32:40 -04:00
m_bAnimals = IniFile . GetValueB ( " Monsters " , " AnimalsOn " , true ) ;
2013-10-20 04:23:30 -04:00
AString AllMonsters = IniFile . GetValueSet ( " Monsters " , " Types " , DefaultMonsters ) ;
AStringVector SplitList = StringSplitAndTrim ( AllMonsters , " , " ) ;
for ( AStringVector : : const_iterator itr = SplitList . begin ( ) , end = SplitList . end ( ) ; itr ! = end ; + + itr )
2013-08-11 14:16:41 -04:00
{
2013-10-20 07:25:56 -04:00
cMonster : : eType ToAdd = cMonster : : StringToMobType ( * itr ) ;
2013-09-07 21:30:09 -04:00
if ( ToAdd ! = cMonster : : mtInvalidType )
{
m_AllowedMobs . insert ( ToAdd ) ;
2013-10-20 07:25:56 -04:00
LOGD ( " Allowed mob: %s " , itr - > c_str ( ) ) ;
2013-09-07 21:30:09 -04:00
}
2013-10-20 04:23:30 -04:00
else
{
LOG ( " World \" %s \" : Unknown mob type: %s " , m_WorldName . c_str ( ) , itr - > c_str ( ) ) ;
}
}
2013-08-11 14:16:41 -04:00
m_ChunkMap = new cChunkMap ( this ) ;
m_LastSave = 0 ;
m_LastUnload = 0 ;
2013-09-10 16:02:46 -04:00
// preallocate some memory for ticking blocks so we don't need to allocate that often
2013-08-11 14:16:41 -04:00
m_BlockTickQueue . reserve ( 1000 ) ;
m_BlockTickQueueCopy . reserve ( 1000 ) ;
// Simulators:
m_SimulatorManager = new cSimulatorManager ( * this ) ;
m_WaterSimulator = InitializeFluidSimulator ( IniFile , " Water " , E_BLOCK_WATER , E_BLOCK_STATIONARY_WATER ) ;
m_LavaSimulator = InitializeFluidSimulator ( IniFile , " Lava " , E_BLOCK_LAVA , E_BLOCK_STATIONARY_LAVA ) ;
m_SandSimulator = new cSandSimulator ( * this , IniFile ) ;
m_FireSimulator = new cFireSimulator ( * this , IniFile ) ;
m_RedstoneSimulator = new cRedstoneSimulator ( * this ) ;
// Water and Lava simulators get registered in InitializeFluidSimulator()
m_SimulatorManager - > RegisterSimulator ( m_SandSimulator , 1 ) ;
m_SimulatorManager - > RegisterSimulator ( m_FireSimulator , 1 ) ;
m_SimulatorManager - > RegisterSimulator ( m_RedstoneSimulator , 1 ) ;
m_Lighting . Start ( this ) ;
m_Storage . Start ( this , m_StorageSchema ) ;
m_Generator . Start ( this , IniFile ) ;
m_ChunkSender . Start ( this ) ;
m_TickThread . Start ( ) ;
2013-09-07 20:47:02 -04:00
// Init of the spawn monster time (as they are supposed to have different spawn rate)
2013-10-20 04:23:30 -04:00
m_LastSpawnMonster . insert ( std : : map < cMonster : : eFamily , Int64 > : : value_type ( cMonster : : mfHostile , 0 ) ) ;
m_LastSpawnMonster . insert ( std : : map < cMonster : : eFamily , Int64 > : : value_type ( cMonster : : mfPassive , 0 ) ) ;
m_LastSpawnMonster . insert ( std : : map < cMonster : : eFamily , Int64 > : : value_type ( cMonster : : mfAmbient , 0 ) ) ;
m_LastSpawnMonster . insert ( std : : map < cMonster : : eFamily , Int64 > : : value_type ( cMonster : : mfWater , 0 ) ) ;
2013-09-07 20:47:02 -04:00
2013-08-11 14:16:41 -04:00
// Save any changes that the defaults may have done to the ini file:
if ( ! IniFile . WriteFile ( ) )
{
LOGWARNING ( " Could not write world config to %s " , m_IniFileName . c_str ( ) ) ;
}
}
void cWorld : : Stop ( void )
2012-07-15 16:07:38 -04:00
{
2013-08-14 13:56:29 -04:00
// Delete the clients that have been in this world:
{
cCSLock Lock ( m_CSClients ) ;
for ( cClientHandleList : : iterator itr = m_Clients . begin ( ) ; itr ! = m_Clients . end ( ) ; + + itr )
{
( * itr ) - > Destroy ( ) ;
delete * itr ;
} // for itr - m_Clients[]
m_Clients . clear ( ) ;
}
2013-08-11 14:16:41 -04:00
m_TickThread . Stop ( ) ;
m_Lighting . Stop ( ) ;
2012-07-15 16:07:38 -04:00
m_Generator . Stop ( ) ;
m_ChunkSender . Stop ( ) ;
2013-08-11 14:16:41 -04:00
m_Storage . Stop ( ) ;
2012-07-15 16:07:38 -04:00
}
2012-06-14 09:06:06 -04:00
void cWorld : : Tick ( float a_Dt )
{
2013-08-19 03:28:22 -04:00
// Call the plugins
cPluginManager : : Get ( ) - > CallHookWorldTick ( * this , a_Dt ) ;
2012-11-01 17:38:20 -04:00
// We need sub-tick precision here, that's why we store the time in seconds and calculate ticks off of it
m_WorldAgeSecs + = ( double ) a_Dt / 1000.0 ;
m_TimeOfDaySecs + = ( double ) a_Dt / 1000.0 ;
2012-06-14 09:06:06 -04:00
2012-11-01 17:38:20 -04:00
// Wrap time of day each 20 minutes (1200 seconds)
if ( m_TimeOfDaySecs > 1200.0 )
2012-06-14 09:06:06 -04:00
{
2012-11-01 17:38:20 -04:00
m_TimeOfDaySecs - = 1200.0 ;
2012-06-14 09:06:06 -04:00
}
2012-11-01 17:38:20 -04:00
2012-12-17 09:23:17 -05:00
m_WorldAge = ( Int64 ) ( m_WorldAgeSecs * 20.0 ) ;
m_TimeOfDay = ( Int64 ) ( m_TimeOfDaySecs * 20.0 ) ;
2012-11-01 17:38:20 -04:00
2013-08-12 02:35:13 -04:00
// Broadcast time update every 40 ticks (2 seconds)
2012-11-01 17:38:20 -04:00
if ( m_LastTimeUpdate < m_WorldAge - 40 )
2012-06-14 09:06:06 -04:00
{
2012-08-25 17:46:18 -04:00
BroadcastTimeUpdate ( ) ;
2012-11-01 17:38:20 -04:00
m_LastTimeUpdate = m_WorldAge ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
m_ChunkMap - > Tick ( a_Dt ) ;
2012-07-15 16:36:34 -04:00
2013-08-13 16:45:29 -04:00
TickClients ( a_Dt ) ;
2013-09-16 03:25:23 -04:00
TickQueuedBlocks ( ) ;
2013-08-11 15:05:44 -04:00
TickQueuedTasks ( ) ;
2012-06-14 09:06:06 -04:00
GetSimulatorManager ( ) - > Simulate ( a_Dt ) ;
TickWeather ( a_Dt ) ;
// Asynchronously set blocks:
sSetBlockList FastSetBlockQueueCopy ;
{
cCSLock Lock ( m_CSFastSetBlock ) ;
std : : swap ( FastSetBlockQueueCopy , m_FastSetBlockQueue ) ;
}
m_ChunkMap - > FastSetBlocks ( FastSetBlockQueueCopy ) ;
if ( ! FastSetBlockQueueCopy . empty ( ) )
{
// Some blocks failed, store them for next tick:
cCSLock Lock ( m_CSFastSetBlock ) ;
m_FastSetBlockQueue . splice ( m_FastSetBlockQueue . end ( ) , FastSetBlockQueueCopy ) ;
}
2012-11-01 17:38:20 -04:00
if ( m_WorldAge - m_LastSave > 60 * 5 * 20 ) // Save each 5 minutes
2012-06-14 09:06:06 -04:00
{
SaveAllChunks ( ) ;
}
2013-03-03 14:05:11 -05:00
if ( m_WorldAge - m_LastUnload > 10 * 20 ) // Unload every 10 seconds
2012-06-14 09:06:06 -04:00
{
UnloadUnusedChunks ( ) ;
}
2013-09-07 19:43:55 -04:00
TickMobs ( a_Dt ) ;
2012-06-14 09:06:06 -04:00
std : : vector < int > m_RSList_copy ( m_RSList ) ;
m_RSList . clear ( ) ;
std : : vector < int > : : const_iterator cii ; // FIXME - Please rename this variable, WTF is cii??? Use human readable variable names or common abbreviations (i, idx, itr, iter)
2012-10-06 16:04:58 -04:00
for ( cii = m_RSList_copy . begin ( ) ; cii ! = m_RSList_copy . end ( ) ; )
2012-06-14 09:06:06 -04:00
{
2012-10-06 16:04:58 -04:00
int tempX = * cii ; cii + + ;
int tempY = * cii ; cii + + ;
int tempZ = * cii ; cii + + ;
int state = * cii ; cii + + ;
2012-06-14 09:06:06 -04:00
2013-03-03 14:05:11 -05:00
if ( ( state = = 11111 ) & & ( ( int ) GetBlock ( tempX , tempY , tempZ ) = = E_BLOCK_REDSTONE_TORCH_OFF ) )
2012-06-14 09:06:06 -04:00
{
2013-03-03 14:05:11 -05:00
FastSetBlock ( tempX , tempY , tempZ , E_BLOCK_REDSTONE_TORCH_ON , ( int ) GetBlockMeta ( tempX , tempY , tempZ ) ) ;
2012-06-14 09:06:06 -04:00
}
2013-03-03 14:05:11 -05:00
else if ( ( state = = 00000 ) & & ( ( int ) GetBlock ( tempX , tempY , tempZ ) = = E_BLOCK_REDSTONE_TORCH_ON ) )
2012-06-14 09:06:06 -04:00
{
2013-03-03 14:05:11 -05:00
FastSetBlock ( tempX , tempY , tempZ , E_BLOCK_REDSTONE_TORCH_OFF , ( int ) GetBlockMeta ( tempX , tempY , tempZ ) ) ;
2012-06-14 09:06:06 -04:00
}
}
m_RSList_copy . erase ( m_RSList_copy . begin ( ) , m_RSList_copy . end ( ) ) ;
}
2013-02-13 14:22:08 -05:00
void cWorld : : TickWeather ( float a_Dt )
2012-06-14 09:06:06 -04:00
{
2013-06-24 12:50:32 -04:00
// There are no weather changes anywhere but in the Overworld:
if ( GetDimension ( ) ! = dimOverworld )
{
return ;
}
2013-02-13 14:22:08 -05:00
if ( m_WeatherInterval > 0 )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
// Not yet, wait for the weather period to end
m_WeatherInterval - - ;
2012-06-14 09:06:06 -04:00
}
2013-02-13 14:22:08 -05:00
else
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
// Change weather:
2012-06-14 09:06:06 -04:00
2013-02-13 14:22:08 -05:00
// Pick a new weather. Only reasonable transitions allowed:
eWeather NewWeather = m_Weather ;
switch ( m_Weather )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
case eWeather_Sunny : NewWeather = eWeather_Rain ; break ;
case eWeather_ThunderStorm : NewWeather = eWeather_Rain ; break ;
2012-06-14 09:06:06 -04:00
case eWeather_Rain :
2013-02-13 14:22:08 -05:00
{
// 1/8 chance of turning into a thunderstorm
NewWeather = ( ( m_TickRand . randInt ( ) % 256 ) < 32 ) ? eWeather_ThunderStorm : eWeather_Sunny ;
2012-06-14 09:06:06 -04:00
break ;
2013-02-13 14:22:08 -05:00
}
2012-06-14 09:06:06 -04:00
default :
2013-02-13 14:22:08 -05:00
{
LOGWARNING ( " Unknown current weather: %d. Setting sunny. " , m_Weather ) ;
ASSERT ( ! " Unknown weather " ) ;
NewWeather = eWeather_Sunny ;
}
2012-06-14 09:06:06 -04:00
}
2013-02-13 14:22:08 -05:00
SetWeather ( NewWeather ) ;
} // else (m_WeatherInterval > 0)
2012-06-14 09:06:06 -04:00
2013-02-13 14:22:08 -05:00
if ( m_Weather = = eWeather_ThunderStorm )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
// 0.5% chance per tick of thunderbolt
if ( m_TickRand . randInt ( ) % 199 = = 0 )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
CastThunderbolt ( 0 , 0 , 0 ) ; // TODO: find random possitions near players to cast thunderbolts.
2012-06-14 09:06:06 -04:00
}
}
}
2013-09-07 19:43:55 -04:00
void cWorld : : TickMobs ( float a_Dt )
2012-06-14 09:06:06 -04:00
{
2013-09-07 19:43:55 -04:00
if ( ! m_bAnimals )
2012-06-14 09:06:06 -04:00
{
return ;
}
2013-09-07 20:47:02 -04:00
// before every Mob action, we have to "counts" them depending on the distance to players, on their megatype ...
2013-09-07 16:19:56 -04:00
cMobCensus MobCensus ;
m_ChunkMap - > CollectMobCensus ( MobCensus ) ;
2013-09-07 20:47:02 -04:00
for ( cMobFamilyCollecter : : tMobFamilyList : : const_iterator itr = cMobFamilyCollecter : : m_AllFamilies ( ) . begin ( ) ; itr ! = cMobFamilyCollecter : : m_AllFamilies ( ) . end ( ) ; itr + + )
2012-06-14 09:06:06 -04:00
{
2013-09-07 20:47:02 -04:00
cMobCensus : : tMobSpawnRate : : const_iterator spawnrate = cMobCensus : : m_SpawnRate ( ) . find ( * itr ) ;
// hostile mobs are spawned more often
2013-10-18 14:02:53 -04:00
if ( ( spawnrate ! = cMobCensus : : m_SpawnRate ( ) . end ( ) ) & & ( m_LastSpawnMonster [ * itr ] < m_WorldAge - spawnrate - > second ) )
2013-06-25 02:36:59 -04:00
{
2013-09-07 20:47:02 -04:00
m_LastSpawnMonster [ * itr ] = m_WorldAge ;
// each megatype of mob has it's own cap
2013-10-18 14:02:53 -04:00
if ( ! ( MobCensus . IsCapped ( * itr ) ) )
2013-08-16 04:48:19 -04:00
{
2013-09-07 20:47:02 -04:00
if ( m_bAnimals )
2013-08-16 04:48:19 -04:00
{
2013-09-07 20:47:02 -04:00
cMobSpawner Spawner ( * itr , m_AllowedMobs ) ;
if ( Spawner . CanSpawnSomething ( ) )
{
m_ChunkMap - > SpawnMobs ( Spawner ) ;
// do the spawn
for ( cMobSpawner : : tSpawnedContainer : : const_iterator itr2 = Spawner . getSpawned ( ) . begin ( ) ; itr2 ! = Spawner . getSpawned ( ) . end ( ) ; itr2 + + )
{
SpawnMobFinalize ( * itr2 ) ;
}
}
2013-08-16 04:48:19 -04:00
}
2013-09-07 20:47:02 -04:00
}
}
}
2012-06-14 09:06:06 -04:00
2013-09-07 20:47:02 -04:00
// move close mobs
2013-10-18 14:02:53 -04:00
cMobProximityCounter : : sIterablePair allCloseEnoughToMoveMobs = MobCensus . GetProximityCounter ( ) . getMobWithinThosesDistances ( - 1 , 64 * 16 ) ; // MG TODO : deal with this magic number (the 16 is the size of a block)
2013-09-07 20:47:02 -04:00
for ( cMobProximityCounter : : tDistanceToMonster : : const_iterator itr = allCloseEnoughToMoveMobs . m_Begin ; itr ! = allCloseEnoughToMoveMobs . m_End ; itr + + )
2012-06-14 09:06:06 -04:00
{
2013-10-18 14:02:53 -04:00
itr - > second . m_Monster . Tick ( a_Dt , itr - > second . m_Chunk ) ;
2013-09-07 20:47:02 -04:00
}
2012-06-14 09:06:06 -04:00
2013-09-07 20:47:02 -04:00
// remove too far mobs
2013-10-18 14:02:53 -04:00
cMobProximityCounter : : sIterablePair allTooFarMobs = MobCensus . GetProximityCounter ( ) . getMobWithinThosesDistances ( 128 * 16 , - 1 ) ; // MG TODO : deal with this magic number (the 16 is the size of a block)
2013-09-07 20:47:02 -04:00
for ( cMobProximityCounter : : tDistanceToMonster : : const_iterator itr = allTooFarMobs . m_Begin ; itr ! = allTooFarMobs . m_End ; itr + + )
2012-06-14 09:06:06 -04:00
{
2013-09-07 20:47:02 -04:00
itr - > second . m_Monster . Destroy ( true ) ;
2012-06-14 09:06:06 -04:00
}
}
2013-08-11 15:05:44 -04:00
void cWorld : : TickQueuedTasks ( void )
{
// Make a copy of the tasks to avoid deadlocks on accessing m_Tasks
cTasks Tasks ;
{
cCSLock Lock ( m_CSTasks ) ;
std : : swap ( Tasks , m_Tasks ) ;
}
// Execute and delete each task:
2013-10-12 16:24:59 -04:00
for ( cTasks : : iterator itr = Tasks . begin ( ) , end = Tasks . end ( ) ; itr ! = end ; + + itr )
2013-08-11 15:05:44 -04:00
{
( * itr ) - > Run ( * this ) ;
delete * itr ;
} // for itr - m_Tasks[]
}
2013-08-13 16:45:29 -04:00
void cWorld : : TickClients ( float a_Dt )
{
cClientHandleList RemoveClients ;
{
cCSLock Lock ( m_CSClients ) ;
2013-08-14 07:43:55 -04:00
// Remove clients scheduled for removal:
for ( cClientHandleList : : iterator itr = m_ClientsToRemove . begin ( ) , end = m_ClientsToRemove . end ( ) ; itr ! = end ; + + itr )
{
m_Clients . remove ( * itr ) ;
} // for itr - m_ClientsToRemove[]
m_ClientsToRemove . clear ( ) ;
// Add clients scheduled for adding:
for ( cClientHandleList : : iterator itr = m_ClientsToAdd . begin ( ) , end = m_ClientsToAdd . end ( ) ; itr ! = end ; + + itr )
{
if ( std : : find ( m_Clients . begin ( ) , m_Clients . end ( ) , * itr ) ! = m_Clients . end ( ) )
{
ASSERT ( ! " Adding a client that is already in the clientlist " ) ;
continue ;
}
m_Clients . push_back ( * itr ) ;
} // for itr - m_ClientsToRemove[]
m_ClientsToAdd . clear ( ) ;
2013-08-13 16:45:29 -04:00
// Tick the clients, take out those that have been destroyed into RemoveClients
for ( cClientHandleList : : iterator itr = m_Clients . begin ( ) ; itr ! = m_Clients . end ( ) ; )
{
if ( ( * itr ) - > IsDestroyed ( ) )
{
// Remove the client later, when CS is not held, to avoid deadlock
RemoveClients . push_back ( * itr ) ;
itr = m_Clients . erase ( itr ) ;
continue ;
}
( * itr ) - > Tick ( a_Dt ) ;
+ + itr ;
} // for itr - m_Clients[]
}
// Delete the clients that have been destroyed
for ( cClientHandleList : : iterator itr = RemoveClients . begin ( ) ; itr ! = RemoveClients . end ( ) ; + + itr )
{
delete * itr ;
} // for itr - RemoveClients[]
}
2013-02-28 02:42:45 -05:00
void cWorld : : WakeUpSimulators ( int a_BlockX , int a_BlockY , int a_BlockZ )
{
return m_ChunkMap - > WakeUpSimulators ( a_BlockX , a_BlockY , a_BlockZ ) ;
}
2013-06-21 16:47:58 -04:00
/// Wakes up the simulators for the specified area of blocks
void cWorld : : WakeUpSimulatorsInArea ( int a_MinBlockX , int a_MaxBlockX , int a_MinBlockY , int a_MaxBlockY , int a_MinBlockZ , int a_MaxBlockZ )
{
return m_ChunkMap - > WakeUpSimulatorsInArea ( a_MinBlockX , a_MaxBlockX , a_MinBlockY , a_MaxBlockY , a_MinBlockZ , a_MaxBlockZ ) ;
}
2012-06-17 15:58:39 -04:00
bool cWorld : : ForEachChestInChunk ( int a_ChunkX , int a_ChunkZ , cChestCallback & a_Callback )
{
return m_ChunkMap - > ForEachChestInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2012-12-26 12:16:33 -05:00
bool cWorld : : ForEachDispenserInChunk ( int a_ChunkX , int a_ChunkZ , cDispenserCallback & a_Callback )
{
return m_ChunkMap - > ForEachDispenserInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2013-05-26 10:39:04 -04:00
bool cWorld : : ForEachDropperInChunk ( int a_ChunkX , int a_ChunkZ , cDropperCallback & a_Callback )
{
return m_ChunkMap - > ForEachDropperInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
bool cWorld : : ForEachDropSpenserInChunk ( int a_ChunkX , int a_ChunkZ , cDropSpenserCallback & a_Callback )
{
return m_ChunkMap - > ForEachDropSpenserInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2012-06-17 15:58:39 -04:00
bool cWorld : : ForEachFurnaceInChunk ( int a_ChunkX , int a_ChunkZ , cFurnaceCallback & a_Callback )
{
return m_ChunkMap - > ForEachFurnaceInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2013-09-07 12:12:22 -04:00
void cWorld : : DoExplosionAt ( double a_ExplosionSize , double a_BlockX , double a_BlockY , double a_BlockZ , bool a_CanCauseFire , eExplosionSource a_Source , void * a_SourceData )
2013-04-17 22:42:45 -04:00
{
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
if ( cPluginManager : : Get ( ) - > CallHookExploding ( * this , a_ExplosionSize , a_CanCauseFire , a_BlockX , a_BlockY , a_BlockZ , a_Source , a_SourceData ) | | ( a_ExplosionSize < = 0 ) )
{
return ;
}
2013-05-04 02:25:58 -04:00
// TODO: Add damage to entities, add support for pickups, and implement block hardiness
2013-06-18 04:24:34 -04:00
Vector3d explosion_pos = Vector3d ( a_BlockX , a_BlockY , a_BlockZ ) ;
2013-06-18 15:09:51 -04:00
cVector3iArray BlocksAffected ;
2013-09-07 12:12:22 -04:00
m_ChunkMap - > DoExplosionAt ( a_ExplosionSize , a_BlockX , a_BlockY , a_BlockZ , BlocksAffected ) ;
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
BroadcastSoundEffect ( " random.explode " , ( int ) floor ( a_BlockX * 8 ) , ( int ) floor ( a_BlockY * 8 ) , ( int ) floor ( a_BlockZ * 8 ) , 1.0f , 0.6f ) ;
2013-04-17 22:42:45 -04:00
{
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
cClientHandle * ch = ( * itr ) - > GetClientHandle ( ) ;
if ( ( ch = = NULL ) | | ! ch - > IsLoggedIn ( ) | | ch - > IsDestroyed ( ) )
{
continue ;
}
Vector3d distance_explosion = ( * itr ) - > GetPosition ( ) - explosion_pos ;
if ( distance_explosion . SqrLength ( ) < 4096.0 )
{
2013-04-19 04:21:39 -04:00
double real_distance = std : : max ( 0.004 , sqrt ( distance_explosion . SqrLength ( ) ) ) ;
2013-04-17 22:42:45 -04:00
double power = a_ExplosionSize / real_distance ;
if ( power < = 1 )
2013-04-19 04:21:39 -04:00
{
2013-04-17 22:42:45 -04:00
power = 0 ;
2013-04-19 04:21:39 -04:00
}
2013-04-17 22:42:45 -04:00
distance_explosion . Normalize ( ) ;
distance_explosion * = power ;
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
ch - > SendExplosion ( a_BlockX , a_BlockY , a_BlockZ , ( float ) a_ExplosionSize , BlocksAffected , distance_explosion ) ;
2013-04-17 22:42:45 -04:00
}
}
}
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
cPluginManager : : Get ( ) - > CallHookExploded ( * this , a_ExplosionSize , a_CanCauseFire , a_BlockX , a_BlockY , a_BlockZ , a_Source , a_SourceData ) ;
2013-04-17 22:42:45 -04:00
}
2012-06-17 15:58:39 -04:00
bool cWorld : : DoWithChestAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cChestCallback & a_Callback )
{
return m_ChunkMap - > DoWithChestAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2012-12-26 12:16:33 -05:00
bool cWorld : : DoWithDispenserAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cDispenserCallback & a_Callback )
{
return m_ChunkMap - > DoWithDispenserAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2013-05-26 10:39:04 -04:00
bool cWorld : : DoWithDropperAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cDropperCallback & a_Callback )
{
return m_ChunkMap - > DoWithDropperAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
bool cWorld : : DoWithDropSpenserAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cDropSpenserCallback & a_Callback )
{
return m_ChunkMap - > DoWithDropSpenserAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2012-12-26 12:16:33 -05:00
2012-06-17 15:58:39 -04:00
bool cWorld : : DoWithFurnaceAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cFurnaceCallback & a_Callback )
{
return m_ChunkMap - > DoWithFurnaceAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
bool cWorld : : GetSignLines ( int a_BlockX , int a_BlockY , int a_BlockZ , AString & a_Line1 , AString & a_Line2 , AString & a_Line3 , AString & a_Line4 )
{
return m_ChunkMap - > GetSignLines ( a_BlockX , a_BlockY , a_BlockZ , a_Line1 , a_Line2 , a_Line3 , a_Line4 ) ;
}
2013-08-03 14:26:50 -04:00
bool cWorld : : DoWithChunk ( int a_ChunkX , int a_ChunkZ , cChunkCallback & a_Callback )
{
return m_ChunkMap - > DoWithChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2013-03-03 14:05:11 -05:00
void cWorld : : GrowTree ( int a_X , int a_Y , int a_Z )
2012-06-14 09:06:06 -04:00
{
if ( GetBlock ( a_X , a_Y , a_Z ) = = E_BLOCK_SAPLING )
{
// There is a sapling here, grow a tree according to its type:
GrowTreeFromSapling ( a_X , a_Y , a_Z , GetBlockMeta ( a_X , a_Y , a_Z ) ) ;
}
else
{
// There is nothing here, grow a tree based on the current biome here:
GrowTreeByBiome ( a_X , a_Y , a_Z ) ;
}
}
2013-09-15 16:11:02 -04:00
void cWorld : : GrowTreeFromSapling ( int a_X , int a_Y , int a_Z , NIBBLETYPE a_SaplingMeta )
2012-06-14 09:06:06 -04:00
{
cNoise Noise ( m_Generator . GetSeed ( ) ) ;
2012-07-15 09:33:43 -04:00
sSetBlockVector Logs , Other ;
2012-06-14 09:06:06 -04:00
switch ( a_SaplingMeta & 0x07 )
{
2012-11-01 17:38:20 -04:00
case E_META_SAPLING_APPLE : GetAppleTreeImage ( a_X , a_Y , a_Z , Noise , ( int ) ( m_WorldAge & 0xffffffff ) , Logs , Other ) ; break ;
case E_META_SAPLING_BIRCH : GetBirchTreeImage ( a_X , a_Y , a_Z , Noise , ( int ) ( m_WorldAge & 0xffffffff ) , Logs , Other ) ; break ;
case E_META_SAPLING_CONIFER : GetConiferTreeImage ( a_X , a_Y , a_Z , Noise , ( int ) ( m_WorldAge & 0xffffffff ) , Logs , Other ) ; break ;
case E_META_SAPLING_JUNGLE : GetJungleTreeImage ( a_X , a_Y , a_Z , Noise , ( int ) ( m_WorldAge & 0xffffffff ) , Logs , Other ) ; break ;
2012-06-14 09:06:06 -04:00
}
2012-07-15 09:33:43 -04:00
Other . insert ( Other . begin ( ) , Logs . begin ( ) , Logs . end ( ) ) ;
Logs . clear ( ) ;
2012-10-03 15:01:34 -04:00
GrowTreeImage ( Other ) ;
2012-06-14 09:06:06 -04:00
}
void cWorld : : GrowTreeByBiome ( int a_X , int a_Y , int a_Z )
{
cNoise Noise ( m_Generator . GetSeed ( ) ) ;
2012-07-15 09:33:43 -04:00
sSetBlockVector Logs , Other ;
2012-11-01 17:38:20 -04:00
GetTreeImageByBiome ( a_X , a_Y , a_Z , Noise , ( int ) ( m_WorldAge & 0xffffffff ) , ( EMCSBiome ) GetBiomeAt ( a_X , a_Z ) , Logs , Other ) ;
2012-07-15 09:33:43 -04:00
Other . insert ( Other . begin ( ) , Logs . begin ( ) , Logs . end ( ) ) ;
Logs . clear ( ) ;
GrowTreeImage ( Other ) ;
2012-06-14 09:06:06 -04:00
}
void cWorld : : GrowTreeImage ( const sSetBlockVector & a_Blocks )
{
// Check that the tree has place to grow
// Make a copy of the log blocks:
sSetBlockVector b2 ;
for ( sSetBlockVector : : const_iterator itr = a_Blocks . begin ( ) ; itr ! = a_Blocks . end ( ) ; + + itr )
{
if ( itr - > BlockType = = E_BLOCK_LOG )
{
b2 . push_back ( * itr ) ;
}
} // for itr - a_Blocks[]
// Query blocktypes and metas at those log blocks:
if ( ! GetBlocks ( b2 , false ) )
{
return ;
}
// Check that at each log's coord there's an block allowed to be overwritten:
for ( sSetBlockVector : : const_iterator itr = b2 . begin ( ) ; itr ! = b2 . end ( ) ; + + itr )
{
switch ( itr - > BlockType )
{
CASE_TREE_ALLOWED_BLOCKS :
{
break ;
}
default :
{
return ;
}
}
} // for itr - b2[]
// All ok, replace blocks with the tree image:
m_ChunkMap - > ReplaceTreeBlocks ( a_Blocks ) ;
}
2012-10-03 04:52:11 -04:00
bool cWorld : : GrowRipePlant ( int a_BlockX , int a_BlockY , int a_BlockZ , bool a_IsByBonemeal )
2012-06-14 09:06:06 -04:00
{
BLOCKTYPE BlockType ;
NIBBLETYPE BlockMeta ;
GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , BlockType , BlockMeta ) ;
switch ( BlockType )
{
2013-04-05 09:45:00 -04:00
case E_BLOCK_CARROTS :
{
if ( a_IsByBonemeal & & ! m_IsCarrotsBonemealable )
{
return false ;
}
if ( BlockMeta < 7 )
{
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , 7 ) ;
}
return true ;
}
2012-06-14 09:06:06 -04:00
case E_BLOCK_CROPS :
{
2013-04-05 09:45:00 -04:00
if ( a_IsByBonemeal & & ! m_IsCropsBonemealable )
2012-06-14 09:06:06 -04:00
{
return false ;
}
if ( BlockMeta < 7 )
{
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , 7 ) ;
}
return true ;
}
case E_BLOCK_MELON_STEM :
{
if ( BlockMeta < 7 )
{
if ( a_IsByBonemeal & & ! m_IsMelonStemBonemealable )
{
return false ;
}
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , 7 ) ;
}
else
{
if ( a_IsByBonemeal & & ! m_IsMelonBonemealable )
{
return false ;
}
GrowMelonPumpkin ( a_BlockX , a_BlockY , a_BlockZ , BlockType ) ;
}
return true ;
}
2013-04-05 09:45:00 -04:00
case E_BLOCK_POTATOES :
{
if ( a_IsByBonemeal & & ! m_IsPotatoesBonemealable )
{
return false ;
}
if ( BlockMeta < 7 )
{
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , 7 ) ;
}
return true ;
}
2012-06-14 09:06:06 -04:00
case E_BLOCK_PUMPKIN_STEM :
{
if ( BlockMeta < 7 )
{
if ( a_IsByBonemeal & & ! m_IsPumpkinStemBonemealable )
{
return false ;
}
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , 7 ) ;
}
else
{
if ( a_IsByBonemeal & & ! m_IsPumpkinBonemealable )
{
return false ;
}
GrowMelonPumpkin ( a_BlockX , a_BlockY , a_BlockZ , BlockType ) ;
}
return true ;
}
case E_BLOCK_SAPLING :
{
if ( a_IsByBonemeal & & ! m_IsSaplingBonemealable )
{
return false ;
}
GrowTreeFromSapling ( a_BlockX , a_BlockY , a_BlockZ , BlockMeta ) ;
return true ;
}
case E_BLOCK_GRASS :
{
if ( a_IsByBonemeal & & ! m_IsGrassBonemealable )
{
return false ;
}
MTRand r1 ;
for ( int i = 0 ; i < 60 ; i + + )
{
int OfsX = ( r1 . randInt ( 3 ) + r1 . randInt ( 3 ) + r1 . randInt ( 3 ) + r1 . randInt ( 3 ) ) / 2 - 3 ;
int OfsY = r1 . randInt ( 3 ) + r1 . randInt ( 3 ) - 3 ;
int OfsZ = ( r1 . randInt ( 3 ) + r1 . randInt ( 3 ) + r1 . randInt ( 3 ) + r1 . randInt ( 3 ) ) / 2 - 3 ;
BLOCKTYPE Ground = GetBlock ( a_BlockX + OfsX , a_BlockY + OfsY , a_BlockZ + OfsZ ) ;
if ( Ground ! = E_BLOCK_GRASS )
{
continue ;
}
BLOCKTYPE Above = GetBlock ( a_BlockX + OfsX , a_BlockY + OfsY + 1 , a_BlockZ + OfsZ ) ;
if ( Above ! = E_BLOCK_AIR )
{
continue ;
}
BLOCKTYPE SpawnType ;
NIBBLETYPE SpawnMeta = 0 ;
switch ( r1 . randInt ( 10 ) )
{
case 0 : SpawnType = E_BLOCK_YELLOW_FLOWER ; break ;
case 1 : SpawnType = E_BLOCK_RED_ROSE ; break ;
default :
{
SpawnType = E_BLOCK_TALL_GRASS ;
SpawnMeta = E_META_TALL_GRASS_GRASS ;
break ;
}
2012-10-03 04:52:11 -04:00
} // switch (random spawn block type)
2012-06-14 09:06:06 -04:00
FastSetBlock ( a_BlockX + OfsX , a_BlockY + OfsY + 1 , a_BlockZ + OfsZ , SpawnType , SpawnMeta ) ;
} // for i - 50 times
return true ;
}
case E_BLOCK_SUGARCANE :
{
if ( a_IsByBonemeal & & ! m_IsSugarcaneBonemealable )
{
return false ;
}
m_ChunkMap - > GrowSugarcane ( a_BlockX , a_BlockY , a_BlockZ , m_MaxSugarcaneHeight ) ;
return true ;
}
case E_BLOCK_CACTUS :
{
if ( a_IsByBonemeal & & ! m_IsCactusBonemealable )
{
return false ;
}
m_ChunkMap - > GrowCactus ( a_BlockX , a_BlockY , a_BlockZ , m_MaxCactusHeight ) ;
return true ;
}
} // switch (BlockType)
return false ;
}
2012-10-03 04:52:11 -04:00
void cWorld : : GrowCactus ( int a_BlockX , int a_BlockY , int a_BlockZ , int a_NumBlocksToGrow )
{
m_ChunkMap - > GrowCactus ( a_BlockX , a_BlockY , a_BlockZ , a_NumBlocksToGrow ) ;
}
2013-09-15 16:11:02 -04:00
void cWorld : : GrowMelonPumpkin ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType )
2012-06-14 09:06:06 -04:00
{
MTRand Rand ;
m_ChunkMap - > GrowMelonPumpkin ( a_BlockX , a_BlockY , a_BlockZ , a_BlockType , Rand ) ;
}
2012-10-03 04:52:11 -04:00
void cWorld : : GrowSugarcane ( int a_BlockX , int a_BlockY , int a_BlockZ , int a_NumBlocksToGrow )
{
m_ChunkMap - > GrowSugarcane ( a_BlockX , a_BlockY , a_BlockZ , a_NumBlocksToGrow ) ;
}
2012-06-14 09:06:06 -04:00
int cWorld : : GetBiomeAt ( int a_BlockX , int a_BlockZ )
{
return m_ChunkMap - > GetBiomeAt ( a_BlockX , a_BlockZ ) ;
}
2012-10-13 04:56:12 -04:00
void cWorld : : SetBlock ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta )
2012-06-14 09:06:06 -04:00
{
2013-01-11 23:46:01 -05:00
if ( a_BlockType = = E_BLOCK_AIR )
2012-07-15 16:36:34 -04:00
{
2012-10-13 04:56:12 -04:00
BlockHandler ( GetBlock ( a_BlockX , a_BlockY , a_BlockZ ) ) - > OnDestroyed ( this , a_BlockX , a_BlockY , a_BlockZ ) ;
2012-07-15 16:36:34 -04:00
}
2012-10-13 04:56:12 -04:00
m_ChunkMap - > SetBlock ( a_BlockX , a_BlockY , a_BlockZ , a_BlockType , a_BlockMeta ) ;
2012-06-14 09:06:06 -04:00
2013-01-11 23:46:01 -05:00
BlockHandler ( a_BlockType ) - > OnPlaced ( this , a_BlockX , a_BlockY , a_BlockZ , a_BlockType , a_BlockMeta ) ;
2012-06-14 09:06:06 -04:00
}
2013-03-03 14:05:11 -05:00
void cWorld : : FastSetBlock ( int a_X , int a_Y , int a_Z , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta )
2012-06-14 09:06:06 -04:00
{
cCSLock Lock ( m_CSFastSetBlock ) ;
m_FastSetBlockQueue . push_back ( sSetBlock ( a_X , a_Y , a_Z , a_BlockType , a_BlockMeta ) ) ;
}
2013-08-18 16:44:22 -04:00
void cWorld : : QueueSetBlock ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , int a_TickDelay )
{
m_ChunkMap - > QueueSetBlock ( a_BlockX , a_BlockY , a_BlockZ , a_BlockType , a_BlockMeta , GetWorldAge ( ) + a_TickDelay ) ;
}
2012-08-03 07:53:11 -04:00
BLOCKTYPE cWorld : : GetBlock ( int a_X , int a_Y , int a_Z )
2012-06-14 09:06:06 -04:00
{
// First check if it isn't queued in the m_FastSetBlockQueue:
{
int X = a_X , Y = a_Y , Z = a_Z ;
int ChunkX , ChunkY , ChunkZ ;
AbsoluteToRelative ( X , Y , Z , ChunkX , ChunkY , ChunkZ ) ;
cCSLock Lock ( m_CSFastSetBlock ) ;
for ( sSetBlockList : : iterator itr = m_FastSetBlockQueue . begin ( ) ; itr ! = m_FastSetBlockQueue . end ( ) ; + + itr )
{
if ( ( itr - > x = = X ) & & ( itr - > y = = Y ) & & ( itr - > z = = Z ) & & ( itr - > ChunkX = = ChunkX ) & & ( itr - > ChunkZ = = ChunkZ ) )
{
return itr - > BlockType ;
}
} // for itr - m_FastSetBlockQueue[]
}
return m_ChunkMap - > GetBlock ( a_X , a_Y , a_Z ) ;
}
2013-03-03 14:05:11 -05:00
NIBBLETYPE cWorld : : GetBlockMeta ( int a_X , int a_Y , int a_Z )
2012-06-14 09:06:06 -04:00
{
// First check if it isn't queued in the m_FastSetBlockQueue:
{
cCSLock Lock ( m_CSFastSetBlock ) ;
for ( sSetBlockList : : iterator itr = m_FastSetBlockQueue . begin ( ) ; itr ! = m_FastSetBlockQueue . end ( ) ; + + itr )
{
if ( ( itr - > x = = a_X ) & & ( itr - > y = = a_Y ) & & ( itr - > y = = a_Y ) )
{
return itr - > BlockMeta ;
}
} // for itr - m_FastSetBlockQueue[]
}
return m_ChunkMap - > GetBlockMeta ( a_X , a_Y , a_Z ) ;
}
2013-03-03 14:05:11 -05:00
void cWorld : : SetBlockMeta ( int a_X , int a_Y , int a_Z , NIBBLETYPE a_MetaData )
2012-06-14 09:06:06 -04:00
{
m_ChunkMap - > SetBlockMeta ( a_X , a_Y , a_Z , a_MetaData ) ;
}
2013-03-03 14:05:11 -05:00
NIBBLETYPE cWorld : : GetBlockSkyLight ( int a_X , int a_Y , int a_Z )
2012-06-14 09:06:06 -04:00
{
return m_ChunkMap - > GetBlockSkyLight ( a_X , a_Y , a_Z ) ;
}
2012-10-20 07:40:34 -04:00
NIBBLETYPE cWorld : : GetBlockBlockLight ( int a_BlockX , int a_BlockY , int a_BlockZ )
{
return m_ChunkMap - > GetBlockBlockLight ( a_BlockX , a_BlockY , a_BlockZ ) ;
}
2012-10-26 04:47:30 -04:00
bool cWorld : : GetBlockTypeMeta ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE & a_BlockType , NIBBLETYPE & a_BlockMeta )
2012-06-14 09:06:06 -04:00
{
2012-10-26 04:47:30 -04:00
return m_ChunkMap - > GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , ( BLOCKTYPE & ) a_BlockType , ( NIBBLETYPE & ) a_BlockMeta ) ;
2012-06-14 09:06:06 -04:00
}
2012-10-26 04:47:30 -04:00
bool cWorld : : GetBlockInfo ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE & a_BlockType , NIBBLETYPE & a_Meta , NIBBLETYPE & a_SkyLight , NIBBLETYPE & a_BlockLight )
2012-10-20 07:40:34 -04:00
{
2012-10-26 04:47:30 -04:00
return m_ChunkMap - > GetBlockInfo ( a_BlockX , a_BlockY , a_BlockZ , a_BlockType , a_Meta , a_SkyLight , a_BlockLight ) ;
2012-10-20 07:40:34 -04:00
}
2012-10-06 12:58:31 -04:00
bool cWorld : : WriteBlockArea ( cBlockArea & a_Area , int a_MinBlockX , int a_MinBlockY , int a_MinBlockZ , int a_DataTypes )
{
return m_ChunkMap - > WriteBlockArea ( a_Area , a_MinBlockX , a_MinBlockY , a_MinBlockZ , a_DataTypes ) ;
}
2012-06-14 09:06:06 -04:00
void cWorld : : SpawnItemPickups ( const cItems & a_Pickups , double a_BlockX , double a_BlockY , double a_BlockZ , double a_FlyAwaySpeed )
{
MTRand r1 ;
2012-08-24 03:58:26 -04:00
a_FlyAwaySpeed / = 1000 ; // Pre-divide, so that we don't have to divide each time inside the loop
2012-06-14 09:06:06 -04:00
for ( cItems : : const_iterator itr = a_Pickups . begin ( ) ; itr ! = a_Pickups . end ( ) ; + + itr )
{
float SpeedX = ( float ) ( a_FlyAwaySpeed * ( r1 . randInt ( 1000 ) - 500 ) ) ;
2013-09-04 13:26:00 -04:00
float SpeedY = 1 ;
2012-06-14 09:06:06 -04:00
float SpeedZ = ( float ) ( a_FlyAwaySpeed * ( r1 . randInt ( 1000 ) - 500 ) ) ;
2013-05-12 17:00:21 -04:00
2012-06-14 09:06:06 -04:00
cPickup * Pickup = new cPickup (
2013-09-18 17:20:08 -04:00
a_BlockX , a_BlockY , a_BlockZ ,
2012-06-14 09:06:06 -04:00
* itr , SpeedX , SpeedY , SpeedZ
) ;
Pickup - > Initialize ( this ) ;
}
}
void cWorld : : SpawnItemPickups ( const cItems & a_Pickups , double a_BlockX , double a_BlockY , double a_BlockZ , double a_SpeedX , double a_SpeedY , double a_SpeedZ )
{
for ( cItems : : const_iterator itr = a_Pickups . begin ( ) ; itr ! = a_Pickups . end ( ) ; + + itr )
{
cPickup * Pickup = new cPickup (
2013-09-18 17:20:08 -04:00
a_BlockX , a_BlockY , a_BlockZ ,
2012-06-14 09:06:06 -04:00
* itr , ( float ) a_SpeedX , ( float ) a_SpeedY , ( float ) a_SpeedZ
) ;
Pickup - > Initialize ( this ) ;
}
}
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
void cWorld : : SpawnPrimedTNT ( double a_X , double a_Y , double a_Z , double a_FuseTimeInSec , double a_InitialVelocityCoeff )
2013-06-18 15:09:51 -04:00
{
cTNTEntity * TNT = new cTNTEntity ( a_X , a_Y , a_Z , a_FuseTimeInSec ) ;
TNT - > Initialize ( this ) ;
2013-06-21 17:19:47 -04:00
// TODO: Add a bit of speed in horiz and vert axes, based on the a_InitialVelocityCoeff
2013-06-18 15:09:51 -04:00
}
2012-06-14 09:06:06 -04:00
void cWorld : : ReplaceBlocks ( const sSetBlockVector & a_Blocks , BLOCKTYPE a_FilterBlockType )
{
m_ChunkMap - > ReplaceBlocks ( a_Blocks , a_FilterBlockType ) ;
}
bool cWorld : : GetBlocks ( sSetBlockVector & a_Blocks , bool a_ContinueOnFailure )
{
return m_ChunkMap - > GetBlocks ( a_Blocks , a_ContinueOnFailure ) ;
}
2013-03-03 14:05:11 -05:00
bool cWorld : : DigBlock ( int a_X , int a_Y , int a_Z )
2012-06-14 09:06:06 -04:00
{
2012-07-15 16:36:34 -04:00
cBlockHandler * Handler = cBlockHandler : : GetBlockHandler ( GetBlock ( a_X , a_Y , a_Z ) ) ;
Handler - > OnDestroyed ( this , a_X , a_Y , a_Z ) ;
2012-06-14 09:06:06 -04:00
return m_ChunkMap - > DigBlock ( a_X , a_Y , a_Z ) ;
}
2013-03-03 14:05:11 -05:00
void cWorld : : SendBlockTo ( int a_X , int a_Y , int a_Z , cPlayer * a_Player )
2012-06-14 09:06:06 -04:00
{
m_ChunkMap - > SendBlockTo ( a_X , a_Y , a_Z , a_Player ) ;
}
2013-03-03 14:05:11 -05:00
int cWorld : : GetHeight ( int a_X , int a_Z )
2012-06-14 09:06:06 -04:00
{
return m_ChunkMap - > GetHeight ( a_X , a_Z ) ;
}
2013-04-13 17:02:10 -04:00
bool cWorld : : TryGetHeight ( int a_BlockX , int a_BlockZ , int & a_Height )
{
return m_ChunkMap - > TryGetHeight ( a_BlockX , a_BlockZ , a_Height ) ;
}
2013-03-03 14:05:11 -05:00
void cWorld : : BroadcastAttachEntity ( const cEntity & a_Entity , const cEntity * a_Vehicle )
{
return m_ChunkMap - > BroadcastAttachEntity ( a_Entity , a_Vehicle ) ;
}
2013-07-07 09:06:06 -04:00
void cWorld : : 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-18 06:38:15 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastBlockAction ( a_BlockX , a_BlockY , a_BlockZ , a_Byte1 , a_Byte2 , a_BlockType , a_Exclude ) ;
2012-08-18 06:38:15 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastBlockBreakAnimation ( int a_EntityID , int a_BlockX , int a_BlockY , int a_BlockZ , char a_Stage , const cClientHandle * a_Exclude )
2012-08-19 07:51:17 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastBlockBreakAnimation ( a_EntityID , a_BlockX , a_BlockY , a_BlockZ , a_Stage , a_Exclude ) ;
2012-08-19 07:51:17 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastBlockEntity ( int a_BlockX , int a_BlockY , int a_BlockZ , const cClientHandle * a_Exclude )
2012-08-19 07:51:17 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastBlockEntity ( a_BlockX , a_BlockY , a_BlockZ , a_Exclude ) ;
2012-08-19 07:51:17 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastChat ( const AString & a_Message , const cClientHandle * a_Exclude )
2012-08-19 15:42:32 -04:00
{
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
cClientHandle * ch = ( * itr ) - > GetClientHandle ( ) ;
if ( ( ch = = a_Exclude ) | | ( ch = = NULL ) | | ! ch - > IsLoggedIn ( ) | | ch - > IsDestroyed ( ) )
{
continue ;
}
2013-07-07 09:06:06 -04:00
ch - > SendChat ( a_Message ) ;
2012-08-19 15:42:32 -04:00
}
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastChunkData ( int a_ChunkX , int a_ChunkZ , cChunkDataSerializer & a_Serializer , const cClientHandle * a_Exclude )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastChunkData ( a_ChunkX , a_ChunkZ , a_Serializer , a_Exclude ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastCollectPickup ( const cPickup & a_Pickup , const cPlayer & a_Player , const cClientHandle * a_Exclude )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastCollectPickup ( a_Pickup , a_Player , a_Exclude ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastDestroyEntity ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastDestroyEntity ( a_Entity , a_Exclude ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastEntityEquipment ( const cEntity & a_Entity , short a_SlotNum , const cItem & a_Item , const cClientHandle * a_Exclude )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastEntityEquipment ( a_Entity , a_SlotNum , a_Item , a_Exclude ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastEntityHeadLook ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastEntityHeadLook ( a_Entity , a_Exclude ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastEntityLook ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastEntityLook ( a_Entity , a_Exclude ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastEntityMetadata ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-19 17:14:45 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastEntityMetadata ( a_Entity , a_Exclude ) ;
2012-08-19 17:14:45 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastEntityRelMove ( const cEntity & a_Entity , char a_RelX , char a_RelY , char a_RelZ , const cClientHandle * a_Exclude )
2012-08-19 17:14:45 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastEntityRelMove ( a_Entity , a_RelX , a_RelY , a_RelZ , a_Exclude ) ;
2012-08-19 17:14:45 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastEntityRelMoveLook ( const cEntity & a_Entity , char a_RelX , char a_RelY , char a_RelZ , const cClientHandle * a_Exclude )
2012-08-24 03:58:26 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastEntityRelMoveLook ( a_Entity , a_RelX , a_RelY , a_RelZ , a_Exclude ) ;
2012-08-24 03:58:26 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastEntityStatus ( const cEntity & a_Entity , char a_Status , const cClientHandle * a_Exclude )
2012-08-24 05:49:00 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastEntityStatus ( a_Entity , a_Status , a_Exclude ) ;
2012-08-24 05:49:00 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastEntityVelocity ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
{
m_ChunkMap - > BroadcastEntityVelocity ( a_Entity , a_Exclude ) ;
}
void cWorld : : BroadcastPlayerAnimation ( const cPlayer & a_Player , char a_Animation , const cClientHandle * a_Exclude )
{
m_ChunkMap - > BroadcastPlayerAnimation ( a_Player , a_Animation , a_Exclude ) ;
}
void cWorld : : BroadcastPlayerListItem ( const cPlayer & a_Player , bool a_IsOnline , const cClientHandle * a_Exclude )
2012-08-25 17:46:18 -04:00
{
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
cClientHandle * ch = ( * itr ) - > GetClientHandle ( ) ;
if ( ( ch = = a_Exclude ) | | ( ch = = NULL ) | | ! ch - > IsLoggedIn ( ) | | ch - > IsDestroyed ( ) )
{
continue ;
}
2013-07-07 09:06:06 -04:00
ch - > SendPlayerListItem ( a_Player , a_IsOnline ) ;
2012-08-25 17:46:18 -04:00
}
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastSoundEffect ( const AString & a_SoundName , int a_SrcX , int a_SrcY , int a_SrcZ , float a_Volume , float a_Pitch , const cClientHandle * a_Exclude )
2012-08-25 17:46:18 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastSoundEffect ( a_SoundName , a_SrcX , a_SrcY , a_SrcZ , a_Volume , a_Pitch , a_Exclude ) ;
2012-08-25 17:46:18 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastSoundParticleEffect ( int a_EffectID , int a_SrcX , int a_SrcY , int a_SrcZ , int a_Data , const cClientHandle * a_Exclude )
2012-08-25 17:46:18 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastSoundParticleEffect ( a_EffectID , a_SrcX , a_SrcY , a_SrcZ , a_Data , a_Exclude ) ;
2012-08-25 17:46:18 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastSpawnEntity ( cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-26 17:01:07 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastSpawnEntity ( a_Entity , a_Exclude ) ;
2012-08-26 17:01:07 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastTeleportEntity ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-27 13:31:16 -04:00
{
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
cClientHandle * ch = ( * itr ) - > GetClientHandle ( ) ;
if ( ( ch = = a_Exclude ) | | ( ch = = NULL ) | | ! ch - > IsLoggedIn ( ) | | ch - > IsDestroyed ( ) )
{
continue ;
}
2013-07-07 09:06:06 -04:00
ch - > SendTeleportEntity ( a_Entity ) ;
2012-08-27 13:31:16 -04:00
}
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastThunderbolt ( int a_BlockX , int a_BlockY , int a_BlockZ , const cClientHandle * a_Exclude )
2012-09-25 05:54:36 -04:00
{
2013-07-07 09:06:06 -04:00
m_ChunkMap - > BroadcastThunderbolt ( a_BlockX , a_BlockY , a_BlockZ , a_Exclude ) ;
2012-10-21 03:46:28 -04:00
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastTimeUpdate ( const cClientHandle * a_Exclude )
2012-10-21 03:46:28 -04:00
{
2013-07-07 09:06:06 -04:00
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
cClientHandle * ch = ( * itr ) - > GetClientHandle ( ) ;
if ( ( ch = = a_Exclude ) | | ( ch = = NULL ) | | ! ch - > IsLoggedIn ( ) | | ch - > IsDestroyed ( ) )
{
continue ;
}
ch - > SendTimeUpdate ( m_WorldAge , m_TimeOfDay ) ;
}
2012-09-25 05:54:36 -04:00
}
2013-03-03 14:05:11 -05:00
void cWorld : : BroadcastUseBed ( const cEntity & a_Entity , int a_BlockX , int a_BlockY , int a_BlockZ )
2012-09-29 16:43:42 -04:00
{
m_ChunkMap - > BroadcastUseBed ( a_Entity , a_BlockX , a_BlockY , a_BlockZ ) ;
}
2013-07-07 09:06:06 -04:00
void cWorld : : BroadcastWeather ( eWeather a_Weather , const cClientHandle * a_Exclude )
2012-08-24 03:58:26 -04:00
{
2013-07-07 09:06:06 -04:00
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
cClientHandle * ch = ( * itr ) - > GetClientHandle ( ) ;
if ( ( ch = = a_Exclude ) | | ( ch = = NULL ) | | ! ch - > IsLoggedIn ( ) | | ch - > IsDestroyed ( ) )
{
continue ;
}
ch - > SendWeather ( a_Weather ) ;
}
2012-08-24 03:58:26 -04:00
}
void cWorld : : SendBlockEntity ( int a_BlockX , int a_BlockY , int a_BlockZ , cClientHandle & a_Client )
{
m_ChunkMap - > SendBlockEntity ( a_BlockX , a_BlockY , a_BlockZ , a_Client ) ;
}
2013-04-13 17:02:10 -04:00
void cWorld : : MarkChunkDirty ( int a_ChunkX , int a_ChunkZ )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkMap - > MarkChunkDirty ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
void cWorld : : MarkChunkSaving ( int a_ChunkX , int a_ChunkZ )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkMap - > MarkChunkSaving ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
void cWorld : : MarkChunkSaved ( int a_ChunkX , int a_ChunkZ )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkMap - > MarkChunkSaved ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
void cWorld : : SetChunkData (
2013-04-13 17:02:10 -04:00
int a_ChunkX , int a_ChunkZ ,
2012-06-14 09:06:06 -04:00
const BLOCKTYPE * a_BlockTypes ,
const NIBBLETYPE * a_BlockMeta ,
const NIBBLETYPE * a_BlockLight ,
const NIBBLETYPE * a_BlockSkyLight ,
const cChunkDef : : HeightMap * a_HeightMap ,
const cChunkDef : : BiomeMap * a_BiomeMap ,
cEntityList & a_Entities ,
cBlockEntityList & a_BlockEntities ,
bool a_MarkDirty
)
{
// Validate biomes, if needed:
cChunkDef : : BiomeMap BiomeMap ;
const cChunkDef : : BiomeMap * Biomes = a_BiomeMap ;
if ( a_BiomeMap = = NULL )
{
// The biomes are not assigned, get them from the generator:
Biomes = & BiomeMap ;
m_Generator . GenerateBiomes ( a_ChunkX , a_ChunkZ , BiomeMap ) ;
}
2013-04-01 16:56:25 -04:00
m_ChunkMap - > SetChunkData (
2013-04-13 17:02:10 -04:00
a_ChunkX , a_ChunkZ ,
2013-04-01 16:56:25 -04:00
a_BlockTypes , a_BlockMeta , a_BlockLight , a_BlockSkyLight ,
a_HeightMap , * Biomes ,
2013-04-13 17:02:10 -04:00
a_BlockEntities ,
2013-04-01 16:56:25 -04:00
a_MarkDirty
) ;
// Initialize the entities (outside the m_ChunkMap's CS, to fix FS #347):
for ( cEntityList : : iterator itr = a_Entities . begin ( ) , end = a_Entities . end ( ) ; itr ! = end ; + + itr )
2013-04-01 14:24:05 -04:00
{
2013-04-01 16:56:25 -04:00
( * itr ) - > Initialize ( this ) ;
2013-04-01 14:24:05 -04:00
}
2012-06-14 09:06:06 -04:00
// If a client is requesting this chunk, send it to them:
2013-04-13 17:02:10 -04:00
if ( m_ChunkMap - > HasChunkAnyClients ( a_ChunkX , a_ChunkZ ) )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkSender . ChunkReady ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
// Notify the lighting thread that the chunk has become valid (in case it is a neighbor of a postponed chunk):
m_Lighting . ChunkReady ( a_ChunkX , a_ChunkZ ) ;
}
void cWorld : : ChunkLighted (
int a_ChunkX , int a_ChunkZ ,
const cChunkDef : : BlockNibbles & a_BlockLight ,
const cChunkDef : : BlockNibbles & a_SkyLight
)
{
m_ChunkMap - > ChunkLighted ( a_ChunkX , a_ChunkZ , a_BlockLight , a_SkyLight ) ;
}
2013-04-13 17:02:10 -04:00
bool cWorld : : GetChunkData ( int a_ChunkX , int a_ChunkZ , cChunkDataCallback & a_Callback )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > GetChunkData ( a_ChunkX , a_ChunkZ , a_Callback ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
bool cWorld : : GetChunkBlockTypes ( int a_ChunkX , int a_ChunkZ , BLOCKTYPE * a_BlockTypes )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > GetChunkBlockTypes ( a_ChunkX , a_ChunkZ , a_BlockTypes ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
bool cWorld : : IsChunkValid ( int a_ChunkX , int a_ChunkZ ) const
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > IsChunkValid ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
bool cWorld : : HasChunkAnyClients ( int a_ChunkX , int a_ChunkZ ) const
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > HasChunkAnyClients ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2013-03-03 14:05:11 -05:00
void cWorld : : UnloadUnusedChunks ( void )
2012-06-14 09:06:06 -04:00
{
2012-11-01 17:38:20 -04:00
m_LastUnload = m_WorldAge ;
2012-06-14 09:06:06 -04:00
m_ChunkMap - > UnloadUnusedChunks ( ) ;
}
void cWorld : : CollectPickupsByPlayer ( cPlayer * a_Player )
{
m_ChunkMap - > CollectPickupsByPlayer ( a_Player ) ;
}
2013-04-13 17:02:10 -04:00
void cWorld : : AddPlayer ( cPlayer * a_Player )
2012-06-14 09:06:06 -04:00
{
{
2013-08-13 16:45:29 -04:00
cCSLock Lock ( m_CSPlayers ) ;
ASSERT ( std : : find ( m_Players . begin ( ) , m_Players . end ( ) , a_Player ) = = m_Players . end ( ) ) ; // Is it already in the list? HOW?
m_Players . remove ( a_Player ) ; // Make sure the player is registered only once
m_Players . push_back ( a_Player ) ;
2012-06-14 09:06:06 -04:00
}
2013-07-03 03:47:35 -04:00
2013-08-13 16:45:29 -04:00
// Add the player's client to the list of clients to be ticked:
if ( a_Player - > GetClientHandle ( ) ! = NULL )
{
cCSLock Lock ( m_CSClients ) ;
2013-08-14 07:43:55 -04:00
m_ClientsToAdd . push_back ( a_Player - > GetClientHandle ( ) ) ;
2013-08-13 16:45:29 -04:00
}
2013-07-03 03:47:35 -04:00
// The player has already been added to the chunkmap as the entity, do NOT add again!
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
void cWorld : : RemovePlayer ( cPlayer * a_Player )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:28:55 -04:00
m_ChunkMap - > RemoveEntity ( a_Player ) ;
2013-08-13 16:45:29 -04:00
{
cCSLock Lock ( m_CSPlayers ) ;
m_Players . remove ( a_Player ) ;
}
// Remove the player's client from the list of clients to be ticked:
if ( a_Player - > GetClientHandle ( ) ! = NULL )
{
cCSLock Lock ( m_CSClients ) ;
2013-08-14 07:43:55 -04:00
m_ClientsToRemove . push_back ( a_Player - > GetClientHandle ( ) ) ;
2013-08-13 16:45:29 -04:00
}
2012-06-14 09:06:06 -04:00
}
bool cWorld : : ForEachPlayer ( cPlayerListCallback & a_Callback )
{
// Calls the callback for each player in the list
cCSLock Lock ( m_CSPlayers ) ;
2012-06-19 16:31:21 -04:00
for ( cPlayerList : : iterator itr = m_Players . begin ( ) , itr2 = itr ; itr ! = m_Players . end ( ) ; itr = itr2 )
2012-06-14 09:06:06 -04:00
{
2012-06-19 16:31:21 -04:00
+ + itr2 ;
2012-06-14 09:06:06 -04:00
if ( a_Callback . Item ( * itr ) )
{
return false ;
}
} // for itr - m_Players[]
return true ;
}
2012-07-02 07:21:21 -04:00
bool cWorld : : DoWithPlayer ( const AString & a_PlayerName , cPlayerListCallback & a_Callback )
{
// Calls the callback for each player in the list
2012-06-14 09:06:06 -04:00
cCSLock Lock ( m_CSPlayers ) ;
2012-07-02 07:21:21 -04:00
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
2012-06-14 09:06:06 -04:00
{
2012-07-13 15:25:53 -04:00
if ( NoCaseCompare ( ( * itr ) - > GetName ( ) , a_PlayerName ) = = 0 )
2012-06-14 09:06:06 -04:00
{
2012-07-02 07:21:21 -04:00
a_Callback . Item ( * itr ) ;
return true ;
2012-06-14 09:06:06 -04:00
}
2012-07-02 07:21:21 -04:00
} // for itr - m_Players[]
return false ;
2012-06-14 09:06:06 -04:00
}
2013-02-01 14:59:58 -05:00
bool cWorld : : FindAndDoWithPlayer ( const AString & a_PlayerNameHint , cPlayerListCallback & a_Callback )
2012-08-22 19:05:12 -04:00
{
2013-02-01 14:59:58 -05:00
cPlayer * BestMatch = NULL ;
2012-08-22 19:05:12 -04:00
unsigned int BestRating = 0 ;
2013-02-01 14:59:58 -05:00
unsigned int NameLength = a_PlayerNameHint . length ( ) ;
2012-08-22 19:05:12 -04:00
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
2013-02-01 14:59:58 -05:00
unsigned int Rating = RateCompareString ( a_PlayerNameHint , ( * itr ) - > GetName ( ) ) ;
if ( Rating > = BestRating )
2012-08-22 19:05:12 -04:00
{
BestMatch = * itr ;
BestRating = Rating ;
}
2013-02-01 14:59:58 -05:00
if ( Rating = = NameLength ) // Perfect match
2012-08-22 19:05:12 -04:00
{
break ;
}
} // for itr - m_Players[]
2013-02-01 14:59:58 -05:00
if ( BestMatch ! = NULL )
2012-08-22 19:05:12 -04:00
{
2013-02-01 14:59:58 -05:00
LOG ( " Compared %s and %s with rating %i " , a_PlayerNameHint . c_str ( ) , BestMatch - > GetName ( ) . c_str ( ) , BestRating ) ;
2012-08-22 19:05:12 -04:00
return a_Callback . Item ( BestMatch ) ;
}
return false ;
}
2012-07-02 07:21:21 -04:00
// TODO: This interface is dangerous!
2012-06-14 09:06:06 -04:00
cPlayer * cWorld : : FindClosestPlayer ( const Vector3f & a_Pos , float a_SightLimit )
{
cTracer LineOfSight ( this ) ;
float ClosestDistance = a_SightLimit ;
cPlayer * ClosestPlayer = NULL ;
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : const_iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
Vector3f Pos = ( * itr ) - > GetPosition ( ) ;
float Distance = ( Pos - a_Pos ) . Length ( ) ;
2013-08-24 16:43:17 -04:00
if ( Distance < ClosestDistance )
2012-06-14 09:06:06 -04:00
{
if ( ! LineOfSight . Trace ( a_Pos , ( Pos - a_Pos ) , ( int ) ( Pos - a_Pos ) . Length ( ) ) )
{
2013-08-24 16:43:17 -04:00
ClosestDistance = Distance ;
ClosestPlayer = * itr ;
2012-06-14 09:06:06 -04:00
}
}
}
return ClosestPlayer ;
}
void cWorld : : SendPlayerList ( cPlayer * a_DestPlayer )
{
// Sends the playerlist to a_DestPlayer
cCSLock Lock ( m_CSPlayers ) ;
2012-08-19 15:42:32 -04:00
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
2012-06-14 09:06:06 -04:00
{
cClientHandle * ch = ( * itr ) - > GetClientHandle ( ) ;
if ( ( ch ! = NULL ) & & ! ch - > IsDestroyed ( ) )
{
2012-08-27 13:31:16 -04:00
a_DestPlayer - > GetClientHandle ( ) - > SendPlayerListItem ( * ( * itr ) , true ) ;
2012-06-14 09:06:06 -04:00
}
}
}
2012-06-16 04:35:07 -04:00
bool cWorld : : ForEachEntity ( cEntityCallback & a_Callback )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > ForEachEntity ( a_Callback ) ;
2012-06-14 09:06:06 -04:00
}
2012-06-16 04:35:07 -04:00
bool cWorld : : ForEachEntityInChunk ( int a_ChunkX , int a_ChunkZ , cEntityCallback & a_Callback )
2012-06-14 09:06:06 -04:00
{
2012-06-16 04:35:07 -04:00
return m_ChunkMap - > ForEachEntityInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
2012-06-14 09:06:06 -04:00
}
2013-03-03 14:05:11 -05:00
bool cWorld : : DoWithEntityByID ( int a_UniqueID , cEntityCallback & a_Callback )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > DoWithEntityByID ( a_UniqueID , a_Callback ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
void cWorld : : CompareChunkClients ( int a_ChunkX1 , int a_ChunkZ1 , int a_ChunkX2 , int a_ChunkZ2 , cClientDiffCallback & a_Callback )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkMap - > CompareChunkClients ( a_ChunkX1 , a_ChunkZ1 , a_ChunkX2 , a_ChunkZ2 , a_Callback ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
bool cWorld : : AddChunkClient ( int a_ChunkX , int a_ChunkZ , cClientHandle * a_Client )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > AddChunkClient ( a_ChunkX , a_ChunkZ , a_Client ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
void cWorld : : RemoveChunkClient ( int a_ChunkX , int a_ChunkZ , cClientHandle * a_Client )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkMap - > RemoveChunkClient ( a_ChunkX , a_ChunkZ , a_Client ) ;
2012-06-14 09:06:06 -04:00
}
void cWorld : : RemoveClientFromChunks ( cClientHandle * a_Client )
{
m_ChunkMap - > RemoveClientFromChunks ( a_Client ) ;
}
2013-04-13 17:02:10 -04:00
void cWorld : : SendChunkTo ( int a_ChunkX , int a_ChunkZ , cClientHandle * a_Client )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkSender . QueueSendChunkTo ( a_ChunkX , a_ChunkZ , a_Client ) ;
2012-06-14 09:06:06 -04:00
}
void cWorld : : RemoveClientFromChunkSender ( cClientHandle * a_Client )
{
m_ChunkSender . RemoveClient ( a_Client ) ;
}
void cWorld : : TouchChunk ( int a_ChunkX , int a_ChunkY , int a_ChunkZ )
{
m_ChunkMap - > TouchChunk ( a_ChunkX , a_ChunkY , a_ChunkZ ) ;
}
bool cWorld : : LoadChunk ( int a_ChunkX , int a_ChunkY , int a_ChunkZ )
{
return m_ChunkMap - > LoadChunk ( a_ChunkX , a_ChunkY , a_ChunkZ ) ;
}
void cWorld : : LoadChunks ( const cChunkCoordsList & a_Chunks )
{
m_ChunkMap - > LoadChunks ( a_Chunks ) ;
}
void cWorld : : ChunkLoadFailed ( int a_ChunkX , int a_ChunkY , int a_ChunkZ )
{
m_ChunkMap - > ChunkLoadFailed ( a_ChunkX , a_ChunkY , a_ChunkZ ) ;
}
2013-06-12 03:14:06 -04:00
bool cWorld : : SetSignLines ( int a_BlockX , int a_BlockY , int a_BlockZ , const AString & a_Line1 , const AString & a_Line2 , const AString & a_Line3 , const AString & a_Line4 , cPlayer * a_Player )
2012-06-14 09:06:06 -04:00
{
2012-06-16 11:06:14 -04:00
AString Line1 ( a_Line1 ) ;
AString Line2 ( a_Line2 ) ;
AString Line3 ( a_Line3 ) ;
AString Line4 ( a_Line4 ) ;
2012-09-01 17:31:20 -04:00
if ( cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookUpdatingSign ( this , a_BlockX , a_BlockY , a_BlockZ , Line1 , Line2 , Line3 , Line4 , a_Player ) )
2012-06-16 11:06:14 -04:00
{
2013-06-12 03:14:06 -04:00
return false ;
}
if ( m_ChunkMap - > SetSignLines ( a_BlockX , a_BlockY , a_BlockZ , Line1 , Line2 , Line3 , Line4 ) )
{
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookUpdatedSign ( this , a_BlockX , a_BlockY , a_BlockZ , Line1 , Line2 , Line3 , Line4 , a_Player ) ;
return true ;
2012-06-16 11:06:14 -04:00
}
2013-06-12 03:14:06 -04:00
return false ;
}
bool cWorld : : UpdateSign ( int a_BlockX , int a_BlockY , int a_BlockZ , const AString & a_Line1 , const AString & a_Line2 , const AString & a_Line3 , const AString & a_Line4 , cPlayer * a_Player )
{
return SetSignLines ( a_BlockX , a_BlockY , a_BlockZ , a_Line1 , a_Line2 , a_Line3 , a_Line4 , a_Player ) ;
2012-06-14 09:06:06 -04:00
}
void cWorld : : ChunksStay ( const cChunkCoordsList & a_Chunks , bool a_Stay )
{
m_ChunkMap - > ChunksStay ( a_Chunks , a_Stay ) ;
}
void cWorld : : RegenerateChunk ( int a_ChunkX , int a_ChunkZ )
{
m_ChunkMap - > MarkChunkRegenerating ( a_ChunkX , a_ChunkZ ) ;
// Trick: use Y=1 to force the chunk generation even though the chunk data is already present
m_Generator . QueueGenerateChunk ( a_ChunkX , 1 , a_ChunkZ ) ;
}
void cWorld : : GenerateChunk ( int a_ChunkX , int a_ChunkZ )
{
m_Generator . QueueGenerateChunk ( a_ChunkX , ZERO_CHUNK_Y , a_ChunkZ ) ;
}
void cWorld : : QueueLightChunk ( int a_ChunkX , int a_ChunkZ , cChunkCoordCallback * a_Callback )
{
m_Lighting . QueueChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
bool cWorld : : IsChunkLighted ( int a_ChunkX , int a_ChunkZ )
{
return m_ChunkMap - > IsChunkLighted ( a_ChunkX , a_ChunkZ ) ;
}
2012-07-02 12:30:17 -04:00
bool cWorld : : ForEachChunkInRect ( int a_MinChunkX , int a_MaxChunkX , int a_MinChunkZ , int a_MaxChunkZ , cChunkDataCallback & a_Callback )
{
return m_ChunkMap - > ForEachChunkInRect ( a_MinChunkX , a_MaxChunkX , a_MinChunkZ , a_MaxChunkZ , a_Callback ) ;
}
2012-06-14 09:06:06 -04:00
void cWorld : : SaveAllChunks ( void )
{
2012-08-15 07:54:13 -04:00
LOGINFO ( " Saving all chunks... " ) ;
2012-11-01 17:38:20 -04:00
m_LastSave = m_WorldAge ;
2012-06-14 09:06:06 -04:00
m_ChunkMap - > SaveAllChunks ( ) ;
2012-08-15 07:54:13 -04:00
m_Storage . QueueSavedMessage ( ) ;
2012-06-14 09:06:06 -04:00
}
2013-08-11 15:05:44 -04:00
void cWorld : : QueueSaveAllChunks ( void )
{
QueueTask ( new cWorld : : cTaskSaveAllChunks ) ;
}
void cWorld : : QueueTask ( cTask * a_Task )
{
cCSLock Lock ( m_CSTasks ) ;
m_Tasks . push_back ( a_Task ) ;
}
2012-11-01 17:38:20 -04:00
void cWorld : : AddEntity ( cEntity * a_Entity )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkMap - > AddEntity ( a_Entity ) ;
}
bool cWorld : : HasEntity ( int a_UniqueID )
{
return m_ChunkMap - > HasEntity ( a_UniqueID ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:28:55 -04:00
void cWorld : : RemoveEntity ( cEntity * a_Entity )
{
m_ChunkMap - > RemoveEntity ( a_Entity ) ;
}
2013-08-11 13:18:06 -04:00
/*
2012-11-01 17:38:20 -04:00
unsigned int cWorld : : GetNumPlayers ( void )
2012-06-14 09:06:06 -04:00
{
cCSLock Lock ( m_CSPlayers ) ;
return m_Players . size ( ) ;
}
2013-08-11 13:18:06 -04:00
*/
2012-06-14 09:06:06 -04:00
int cWorld : : GetNumChunks ( void ) const
{
return m_ChunkMap - > GetNumChunks ( ) ;
}
void cWorld : : GetChunkStats ( int & a_NumValid , int & a_NumDirty , int & a_NumInLightingQueue )
{
m_ChunkMap - > GetChunkStats ( a_NumValid , a_NumDirty ) ;
a_NumInLightingQueue = ( int ) m_Lighting . GetQueueLength ( ) ;
}
2012-10-13 04:56:12 -04:00
2013-09-16 03:25:23 -04:00
void cWorld : : TickQueuedBlocks ( void )
2012-07-15 16:36:34 -04:00
{
2012-10-13 04:56:12 -04:00
if ( m_BlockTickQueue . empty ( ) )
{
2012-07-15 16:36:34 -04:00
return ;
2012-10-13 04:56:12 -04:00
}
2012-07-15 16:36:34 -04:00
m_BlockTickQueueCopy . clear ( ) ;
m_BlockTickQueue . swap ( m_BlockTickQueueCopy ) ;
2012-10-13 04:56:12 -04:00
for ( std : : vector < BlockTickQueueItem * > : : iterator itr = m_BlockTickQueueCopy . begin ( ) ; itr ! = m_BlockTickQueueCopy . end ( ) ; itr + + )
2012-07-15 16:36:34 -04:00
{
BlockTickQueueItem * Block = ( * itr ) ;
2013-09-16 03:25:23 -04:00
Block - > TicksToWait - = 1 ;
if ( Block - > TicksToWait < = 0 )
2012-07-15 16:36:34 -04:00
{
2013-09-16 03:25:23 -04:00
// TODO: Handle the case when the chunk is already unloaded
2012-07-15 16:36:34 -04:00
BlockHandler ( GetBlock ( Block - > X , Block - > Y , Block - > Z ) ) - > OnUpdate ( this , Block - > X , Block - > Y , Block - > Z ) ;
2013-09-16 03:25:23 -04:00
delete Block ; // We don't have to remove it from the vector, this will happen automatically on the next tick
2012-10-13 04:56:12 -04:00
}
else
{
2013-09-16 03:25:23 -04:00
m_BlockTickQueue . push_back ( Block ) ; // Keep the block in the queue
2012-07-15 16:36:34 -04:00
}
2012-10-13 04:56:12 -04:00
} // for itr - m_BlockTickQueueCopy[]
2012-07-15 16:36:34 -04:00
}
2012-10-13 04:56:12 -04:00
2013-09-16 03:25:23 -04:00
void cWorld : : QueueBlockForTick ( int a_BlockX , int a_BlockY , int a_BlockZ , int a_TicksToWait )
2012-07-15 16:36:34 -04:00
{
2012-10-13 04:56:12 -04:00
BlockTickQueueItem * Block = new BlockTickQueueItem ;
Block - > X = a_BlockX ;
Block - > Y = a_BlockY ;
Block - > Z = a_BlockZ ;
2013-09-16 03:25:23 -04:00
Block - > TicksToWait = a_TicksToWait ;
2012-07-15 16:36:34 -04:00
m_BlockTickQueue . push_back ( Block ) ;
}
2012-10-13 04:56:12 -04:00
bool cWorld : : IsBlockDirectlyWatered ( int a_BlockX , int a_BlockY , int a_BlockZ )
2012-07-15 16:36:34 -04:00
{
2012-10-13 04:56:12 -04:00
return (
IsBlockWater ( GetBlock ( a_BlockX - 1 , a_BlockY , a_BlockZ ) ) | |
IsBlockWater ( GetBlock ( a_BlockX + 1 , a_BlockY , a_BlockZ ) ) | |
IsBlockWater ( GetBlock ( a_BlockX , a_BlockY , a_BlockZ - 1 ) ) | |
IsBlockWater ( GetBlock ( a_BlockX , a_BlockY , a_BlockZ + 1 ) )
) ;
}
2013-08-16 04:48:19 -04:00
int cWorld : : SpawnMob ( double a_PosX , double a_PosY , double a_PosZ , cMonster : : eType a_MonsterType )
2012-10-28 10:57:35 -04:00
{
cMonster * Monster = NULL ;
2013-10-08 14:20:49 -04:00
int ShColor = GetTickRandomNumber ( 15 ) ; // 0 .. 15 - Sheep
2013-10-14 13:12:12 -04:00
bool SkType = GetDimension ( ) = = dimNether ; // Skeleton
2013-09-07 20:47:02 -04:00
2013-10-20 07:25:56 -04:00
Monster = cMonster : : NewMonsterFromType ( a_MonsterType ) ;
if ( Monster ! = NULL )
{
2013-10-14 13:12:12 -04:00
Monster - > SetPosition ( a_PosX , a_PosY , a_PosZ ) ;
2013-10-20 07:25:56 -04:00
}
2013-09-07 20:47:02 -04:00
return SpawnMobFinalize ( Monster ) ;
}
2013-10-20 07:25:56 -04:00
int cWorld : : SpawnMobFinalize ( cMonster * a_Monster )
2013-09-07 20:47:02 -04:00
{
2013-10-14 13:12:12 -04:00
if ( ! a_Monster )
return - 1 ;
2013-09-07 20:47:02 -04:00
a_Monster - > SetHealth ( a_Monster - > GetMaxHealth ( ) ) ;
if ( cPluginManager : : Get ( ) - > CallHookSpawningMonster ( * this , * a_Monster ) )
2013-08-08 03:13:13 -04:00
{
2013-09-07 20:47:02 -04:00
delete a_Monster ;
2013-08-08 03:13:13 -04:00
return - 1 ;
}
2013-09-07 20:47:02 -04:00
if ( ! a_Monster - > Initialize ( this ) )
2013-08-08 03:13:13 -04:00
{
2013-09-07 20:47:02 -04:00
delete a_Monster ;
2013-08-08 03:13:13 -04:00
return - 1 ;
}
2013-09-07 20:47:02 -04:00
BroadcastSpawnEntity ( * a_Monster ) ;
cPluginManager : : Get ( ) - > CallHookSpawnedMonster ( * this , * a_Monster ) ;
2013-10-08 14:20:49 -04:00
2013-09-07 20:47:02 -04:00
return a_Monster - > GetUniqueID ( ) ;
2012-10-28 10:57:35 -04:00
}
2013-08-22 02:55:58 -04:00
int cWorld : : CreateProjectile ( double a_PosX , double a_PosY , double a_PosZ , cProjectileEntity : : eKind a_Kind , cEntity * a_Creator , const Vector3d * a_Speed )
{
cProjectileEntity * Projectile = cProjectileEntity : : Create ( a_Kind , a_Creator , a_PosX , a_PosY , a_PosZ , a_Speed ) ;
if ( Projectile = = NULL )
{
return - 1 ;
}
if ( ! Projectile - > Initialize ( this ) )
{
delete Projectile ;
return - 1 ;
}
BroadcastSpawnEntity ( * Projectile ) ;
return Projectile - > GetUniqueID ( ) ;
}
2013-07-30 16:48:59 -04:00
void cWorld : : TabCompleteUserName ( const AString & a_Text , AStringVector & a_Results )
{
2013-07-31 05:16:11 -04:00
cCSLock Lock ( m_CSPlayers ) ;
for ( cPlayerList : : iterator itr = m_Players . begin ( ) , end = m_Players . end ( ) ; itr ! = end ; + + itr )
{
2013-08-18 09:52:38 -04:00
size_t LastSpace = a_Text . find_last_of ( " " ) ; //Find the position of the last space
std : : string LastWord = a_Text . substr ( LastSpace + 1 , a_Text . length ( ) ) ; //Find the last word
2013-08-18 06:58:19 -04:00
std : : string PlayerName ( ( * itr ) - > GetName ( ) ) ;
2013-08-18 09:52:38 -04:00
std : : size_t Found = PlayerName . find ( LastWord ) ; //Try to find last word in playername
2013-08-18 06:58:19 -04:00
if ( Found ! = 0 )
2013-07-31 05:16:11 -04:00
{
2013-08-18 09:52:38 -04:00
continue ; //No match
2013-07-31 05:16:11 -04:00
}
2013-08-18 09:52:38 -04:00
a_Results . push_back ( ( * itr ) - > GetName ( ) ) ; //Match!
2013-07-31 05:16:11 -04:00
}
2013-07-30 16:48:59 -04:00
}
2012-10-13 05:53:28 -04:00
cFluidSimulator * cWorld : : InitializeFluidSimulator ( cIniFile & a_IniFile , const char * a_FluidName , BLOCKTYPE a_SimulateBlock , BLOCKTYPE a_StationaryBlock )
{
AString SimulatorNameKey ;
Printf ( SimulatorNameKey , " %sSimulator " , a_FluidName ) ;
AString SimulatorSectionName ;
Printf ( SimulatorSectionName , " %sSimulator " , a_FluidName ) ;
2013-08-14 17:26:15 -04:00
AString SimulatorName = a_IniFile . GetValueSet ( " Physics " , SimulatorNameKey , " " ) ;
2012-10-13 05:53:28 -04:00
if ( SimulatorName . empty ( ) )
{
2013-08-14 17:26:15 -04:00
LOGWARNING ( " [Physics] %s not present or empty in %s, using the default of \" Floody \" . " , SimulatorNameKey . c_str ( ) , GetIniFileName ( ) . c_str ( ) ) ;
2013-03-14 16:50:01 -04:00
SimulatorName = " Floody " ;
2012-10-13 05:53:28 -04:00
}
cFluidSimulator * res = NULL ;
2012-10-14 16:10:03 -04:00
bool IsWater = ( strcmp ( a_FluidName , " Water " ) = = 0 ) ; // Used for defaults
2012-10-15 16:16:43 -04:00
int Rate = 1 ;
2013-03-14 16:46:40 -04:00
if (
2013-03-14 15:44:27 -04:00
( NoCaseCompare ( SimulatorName , " vaporize " ) = = 0 ) | |
( NoCaseCompare ( SimulatorName , " vaporise " ) = = 0 )
)
{
res = new cVaporizeFluidSimulator ( * this , a_SimulateBlock , a_StationaryBlock ) ;
}
2013-03-14 16:03:42 -04:00
else if (
( NoCaseCompare ( SimulatorName , " noop " ) = = 0 ) | |
( NoCaseCompare ( SimulatorName , " nop " ) = = 0 ) | |
( NoCaseCompare ( SimulatorName , " null " ) = = 0 ) | |
( NoCaseCompare ( SimulatorName , " nil " ) = = 0 )
)
{
res = new cNoopFluidSimulator ( * this , a_SimulateBlock , a_StationaryBlock ) ;
}
2012-10-13 05:53:28 -04:00
else
{
2013-03-14 16:46:40 -04:00
if ( NoCaseCompare ( SimulatorName , " floody " ) ! = 0 )
2012-10-13 05:53:28 -04:00
{
// The simulator name doesn't match anything we have, issue a warning:
2013-03-14 16:46:40 -04:00
LOGWARNING ( " %s [Physics]:%s specifies an unknown simulator, using the default \" Floody \" . " , GetIniFileName ( ) . c_str ( ) , SimulatorNameKey . c_str ( ) ) ;
2012-10-13 05:53:28 -04:00
}
2013-03-14 16:46:40 -04:00
int Falloff = a_IniFile . GetValueSetI ( SimulatorSectionName , " Falloff " , IsWater ? 1 : 2 ) ;
int TickDelay = a_IniFile . GetValueSetI ( SimulatorSectionName , " TickDelay " , IsWater ? 5 : 30 ) ;
int NumNeighborsForSource = a_IniFile . GetValueSetI ( SimulatorSectionName , " NumNeighborsForSource " , IsWater ? 2 : - 1 ) ;
res = new cFloodyFluidSimulator ( * this , a_SimulateBlock , a_StationaryBlock , Falloff , TickDelay , NumNeighborsForSource ) ;
2012-10-13 05:53:28 -04:00
}
2012-10-15 16:16:43 -04:00
m_SimulatorManager - > RegisterSimulator ( res , Rate ) ;
2012-10-13 05:53:28 -04:00
return res ;
}
2013-08-11 15:05:44 -04:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorld::cTaskSaveAllChunks:
void cWorld : : cTaskSaveAllChunks : : Run ( cWorld & a_World )
{
a_World . SaveAllChunks ( ) ;
}