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
#include "BlockID.h"
2012-09-23 22:09:57 +00:00
#include "World.h"
2012-06-14 13:06:06 +00:00
#include "ChunkDef.h"
2012-09-23 22:09:57 +00:00
#include "ClientHandle.h"
#include "Server.h"
#include "Item.h"
#include "Root.h"
2013-11-26 17:06:00 +00:00
#include "inifile/iniFile.h"
2012-09-23 22:09:57 +00:00
#include "ChunkMap.h"
2014-01-10 22:22:54 +01:00
#include "Generating/ChunkDesc.h"
2013-08-19 22:23:25 +02:00
#include "OSSupport/Timer.h"
2014-07-24 18:32:05 +02:00
#include "SetChunkData.h"
2014-02-13 21:36:24 +02:00
2014-02-20 16:38:37 +02:00
// Serializers
2014-01-22 15:49:21 +02:00
#include "WorldStorage/ScoreboardSerializer.h"
2012-10-13 09:53:28 +00:00
2013-08-22 08:55:58 +02:00
// Entities (except mobs):
2013-11-25 21:03:26 +01:00
#include "Entities/ExpOrb.h"
2013-12-07 14:26:52 +01:00
#include "Entities/FallingBlock.h"
2014-01-12 14:33:32 +01:00
#include "Entities/Minecart.h"
2013-08-22 08:55:58 +02:00
#include "Entities/Pickup.h"
#include "Entities/Player.h"
#include "Entities/TNTEntity.h"
2014-01-23 14:57:04 +02:00
#include "BlockEntities/CommandBlockEntity.h"
2014-07-30 22:19:51 +02:00
#include "BlockEntities/BeaconEntity.h"
2014-01-23 14:57:04 +02:00
2012-10-13 09:53:28 +00:00
// Simulators:
#include "Simulator/SimulatorManager.h"
2012-10-14 17:06:21 +00:00
#include "Simulator/FloodyFluidSimulator.h"
2012-10-13 09:53:28 +00:00
#include "Simulator/FluidSimulator.h"
#include "Simulator/FireSimulator.h"
2013-03-14 20:03:42 +00:00
#include "Simulator/NoopFluidSimulator.h"
2014-02-07 22:13:55 +01:00
#include "Simulator/NoopRedstoneSimulator.h"
2012-10-13 09:53:28 +00:00
#include "Simulator/SandSimulator.h"
2014-02-07 22:59:08 +01:00
#include "Simulator/IncrementalRedstoneSimulator.h"
2014-03-05 15:54:38 +02:00
#include "Simulator/VanillaFluidSimulator.h"
2013-03-14 19:44:27 +00:00
#include "Simulator/VaporizeFluidSimulator.h"
2012-09-23 20:53:08 +00:00
// Mobs:
2013-09-07 20:07:56 +02:00
#include "Mobs/IncludeAllMonsters.h"
2013-09-07 22:19:56 +02:00
#include "MobCensus.h"
2013-09-08 02:47:02 +02:00
#include "MobSpawner.h"
2012-09-23 20:53:08 +00:00
2012-06-14 13:06:06 +00:00
#include "MersenneTwister.h"
2012-09-23 20:14:04 +00:00
#include "Generating/Trees.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"
2012-06-14 13:06:06 +00:00
2013-08-19 11:39:13 +02:00
#include "Tracer.h"
2012-06-14 13:06:06 +00:00
2013-08-04 16:08:09 +02:00
// DEBUG: Test out the cLineBlockTracer class by tracing a few lines:
#include "LineBlockTracer.h"
2012-06-14 13:06:06 +00:00
#ifndef _WIN32
#include <stdlib.h>
#endif
2013-10-30 23:33:42 +01:00
const int TIME_SUNSET = 12000 ;
const int TIME_NIGHT_START = 13187 ;
const int TIME_NIGHT_END = 22812 ;
const int TIME_SUNRISE = 23999 ;
const int TIME_SPAWN_DIVISOR = 148 ;
2012-06-14 13:06:06 +00:00
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2012-06-14 13:06:06 +00:00
// 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 (;;)
{
2014-07-17 16:33:09 +02:00
LOG ( "" SIZE_T_FMT " chunks to load, %d chunks to generate" ,
2012-06-14 13:06:06 +00:00
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)
}
} ;
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-08-11 19:18:06 +02:00
// cWorldLightingProgress:
2012-06-14 13:06:06 +00: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 (;;)
{
2014-03-12 10:34:50 -07:00
LOG ( "" SIZE_T_FMT " chunks remaining to light" , m_Lighting -> GetQueueLength ()
2012-06-14 13:06:06 +00:00
);
// 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)
}
} ;
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-06-15 15:29:20 +00:00
// cWorld::cLock:
cWorld :: cLock :: cLock ( cWorld & a_World ) :
super ( & ( a_World . m_ChunkMap -> GetCS ()))
{
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-08-11 19:18:06 +02:00
// cWorld::cTickThread:
2012-06-14 13:06:06 +00:00
2013-08-11 19:18:06 +02:00
cWorld :: cTickThread :: cTickThread ( cWorld & a_World ) :
super ( Printf ( "WorldTickThread: %s" , a_World . GetName (). c_str ())),
m_World ( a_World )
2012-06-14 13:06:06 +00:00
{
2013-08-11 19:18:06 +02:00
}
2012-06-14 13:06:06 +00:00
2013-08-11 19:18:06 +02:00
void cWorld :: cTickThread :: Execute ( void )
{
2013-08-19 22:23:25 +02:00
cTimer Timer ;
2013-11-30 14:22:26 +01:00
const Int64 msPerTick = 50 ;
Int64 LastTime = Timer . GetNowTime ();
2013-08-19 22:23:25 +02:00
2013-11-30 14:22:26 +01:00
Int64 TickDuration = 50 ;
2013-08-11 19:18:06 +02:00
while ( ! m_ShouldTerminate )
2013-03-11 17:15:34 +00:00
{
2013-11-30 14:22:26 +01:00
Int64 NowTime = Timer . GetNowTime ();
2013-08-19 22:23:25 +02:00
float DeltaTime = ( float )( NowTime - LastTime );
2013-11-30 14:22:26 +01:00
m_World . Tick ( DeltaTime , ( int ) TickDuration );
TickDuration = Timer . GetNowTime () - NowTime ;
2013-08-19 22:23:25 +02:00
2013-11-30 14:22:26 +01:00
if ( TickDuration < msPerTick )
2013-03-11 17:15:34 +00:00
{
2013-08-19 22:23:25 +02:00
// Stretch tick time until it's at least msPerTick
2013-11-30 14:22:26 +01:00
cSleep :: MilliSleep (( unsigned int )( msPerTick - TickDuration ));
2013-03-11 17:15:34 +00:00
}
2013-08-19 22:23:25 +02:00
LastTime = NowTime ;
2012-06-14 13:06:06 +00:00
}
2013-08-11 19:18:06 +02:00
}
2012-06-14 13:06:06 +00:00
2012-07-15 20:36:34 +00:00
2012-06-14 13:06:06 +00:00
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2012-06-14 13:06:06 +00:00
// cWorld:
2012-10-14 17:06:21 +00:00
2014-06-10 20:43:27 +01:00
cWorld :: cWorld ( const AString & a_WorldName , eDimension a_Dimension , const AString & a_OverworldName ) :
2013-08-11 19:18:06 +02:00
m_WorldName ( a_WorldName ),
2014-06-12 18:56:48 +01:00
m_OverworldName ( a_OverworldName ),
2013-08-11 19:18:06 +02:00
m_IniFileName ( m_WorldName + "/world.ini" ),
2013-08-11 20:16:41 +02:00
m_StorageSchema ( "Default" ),
2014-01-30 18:02:37 +01:00
#ifdef __arm__
2014-01-25 21:29:27 +00:00
m_StorageCompressionFactor ( 0 ),
2014-01-25 14:42:26 +00:00
#else
2014-01-17 11:01:14 -08:00
m_StorageCompressionFactor ( 6 ),
2014-01-25 14:42:26 +00:00
#endif
2014-06-12 18:56:48 +01:00
m_Dimension ( a_Dimension ),
2013-12-20 19:10:07 +01:00
m_IsSpawnExplicitlySet ( false ),
2014-08-10 16:46:03 +02:00
m_IsDaylightCycleEnabled ( true ),
2012-11-01 21:38:20 +00:00
m_WorldAgeSecs ( 0 ),
m_TimeOfDaySecs ( 0 ),
m_WorldAge ( 0 ),
m_TimeOfDay ( 0 ),
m_LastTimeUpdate ( 0 ),
2013-12-20 19:10:07 +01:00
m_SkyDarkness ( 0 ),
2014-08-21 21:53:25 +02:00
m_GameMode ( gmNotSet ),
m_bEnabledPVP ( false ),
m_IsDeepSnowEnabled ( false ),
m_ShouldLavaSpawnFire ( true ),
m_VillagersShouldHarvestCrops ( true ),
2014-10-10 15:33:19 +01:00
m_SimulatorManager (),
m_SandSimulator (),
2014-08-21 21:53:25 +02:00
m_WaterSimulator ( NULL ),
2014-10-10 15:33:19 +01:00
m_LavaSimulator ( nullptr ),
m_FireSimulator (),
2014-08-21 21:53:25 +02:00
m_RedstoneSimulator ( NULL ),
m_MaxPlayers ( 10 ),
2014-10-10 15:33:19 +01:00
m_ChunkMap (),
2014-08-21 21:53:25 +02:00
m_bAnimals ( true ),
2012-10-25 19:20:29 +00:00
m_Weather ( eWeather_Sunny ),
2013-08-11 19:18:06 +02:00
m_WeatherInterval ( 24000 ), // Guaranteed 1 day of sunshine at server start :)
2014-08-21 21:53:25 +02:00
m_MaxCactusHeight ( 3 ),
m_MaxSugarcaneHeight ( 4 ),
m_IsCactusBonemealable ( false ),
m_IsCarrotsBonemealable ( true ),
m_IsCropsBonemealable ( true ),
m_IsGrassBonemealable ( true ),
m_IsMelonStemBonemealable ( true ),
m_IsMelonBonemealable ( true ),
m_IsPotatoesBonemealable ( true ),
m_IsPumpkinStemBonemealable ( true ),
m_IsPumpkinBonemealable ( true ),
m_IsSaplingBonemealable ( true ),
m_IsSugarcaneBonemealable ( false ),
m_bCommandBlocksEnabled ( true ),
m_bUseChatPrefixes ( false ),
m_TNTShrapnelLevel ( slNone ),
2014-02-05 09:43:49 -08:00
m_Scoreboard ( this ),
2014-02-23 15:03:40 +02:00
m_MapManager ( this ),
2014-01-10 22:22:54 +01:00
m_GeneratorCallbacks ( * this ),
2014-07-23 21:12:59 +01:00
m_TickThread ( * this )
2012-06-14 13:06:06 +00:00
{
2013-08-11 21:05:44 +02:00
LOGD ( "cWorld::cWorld( \" %s \" )" , a_WorldName . c_str ());
2012-06-14 13:06:06 +00:00
2013-10-09 09:57:48 +02:00
cFile :: CreateFolder ( FILE_IO_PREFIX + m_WorldName );
2014-01-22 15:49:21 +02:00
// Load the scoreboard
cScoreboardSerializer Serializer ( m_WorldName , & m_Scoreboard );
Serializer . Load ();
2012-06-14 13:06:06 +00:00
}
2013-01-12 04:46:01 +00:00
cWorld ::~ cWorld ()
{
2014-06-19 01:49:56 -07:00
delete m_WaterSimulator ; m_WaterSimulator = NULL ;
delete m_LavaSimulator ; m_LavaSimulator = NULL ;
delete m_RedstoneSimulator ; m_RedstoneSimulator = NULL ;
2013-01-12 04:46:01 +00:00
UnloadUnusedChunks ();
m_Storage . WaitForFinish ();
2014-01-22 15:49:21 +02:00
// Unload the scoreboard
cScoreboardSerializer Serializer ( m_WorldName , & m_Scoreboard );
Serializer . Save ();
2014-02-23 15:03:40 +02:00
m_MapManager . SaveMapData ();
2013-01-12 04:46:01 +00:00
}
2013-02-13 19:22:08 +00:00
void cWorld :: CastThunderbolt ( int a_BlockX , int a_BlockY , int a_BlockZ )
{
BroadcastThunderbolt ( a_BlockX , a_BlockY , a_BlockZ );
}
2014-03-02 21:04:01 +01:00
int cWorld :: GetDefaultWeatherInterval ( eWeather a_Weather )
{
switch ( a_Weather )
{
case eWeather_Sunny :
{
return 14400 + ( m_TickRand . randInt () % 4800 ); // 12 - 16 minutes
}
case eWeather_Rain :
{
return 9600 + ( m_TickRand . randInt () % 7200 ); // 8 - 14 minutes
}
case eWeather_ThunderStorm :
{
return 2400 + ( m_TickRand . randInt () % 4800 ); // 2 - 6 minutes
}
2014-04-24 20:25:03 -07:00
}
LOGWARNING ( "%s: Missing default weather interval for weather %d." , __FUNCTION__ , a_Weather );
return - 1 ;
2014-03-02 21:04:01 +01:00
}
2013-02-13 19:22:08 +00:00
void cWorld :: SetWeather ( eWeather a_NewWeather )
2012-06-14 13:06:06 +00:00
{
2013-02-13 19:22:08 +00:00
// Do the plugins agree? Do they want a different weather?
2014-03-02 21:04:01 +01:00
if ( cRoot :: Get () -> GetPluginManager () -> CallHookWeatherChanging ( * this , a_NewWeather ))
{
m_WeatherInterval = GetDefaultWeatherInterval ( m_Weather );
return ;
}
2013-02-13 19:22:08 +00:00
// Set new period for the selected weather:
2014-03-02 21:04:01 +01:00
m_WeatherInterval = GetDefaultWeatherInterval ( a_NewWeather );
// The weather can't be found:
2014-03-03 20:55:04 +01:00
if ( m_WeatherInterval < 0 )
2012-06-14 13:06:06 +00:00
{
2014-03-02 21:04:01 +01:00
return ;
}
2013-02-13 19:22:08 +00:00
m_Weather = a_NewWeather ;
BroadcastWeather ( m_Weather );
// Let the plugins know about the change:
cPluginManager :: Get () -> CallHookWeatherChanged ( * this );
2012-06-14 13:06:06 +00:00
}
2013-02-13 19:22:08 +00:00
void cWorld :: ChangeWeather ( void )
2012-06-14 13:06:06 +00:00
{
2013-02-13 19:22:08 +00:00
// In the next tick the weather will be changed
m_WeatherInterval = 0 ;
2012-06-14 13:06:06 +00:00
}
2012-08-25 21:46:18 +00:00
2012-06-14 13:06:06 +00: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 21:46:18 +00:00
2012-06-14 13:06:06 +00:00
void cWorld :: InitializeSpawn ( void )
{
2014-07-17 19:13:23 +02:00
if ( ! m_IsSpawnExplicitlySet )
2013-11-26 21:54:40 +00:00
{
2014-07-17 19:13:23 +02:00
// Spawn position wasn't already explicitly set, enerate random solid-land coordinate and then write it to the world configuration:
GenerateRandomSpawn ();
2013-11-26 21:54:40 +00:00
cIniFile IniFile ;
IniFile . ReadFile ( m_IniFileName );
IniFile . SetValueF ( "SpawnPosition" , "X" , m_SpawnX );
IniFile . SetValueF ( "SpawnPosition" , "Y" , m_SpawnY );
IniFile . SetValueF ( "SpawnPosition" , "Z" , m_SpawnZ );
IniFile . WriteFile ( m_IniFileName );
}
2014-07-06 23:50:22 +01:00
int ChunkX = 0 , ChunkZ = 0 ;
cChunkDef :: BlockToChunk (( int ) m_SpawnX , ( int ) m_SpawnZ , ChunkX , ChunkZ );
2012-06-14 13:06:06 +00:00
// For the debugging builds, don't make the server build too much world upon start:
2012-11-16 22:06:12 +00:00
#if defined(_DEBUG) || defined(ANDROID_NDK)
2014-05-09 18:31:48 +02:00
const int DefaultViewDist = 9 ;
2012-06-14 13:06:06 +00:00
#else
2014-05-09 18:31:48 +02:00
const int DefaultViewDist = 20 ; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is
2012-06-14 13:06:06 +00:00
#endif // _DEBUG
2014-05-09 18:31:48 +02:00
cIniFile IniFile ;
IniFile . ReadFile ( m_IniFileName );
int ViewDist = IniFile . GetValueSetI ( "SpawnPosition" , "PregenerateDistance" , DefaultViewDist );
IniFile . WriteFile ( m_IniFileName );
2012-06-14 13:06:06 +00:00
2014-08-22 10:35:51 +02:00
LOG ( "Preparing spawn area in world \" %s \" , %d x %d chunks, total %d chunks..." , m_WorldName . c_str (), ViewDist , ViewDist , ViewDist * ViewDist );
2012-06-14 13:06:06 +00:00
for ( int x = 0 ; x < ViewDist ; x ++ )
{
for ( int z = 0 ; z < ViewDist ; z ++ )
{
2014-08-28 11:36:35 +02:00
m_ChunkMap -> TouchChunk ( x + ChunkX - ( ViewDist - 1 ) / 2 , z + ChunkZ - ( ViewDist - 1 ) / 2 ); // Queue the chunk in the generator / loader
2012-06-14 13:06:06 +00:00
}
}
{
// Display progress during this process:
cWorldLoadProgress Progress ( this );
// Wait for the loader to finish loading
2014-01-02 12:32:55 +00:00
m_Storage . WaitForLoadQueueEmpty ();
2012-06-14 13:06:06 +00:00
// Wait for the generator to finish generating
m_Generator . WaitForQueueEmpty ();
2014-01-02 12:32:55 +00:00
// Wait for the loader to finish saving
m_Storage . WaitForSaveQueueEmpty ();
2012-06-14 13:06:06 +00:00
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 ();
}
2013-08-04 16:08:09 +02: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 13:06:06 +00:00
}
2014-07-21 22:49:06 +01:00
void cWorld :: Start ( void )
2012-07-15 20:07:38 +00:00
{
2013-11-26 21:54:40 +00:00
m_SpawnX = 0 ;
2013-08-11 20:16:41 +02:00
m_SpawnY = cChunkDef :: Height ;
2013-11-26 21:54:40 +00:00
m_SpawnZ = 0 ;
2013-08-11 20:16:41 +02:00
m_GameMode = eGameMode_Creative ;
2013-10-25 11:15:44 +02:00
cIniFile IniFile ;
if ( ! IniFile . ReadFile ( m_IniFileName ))
2013-08-11 20:16:41 +02:00
{
LOGWARNING ( "Cannot read world settings from \" %s \" , defaults will be used." , m_IniFileName . c_str ());
2014-07-20 10:46:45 +01:00
// TODO: More descriptions for each key
IniFile . AddHeaderComment ( " This is the per-world configuration file, managing settings such as generators, simulators, and spawn points" );
2014-07-22 17:26:48 +01:00
IniFile . AddKeyComment ( " LinkedWorlds" , "This section governs portal world linkage; leave a value blank to disabled that associated method of teleportation" );
2013-08-11 20:16:41 +02:00
}
2014-06-10 20:43:27 +01:00
2014-07-20 10:46:45 +01:00
// The presence of a configuration value overrides everything
// If no configuration value is found, GetDimension() is written to file and the variable is written to again to ensure that cosmic rays haven't sneakily changed its value
m_Dimension = StringToDimension ( IniFile . GetValueSet ( "General" , "Dimension" , DimensionToString ( GetDimension ())));
2013-11-26 21:54:40 +00:00
2014-07-27 00:39:39 +02:00
m_BroadcastDeathMessages = IniFile . GetValueSetB ( "Broadcasting" , "BroadcastDeathMessages" , true );
m_BroadcastAchievementMessages = IniFile . GetValueSetB ( "Broadcasting" , "BroadcastAchievementMessages" , true );
2013-11-30 15:03:29 +01:00
// Try to find the "SpawnPosition" key and coord values in the world configuration, set the flag if found
2013-11-26 21:54:40 +00:00
int KeyNum = IniFile . FindKey ( "SpawnPosition" );
2013-12-20 19:10:07 +01:00
m_IsSpawnExplicitlySet =
2013-11-30 15:03:29 +01:00
(
( KeyNum >= 0 ) &&
(
2014-07-20 10:46:45 +01:00
( IniFile . FindValue ( KeyNum , "X" ) >= 0 ) &&
( IniFile . FindValue ( KeyNum , "Y" ) >= 0 ) &&
2013-11-30 15:03:29 +01:00
( IniFile . FindValue ( KeyNum , "Z" ) >= 0 )
)
);
2013-11-26 21:54:40 +00:00
2013-12-20 19:10:07 +01:00
if ( m_IsSpawnExplicitlySet )
2013-11-26 21:54:40 +00:00
{
2013-11-30 15:03:29 +01:00
LOGD ( "Spawnpoint explicitly set!" );
2013-11-26 21:54:40 +00:00
m_SpawnX = IniFile . GetValueF ( "SpawnPosition" , "X" , m_SpawnX );
m_SpawnY = IniFile . GetValueF ( "SpawnPosition" , "Y" , m_SpawnY );
m_SpawnZ = IniFile . GetValueF ( "SpawnPosition" , "Z" , m_SpawnZ );
}
2014-03-20 09:28:29 +01:00
m_StorageSchema = IniFile . GetValueSet ( "Storage" , "Schema" , m_StorageSchema );
m_StorageCompressionFactor = IniFile . GetValueSetI ( "Storage" , "CompressionFactor" , m_StorageCompressionFactor );
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_IsDeepSnowEnabled = IniFile . GetValueSetB ( "Physics" , "DeepSnow" , true );
m_ShouldLavaSpawnFire = IniFile . GetValueSetB ( "Physics" , "ShouldLavaSpawnFire" , true );
2014-04-15 13:15:56 +01:00
int TNTShrapnelLevel = IniFile . GetValueSetI ( "Physics" , "TNTShrapnelLevel" , ( int ) slAll );
2014-03-20 09:28:29 +01:00
m_bCommandBlocksEnabled = IniFile . GetValueSetB ( "Mechanics" , "CommandBlocksEnabled" , false );
m_bEnabledPVP = IniFile . GetValueSetB ( "Mechanics" , "PVPEnabled" , true );
m_bUseChatPrefixes = IniFile . GetValueSetB ( "Mechanics" , "UseChatPrefixes" , true );
m_VillagersShouldHarvestCrops = IniFile . GetValueSetB ( "Monsters" , "VillagersShouldHarvestCrops" , true );
2014-08-10 16:48:20 +02:00
m_IsDaylightCycleEnabled = IniFile . GetValueSetB ( "General" , "IsDaylightCycleEnabled" , true );
2014-03-20 09:28:29 +01:00
int GameMode = IniFile . GetValueSetI ( "General" , "Gamemode" , ( int ) m_GameMode );
2014-06-04 20:52:54 +01:00
int Weather = IniFile . GetValueSetI ( "General" , "Weather" , ( int ) m_Weather );
2014-08-01 22:15:14 +01:00
2014-07-22 10:33:16 +01:00
if ( GetDimension () == dimOverworld )
{
2014-07-23 21:12:59 +01:00
m_NetherWorldName = IniFile . GetValueSet ( "LinkedWorlds" , "NetherWorldName" , GetName () + "_nether" );
m_EndWorldName = IniFile . GetValueSet ( "LinkedWorlds" , "EndWorldName" , GetName () + "_end" );
2014-07-22 10:33:16 +01:00
}
2014-07-20 10:46:45 +01:00
else
{
m_OverworldName = IniFile . GetValueSet ( "LinkedWorlds" , "OverworldName" , GetLinkedOverworldName ());
}
2014-03-20 09:28:29 +01:00
// Adjust the enum-backed variables into their respective bounds:
m_GameMode = ( eGameMode ) Clamp ( GameMode , ( int ) gmSurvival , ( int ) gmAdventure );
m_TNTShrapnelLevel = ( eShrapnelLevel ) Clamp ( TNTShrapnelLevel , ( int ) slNone , ( int ) slAll );
2014-06-04 20:52:54 +01:00
m_Weather = ( eWeather ) Clamp ( Weather , ( int ) wSunny , ( int ) wStorm );
2013-08-11 20:16:41 +02:00
2014-07-21 22:49:06 +01:00
InitialiseGeneratorDefaults ( IniFile );
InitialiseAndLoadMobSpawningValues ( IniFile );
2014-08-01 22:15:14 +01:00
SetTimeOfDay ( IniFile . GetValueSetI ( "General" , "TimeInTicks" , m_TimeOfDay ));
2013-08-11 20:16:41 +02:00
2014-10-10 15:33:19 +01:00
m_ChunkMap = make_unique < cChunkMap > ( this );
2013-08-11 20:16:41 +02:00
m_LastSave = 0 ;
m_LastUnload = 0 ;
2013-09-10 22:02:46 +02:00
// preallocate some memory for ticking blocks so we don't need to allocate that often
2013-08-11 20:16:41 +02:00
m_BlockTickQueue . reserve ( 1000 );
m_BlockTickQueueCopy . reserve ( 1000 );
// Simulators:
2014-10-10 15:33:19 +01:00
m_SimulatorManager = make_unique < cSimulatorManager > ( * this );
2013-08-11 20:16:41 +02:00
m_WaterSimulator = InitializeFluidSimulator ( IniFile , "Water" , E_BLOCK_WATER , E_BLOCK_STATIONARY_WATER );
m_LavaSimulator = InitializeFluidSimulator ( IniFile , "Lava" , E_BLOCK_LAVA , E_BLOCK_STATIONARY_LAVA );
2014-10-10 15:33:19 +01:00
m_SandSimulator = make_unique < cSandSimulator > ( * this , IniFile );
m_FireSimulator = make_unique < cFireSimulator > ( * this , IniFile );
2014-02-07 22:13:55 +01:00
m_RedstoneSimulator = InitializeRedstoneSimulator ( IniFile );
2013-08-11 20:16:41 +02:00
2014-02-08 11:20:00 +01:00
// Water, Lava and Redstone simulators get registered in their initialize function.
2014-10-10 15:33:19 +01:00
m_SimulatorManager -> RegisterSimulator ( m_SandSimulator . get (), 1 );
m_SimulatorManager -> RegisterSimulator ( m_FireSimulator . get (), 1 );
2013-08-11 20:16:41 +02:00
m_Lighting . Start ( this );
2014-07-21 15:19:48 +02:00
m_Storage . Start ( this , m_StorageSchema , m_StorageCompressionFactor );
2014-01-10 22:22:54 +01:00
m_Generator . Start ( m_GeneratorCallbacks , m_GeneratorCallbacks , IniFile );
2013-08-11 20:16:41 +02:00
m_ChunkSender . Start ( this );
m_TickThread . Start ();
2013-09-08 02:47:02 +02:00
// Init of the spawn monster time (as they are supposed to have different spawn rate)
2013-10-20 10:23:30 +02: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-08 02:47:02 +02:00
2014-03-04 18:40:55 +01:00
m_MapManager . LoadMapData ();
2013-09-08 02:47:02 +02:00
2013-08-11 20:16:41 +02:00
// Save any changes that the defaults may have done to the ini file:
2013-10-25 11:15:44 +02:00
if ( ! IniFile . WriteFile ( m_IniFileName ))
2013-08-11 20:16:41 +02:00
{
LOGWARNING ( "Could not write world config to %s" , m_IniFileName . c_str ());
}
}
2013-11-26 21:54:40 +00:00
void cWorld :: GenerateRandomSpawn ( void )
{
LOGD ( "Generating random spawnpoint..." );
2014-09-26 13:04:34 +02:00
// Look for a spawn point at most 100 chunks away from map center:
for ( int i = 0 ; i < 100 ; i ++ )
2013-11-26 21:54:40 +00:00
{
2014-09-26 13:04:34 +02:00
EMCSBiome biome = GetBiomeAt (( int ) m_SpawnX , ( int ) m_SpawnZ );
if (
( biome != biOcean ) && ( biome != biFrozenOcean ) && // The biome is acceptable (don't want a small ocean island)
! IsBlockWaterOrIce ( GetBlock (( int ) m_SpawnX , GetHeight (( int ) m_SpawnX , ( int ) m_SpawnZ ), ( int ) m_SpawnZ )) // The terrain is acceptable (don't want to spawn inside a lake / river)
)
{
// A good spawnpoint was found
break ;
}
// Try a neighboring chunk:
2014-07-17 19:13:23 +02:00
if (( GetTickRandomNumber ( 4 ) % 2 ) == 0 ) // Randomise whether to increment X or Z coords
2013-11-26 21:54:40 +00:00
{
m_SpawnX += cChunkDef :: Width ;
}
else
{
m_SpawnZ += cChunkDef :: Width ;
}
2014-09-26 13:04:34 +02:00
} // for i - 100*
2013-11-26 21:54:40 +00:00
2014-07-17 19:13:23 +02:00
m_SpawnY = ( double ) GetHeight (( int ) m_SpawnX , ( int ) m_SpawnZ ) + 1.6f ; // 1.6f to accomodate player height
2013-11-26 21:54:40 +00:00
2014-09-26 13:04:34 +02:00
LOGINFO ( "Generated random spawnpoint position {%i, %i, %i}" , ( int ) m_SpawnX , ( int ) m_SpawnY , ( int ) m_SpawnZ );
2013-11-26 21:54:40 +00:00
}
2014-04-25 16:55:38 -07:00
eWeather cWorld :: ChooseNewWeather ()
{
// Pick a new weather. Only reasonable transitions allowed:
switch ( m_Weather )
{
case eWeather_Sunny :
case eWeather_ThunderStorm : return eWeather_Rain ;
case eWeather_Rain :
{
// 1/8 chance of turning into a thunderstorm
return (( m_TickRand . randInt () % 256 ) < 32 ) ? eWeather_ThunderStorm : eWeather_Sunny ;
}
}
LOGWARNING ( "Unknown current weather: %d. Setting sunny." , m_Weather );
ASSERT ( ! "Unknown weather" );
return eWeather_Sunny ;
}
2014-07-21 22:49:06 +01:00
void cWorld :: InitialiseGeneratorDefaults ( cIniFile & a_IniFile )
{
switch ( GetDimension ())
{
case dimEnd :
{
a_IniFile . GetValueSet ( "Generator" , "BiomeGen" , "Constant" );
a_IniFile . GetValueSet ( "Generator" , "ConstantBiome" , "End" );
a_IniFile . GetValueSet ( "Generator" , "HeightGen" , "Biomal" );
a_IniFile . GetValueSet ( "Generator" , "CompositionGen" , "End" );
break ;
}
case dimOverworld :
{
a_IniFile . GetValueSet ( "Generator" , "BiomeGen" , "MultiStepMap" );
a_IniFile . GetValueSet ( "Generator" , "HeightGen" , "DistortedHeightmap" );
a_IniFile . GetValueSet ( "Generator" , "CompositionGen" , "DistortedHeightmap" );
a_IniFile . GetValueSet ( "Generator" , "Finishers" , "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, PreSimulator" );
break ;
}
case dimNether :
{
a_IniFile . GetValueSet ( "Generator" , "BiomeGen" , "Constant" );
a_IniFile . GetValueSet ( "Generator" , "ConstantBiome" , "Nether" );
a_IniFile . GetValueSet ( "Generator" , "HeightGen" , "Flat" );
a_IniFile . GetValueSet ( "Generator" , "FlatHeight" , "128" );
a_IniFile . GetValueSet ( "Generator" , "CompositionGen" , "Nether" );
a_IniFile . GetValueSet ( "Generator" , "Finishers" , "WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherForts, PreSimulator" );
a_IniFile . GetValueSet ( "Generator" , "BottomLavaHeight" , "30" );
break ;
}
2014-09-08 19:15:29 +01:00
case dimNotSet :
{
2014-09-08 21:31:47 +01:00
ASSERT ( ! "Dimension not set" );
2014-09-08 19:15:29 +01:00
break ;
}
2014-07-21 22:49:06 +01:00
}
}
void cWorld :: InitialiseAndLoadMobSpawningValues ( cIniFile & a_IniFile )
{
AString 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 ;
2014-09-08 21:31:47 +01:00
case dimNotSet : ASSERT ( ! "Dimension not set" ); break ;
2014-07-21 22:49:06 +01:00
}
m_bAnimals = a_IniFile . GetValueSetB ( "Monsters" , "AnimalsOn" , true );
AString AllMonsters = a_IniFile . GetValueSet ( "Monsters" , "Types" , DefaultMonsters );
if ( ! m_bAnimals )
{
return ;
}
AStringVector SplitList = StringSplitAndTrim ( AllMonsters , "," );
for ( AStringVector :: const_iterator itr = SplitList . begin (), end = SplitList . end (); itr != end ; ++ itr )
{
2014-09-17 18:40:10 +01:00
eMonsterType ToAdd = cMonster :: StringToMobType ( * itr );
if ( ToAdd != mtInvalidType )
2014-07-21 22:49:06 +01:00
{
m_AllowedMobs . insert ( ToAdd );
LOGD ( "Allowed mob: %s" , itr -> c_str ());
}
else
{
LOG ( "World \" %s \" : Unknown mob type: %s" , m_WorldName . c_str (), itr -> c_str ());
}
}
}
2013-08-11 20:16:41 +02:00
void cWorld :: Stop ( void )
2012-07-15 20:07:38 +00:00
{
2013-08-14 19:56:29 +02: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 ();
}
2014-06-04 20:00:55 +01:00
// Write settings to file; these are all plugin changeable values - keep updated!
cIniFile IniFile ;
IniFile . ReadFile ( m_IniFileName );
2014-07-22 17:26:48 +01:00
if ( GetDimension () == dimOverworld )
2014-06-04 20:00:55 +01:00
{
2014-07-18 20:12:27 +01:00
IniFile . SetValue ( "LinkedWorlds" , "NetherWorldName" , m_NetherWorldName );
IniFile . SetValue ( "LinkedWorlds" , "EndWorldName" , m_EndWorldName );
2014-06-04 20:00:55 +01:00
}
2014-06-10 20:43:27 +01:00
else
{
2014-07-18 20:12:27 +01:00
IniFile . SetValue ( "LinkedWorlds" , "OverworldName" , m_OverworldName );
2014-06-10 20:43:27 +01:00
}
2014-06-04 20:00:55 +01:00
IniFile . SetValueI ( "Physics" , "TNTShrapnelLevel" , ( int ) m_TNTShrapnelLevel );
IniFile . SetValueB ( "Mechanics" , "CommandBlocksEnabled" , m_bCommandBlocksEnabled );
IniFile . SetValueB ( "Mechanics" , "UseChatPrefixes" , m_bUseChatPrefixes );
2014-08-10 16:48:20 +02:00
IniFile . SetValueB ( "General" , "IsDaylightCycleEnabled" , m_IsDaylightCycleEnabled );
2014-06-04 20:52:54 +01:00
IniFile . SetValueI ( "General" , "Weather" , ( int ) m_Weather );
2014-06-05 17:58:29 +01:00
IniFile . SetValueI ( "General" , "TimeInTicks" , m_TimeOfDay );
2014-06-04 20:00:55 +01:00
IniFile . WriteFile ( m_IniFileName );
2013-08-14 19:56:29 +02:00
2013-08-11 20:16:41 +02:00
m_TickThread . Stop ();
m_Lighting . Stop ();
2012-07-15 20:07:38 +00:00
m_Generator . Stop ();
m_ChunkSender . Stop ();
2013-08-11 20:16:41 +02:00
m_Storage . Stop ();
2012-07-15 20:07:38 +00:00
}
2013-11-30 14:22:26 +01:00
void cWorld :: Tick ( float a_Dt , int a_LastTickDurationMSec )
2012-06-14 13:06:06 +00:00
{
2013-08-19 09:28:22 +02:00
// Call the plugins
2013-11-30 14:22:26 +01:00
cPluginManager :: Get () -> CallHookWorldTick ( * this , a_Dt , a_LastTickDurationMSec );
2013-08-19 09:28:22 +02:00
2014-07-24 18:32:05 +02:00
// Set any chunk data that has been queued for setting:
cSetChunkDataPtrs SetChunkDataQueue ;
{
cCSLock Lock ( m_CSSetChunkDataQueue );
std :: swap ( SetChunkDataQueue , m_SetChunkDataQueue );
}
for ( cSetChunkDataPtrs :: iterator itr = SetChunkDataQueue . begin (), end = SetChunkDataQueue . end (); itr != end ; ++ itr )
{
SetChunkData ( ** itr );
} // for itr - SetChunkDataQueue[]
2014-08-07 01:07:32 +02:00
2012-11-01 21:38:20 +00:00
m_WorldAgeSecs += ( double ) a_Dt / 1000.0 ;
2014-08-07 01:07:32 +02:00
m_WorldAge = ( Int64 )( m_WorldAgeSecs * 20.0 );
2012-06-14 13:06:06 +00:00
2014-08-10 16:46:03 +02:00
if ( m_IsDaylightCycleEnabled )
2012-06-14 13:06:06 +00:00
{
2014-08-07 01:07:32 +02:00
// We need sub-tick precision here, that's why we store the time in seconds and calculate ticks off of it
m_TimeOfDaySecs += ( double ) a_Dt / 1000.0 ;
2012-11-01 21:38:20 +00:00
2014-08-07 01:07:32 +02:00
// Wrap time of day each 20 minutes (1200 seconds)
if ( m_TimeOfDaySecs > 1200.0 )
{
m_TimeOfDaySecs -= 1200.0 ;
}
2012-11-01 21:38:20 +00:00
2014-09-08 19:07:45 +01:00
m_TimeOfDay = static_cast < int > ( m_TimeOfDaySecs * 20.0 );
2013-10-29 10:44:51 -06:00
2014-08-07 01:07:32 +02:00
// Updates the sky darkness based on current time of day
UpdateSkyDarkness ();
// Broadcast time update every 40 ticks (2 seconds)
if ( m_LastTimeUpdate < m_WorldAge - 40 )
{
BroadcastTimeUpdate ();
m_LastTimeUpdate = m_WorldAge ;
}
2012-06-14 13:06:06 +00:00
}
2014-06-06 22:31:16 +02:00
// Add entities waiting in the queue to be added:
{
cCSLock Lock ( m_CSEntitiesToAdd );
for ( cEntityList :: iterator itr = m_EntitiesToAdd . begin (), end = m_EntitiesToAdd . end (); itr != end ; ++ itr )
{
( * itr ) -> SetWorld ( this );
m_ChunkMap -> AddEntity ( * itr );
}
m_EntitiesToAdd . clear ();
}
2014-06-08 21:58:08 +02:00
// Add players waiting in the queue to be added:
AddQueuedPlayers ();
2013-04-13 21:02:10 +00:00
m_ChunkMap -> Tick ( a_Dt );
2012-07-15 20:36:34 +00:00
2013-08-13 22:45:29 +02:00
TickClients ( a_Dt );
2013-09-16 09:25:23 +02:00
TickQueuedBlocks ();
2013-08-11 21:05:44 +02:00
TickQueuedTasks ();
2014-01-14 12:17:03 -08:00
TickScheduledTasks ();
2012-06-14 13:06:06 +00:00
GetSimulatorManager () -> Simulate ( a_Dt );
TickWeather ( a_Dt );
2014-01-26 06:20:39 -08:00
m_ChunkMap -> FastSetQueuedBlocks ();
2012-06-14 13:06:06 +00:00
2014-07-17 19:13:23 +02:00
if ( m_WorldAge - m_LastSave > 60 * 5 * 20 ) // Save each 5 minutes
2012-06-14 13:06:06 +00:00
{
SaveAllChunks ();
}
2014-07-17 19:13:23 +02:00
if ( m_WorldAge - m_LastUnload > 10 * 20 ) // Unload every 10 seconds
2012-06-14 13:06:06 +00:00
{
UnloadUnusedChunks ();
}
2013-09-08 01:43:55 +02:00
TickMobs ( a_Dt );
2012-06-14 13:06:06 +00:00
}
2013-02-13 19:22:08 +00:00
void cWorld :: TickWeather ( float a_Dt )
2012-06-14 13:06:06 +00:00
{
2013-12-22 14:55:24 +00:00
UNUSED ( a_Dt );
2013-06-24 16:50:32 +00:00
// There are no weather changes anywhere but in the Overworld:
if ( GetDimension () != dimOverworld )
{
return ;
}
2013-02-13 19:22:08 +00:00
if ( m_WeatherInterval > 0 )
2012-06-14 13:06:06 +00:00
{
2013-02-13 19:22:08 +00:00
// Not yet, wait for the weather period to end
m_WeatherInterval -- ;
2012-06-14 13:06:06 +00:00
}
2013-02-13 19:22:08 +00:00
else
2012-06-14 13:06:06 +00:00
{
2013-02-13 19:22:08 +00:00
// Change weather:
2014-04-25 16:55:38 -07:00
SetWeather ( ChooseNewWeather ());
}
2012-06-14 13:06:06 +00:00
2013-02-13 19:22:08 +00:00
if ( m_Weather == eWeather_ThunderStorm )
2012-06-14 13:06:06 +00:00
{
2013-02-13 19:22:08 +00:00
// 0.5% chance per tick of thunderbolt
if ( m_TickRand . randInt () % 199 == 0 )
2012-06-14 13:06:06 +00:00
{
2013-02-13 19:22:08 +00:00
CastThunderbolt ( 0 , 0 , 0 ); // TODO: find random possitions near players to cast thunderbolts.
2012-06-14 13:06:06 +00:00
}
}
}
2013-09-08 01:43:55 +02:00
void cWorld :: TickMobs ( float a_Dt )
2012-06-14 13:06:06 +00:00
{
2013-10-22 18:30:26 +02:00
// _X 2013_10_22: This is a quick fix for #283 - the world needs to be locked while ticking mobs
cWorld :: cLock Lock ( * this );
2013-10-20 14:16:21 +02:00
// before every Mob action, we have to count them depending on the distance to players, on their family ...
2013-09-07 22:19:56 +02:00
cMobCensus MobCensus ;
m_ChunkMap -> CollectMobCensus ( MobCensus );
2013-10-20 14:00:45 +02:00
if ( m_bAnimals )
2012-06-14 13:06:06 +00:00
{
2013-10-20 14:16:21 +02:00
// Spawning is enabled, spawn now:
static const cMonster :: eFamily AllFamilies [] =
{
cMonster :: mfHostile ,
cMonster :: mfPassive ,
cMonster :: mfAmbient ,
cMonster :: mfWater ,
} ;
2013-12-22 14:55:24 +00:00
for ( size_t i = 0 ; i < ARRAYCOUNT ( AllFamilies ); i ++ )
2013-06-25 06:36:59 +00:00
{
2013-10-20 14:16:21 +02:00
cMonster :: eFamily Family = AllFamilies [ i ];
2013-10-24 16:45:13 +02:00
int SpawnDelay = cMonster :: GetSpawnDelay ( Family );
2013-10-20 14:00:45 +02:00
if (
2013-10-24 16:45:13 +02:00
( m_LastSpawnMonster [ Family ] > m_WorldAge - SpawnDelay ) || // Not reached the needed ticks before the next round
2013-10-20 14:16:21 +02:00
MobCensus . IsCapped ( Family )
2013-10-20 14:00:45 +02:00
)
{
continue ;
}
2013-10-20 14:16:21 +02:00
m_LastSpawnMonster [ Family ] = m_WorldAge ;
cMobSpawner Spawner ( Family , m_AllowedMobs );
2013-10-20 14:00:45 +02:00
if ( Spawner . CanSpawnAnything ())
2013-08-16 10:48:19 +02:00
{
2013-10-20 14:00:45 +02:00
m_ChunkMap -> SpawnMobs ( Spawner );
// do the spawn
2014-04-18 21:09:44 +02:00
for ( cMobSpawner :: tSpawnedContainer :: const_iterator itr2 = Spawner . getSpawned (). begin (); itr2 != Spawner . getSpawned (). end (); ++ itr2 )
2013-08-16 10:48:19 +02:00
{
2013-10-20 14:00:45 +02:00
SpawnMobFinalize ( * itr2 );
2013-08-16 10:48:19 +02:00
}
2013-10-20 14:00:45 +02:00
}
2014-07-17 19:13:23 +02:00
} // for i - AllFamilies[]
} // if (Spawning enabled)
2012-06-14 13:06:06 +00:00
2013-09-08 02:47:02 +02:00
// move close mobs
2013-10-18 20:02:53 +02: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)
2014-07-20 23:10:31 +02:00
for ( cMobProximityCounter :: tDistanceToMonster :: const_iterator itr = allCloseEnoughToMoveMobs . m_Begin ; itr != allCloseEnoughToMoveMobs . m_End ; ++ itr )
2012-06-14 13:06:06 +00:00
{
2013-10-18 20:02:53 +02:00
itr -> second . m_Monster . Tick ( a_Dt , itr -> second . m_Chunk );
2013-09-08 02:47:02 +02:00
}
2012-06-14 13:06:06 +00:00
2013-09-08 02:47:02 +02:00
// remove too far mobs
2013-10-18 20:02:53 +02: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)
2014-07-20 23:10:31 +02:00
for ( cMobProximityCounter :: tDistanceToMonster :: const_iterator itr = allTooFarMobs . m_Begin ; itr != allTooFarMobs . m_End ; ++ itr )
2012-06-14 13:06:06 +00:00
{
2013-09-08 02:47:02 +02:00
itr -> second . m_Monster . Destroy ( true );
2012-06-14 13:06:06 +00:00
}
}
2013-08-11 21:05:44 +02: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 22:24:59 +02:00
for ( cTasks :: iterator itr = Tasks . begin (), end = Tasks . end (); itr != end ; ++ itr )
2013-08-11 21:05:44 +02:00
{
( * itr ) -> Run ( * this );
} // for itr - m_Tasks[]
}
2014-01-19 23:49:19 +01:00
void cWorld :: TickScheduledTasks ( void )
2014-01-14 12:17:03 -08:00
{
2014-10-12 12:18:52 +01:00
// Move the tasks to be executed to a seperate vector to avoid deadlocks on accessing m_Tasks
2014-01-19 23:49:19 +01:00
cScheduledTasks Tasks ;
2014-01-14 12:17:03 -08:00
{
cCSLock Lock ( m_CSScheduledTasks );
2014-10-11 14:43:06 +01:00
auto WorldAge = m_WorldAge ;
2014-10-10 15:33:19 +01:00
std :: move (
m_ScheduledTasks . begin (),
std :: find_if (
m_ScheduledTasks . begin (),
m_ScheduledTasks . end (),
2014-10-12 12:18:52 +01:00
[ WorldAge ] ( std :: unique_ptr < cScheduledTask > & Task ) { return Task -> m_TargetTick < WorldAge ;}),
2014-10-10 15:33:19 +01:00
std :: back_inserter ( Tasks ));
2014-01-14 12:17:03 -08:00
}
// Execute and delete each task:
2014-01-19 23:49:19 +01:00
for ( cScheduledTasks :: iterator itr = Tasks . begin (), end = Tasks . end (); itr != end ; ++ itr )
2014-01-14 12:17:03 -08:00
{
2014-01-19 23:49:19 +01:00
( * itr ) -> m_Task -> Run ( * this );
2014-01-14 12:17:03 -08:00
} // for itr - m_Tasks[]
}
2013-08-11 21:05:44 +02:00
2013-08-13 22:45:29 +02:00
void cWorld :: TickClients ( float a_Dt )
{
cClientHandleList RemoveClients ;
{
cCSLock Lock ( m_CSClients );
2013-08-14 13:43:55 +02: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 )
{
2014-07-22 17:26:48 +01:00
ASSERT ( std :: find ( m_Clients . begin (), m_Clients . end (), * itr ) == m_Clients . end ());
2013-08-14 13:43:55 +02:00
m_Clients . push_back ( * itr );
} // for itr - m_ClientsToRemove[]
m_ClientsToAdd . clear ();
2013-08-13 22:45:29 +02: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 ;
2014-07-17 19:13:23 +02:00
} // for itr - RemoveClients[]
2013-08-13 22:45:29 +02:00
}
2013-10-30 23:33:42 +01:00
void cWorld :: UpdateSkyDarkness ( void )
{
int TempTime = ( int ) m_TimeOfDay ;
if ( TempTime <= TIME_SUNSET )
{
m_SkyDarkness = 0 ;
}
else if ( TempTime <= TIME_NIGHT_START )
{
2014-09-08 19:15:29 +01:00
m_SkyDarkness = static_cast < NIBBLETYPE > (( TIME_NIGHT_START - TempTime ) / TIME_SPAWN_DIVISOR );
2013-10-30 23:33:42 +01:00
}
else if ( TempTime <= TIME_NIGHT_END )
{
m_SkyDarkness = 8 ;
}
else
{
2014-09-08 19:15:29 +01:00
m_SkyDarkness = static_cast < NIBBLETYPE > (( TIME_SUNRISE - TempTime ) / TIME_SPAWN_DIVISOR );
2013-10-30 23:33:42 +01:00
}
}
2013-02-28 07:42:45 +00: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 20:47:58 +00: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 );
}
2013-11-20 21:53:29 +01:00
bool cWorld :: ForEachBlockEntityInChunk ( int a_ChunkX , int a_ChunkZ , cBlockEntityCallback & a_Callback )
{
return m_ChunkMap -> ForEachBlockEntityInChunk ( a_ChunkX , a_ChunkZ , a_Callback );
}
2012-06-17 19:58:39 +00: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 17:16:33 +00: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 14:39:04 +00: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 19:58:39 +00: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 18:12:22 +02: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-18 02:42:45 +00:00
{
2013-08-09 14:58:43 +02:00
if ( cPluginManager :: Get () -> CallHookExploding ( * this , a_ExplosionSize , a_CanCauseFire , a_BlockX , a_BlockY , a_BlockZ , a_Source , a_SourceData ) || ( a_ExplosionSize <= 0 ))
{
return ;
}
2014-08-29 13:44:10 +01:00
// TODO: Implement block hardiness
2013-06-18 08:24:34 +00:00
Vector3d explosion_pos = Vector3d ( a_BlockX , a_BlockY , a_BlockZ );
2013-06-18 19:09:51 +00:00
cVector3iArray BlocksAffected ;
2013-09-07 18:12:22 +02:00
m_ChunkMap -> DoExplosionAt ( a_ExplosionSize , a_BlockX , a_BlockY , a_BlockZ , BlocksAffected );
2014-07-13 02:08:02 +02:00
BroadcastSoundEffect ( "random.explode" , ( double ) a_BlockX , ( double ) a_BlockY , ( double ) a_BlockZ , 1.0f , 0.6f );
2014-08-29 13:44:10 +01:00
2013-04-18 02:42:45 +00:00
{
cCSLock Lock ( m_CSPlayers );
for ( cPlayerList :: iterator itr = m_Players . begin (); itr != m_Players . end (); ++ itr )
{
cClientHandle * ch = ( * itr ) -> GetClientHandle ();
2014-08-29 13:44:10 +01:00
if ( ch == NULL )
2013-04-18 02:42:45 +00:00
{
continue ;
}
2014-08-29 13:44:10 +01:00
2013-04-18 02:42:45 +00:00
Vector3d distance_explosion = ( * itr ) -> GetPosition () - explosion_pos ;
if ( distance_explosion . SqrLength () < 4096.0 )
{
2014-08-29 13:44:10 +01:00
double real_distance = std :: max ( 0.004 , distance_explosion . Length ());
2013-04-18 02:42:45 +00:00
double power = a_ExplosionSize / real_distance ;
if ( power <= 1 )
2013-04-19 08:21:39 +00:00
{
2013-04-18 02:42:45 +00:00
power = 0 ;
2013-04-19 08:21:39 +00:00
}
2013-04-18 02:42:45 +00:00
distance_explosion . Normalize ();
distance_explosion *= power ;
2013-08-09 14:58:43 +02:00
ch -> SendExplosion ( a_BlockX , a_BlockY , a_BlockZ , ( float ) a_ExplosionSize , BlocksAffected , distance_explosion );
2013-04-18 02:42:45 +00:00
}
}
}
2014-08-29 13:44:10 +01:00
2013-08-09 14:58:43 +02:00
cPluginManager :: Get () -> CallHookExploded ( * this , a_ExplosionSize , a_CanCauseFire , a_BlockX , a_BlockY , a_BlockZ , a_Source , a_SourceData );
2013-04-18 02:42:45 +00:00
}
2013-11-20 21:53:29 +01:00
bool cWorld :: DoWithBlockEntityAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cBlockEntityCallback & a_Callback )
{
return m_ChunkMap -> DoWithBlockEntityAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback );
}
2014-07-30 22:19:51 +02:00
bool cWorld :: DoWithBeaconAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cBeaconCallback & a_Callback )
{
return m_ChunkMap -> DoWithBeaconAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback );
}
2012-06-17 19:58:39 +00: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 17:16:33 +00: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 14:39:04 +00: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 17:16:33 +00:00
2012-06-17 19:58:39 +00: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 );
}
2013-12-14 16:52:22 +00:00
bool cWorld :: DoWithNoteBlockAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cNoteBlockCallback & a_Callback )
{
return m_ChunkMap -> DoWithNoteBlockAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback );
}
2014-01-18 15:16:47 +02:00
bool cWorld :: DoWithCommandBlockAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cCommandBlockCallback & a_Callback )
{
return m_ChunkMap -> DoWithCommandBlockAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback );
}
2014-03-07 11:44:16 +01:00
bool cWorld :: DoWithMobHeadAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cMobHeadCallback & a_Callback )
2014-02-18 21:40:02 +01:00
{
2014-03-07 11:44:16 +01:00
return m_ChunkMap -> DoWithMobHeadAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback );
2014-02-18 21:40:02 +01:00
}
2014-03-07 01:30:34 +01:00
bool cWorld :: DoWithFlowerPotAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cFlowerPotCallback & a_Callback )
2014-02-18 21:40:02 +01:00
{
2014-03-07 01:30:34 +01:00
return m_ChunkMap -> DoWithFlowerPotAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback );
2014-02-18 21:40:02 +01:00
}
2012-06-17 19:58:39 +00:00
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 20:26:50 +02: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 19:05:11 +00:00
void cWorld :: GrowTree ( int a_X , int a_Y , int a_Z )
2012-06-14 13:06:06 +00: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 22:11:02 +02:00
void cWorld :: GrowTreeFromSapling ( int a_X , int a_Y , int a_Z , NIBBLETYPE a_SaplingMeta )
2012-06-14 13:06:06 +00:00
{
cNoise Noise ( m_Generator . GetSeed ());
2012-07-15 13:33:43 +00:00
sSetBlockVector Logs , Other ;
2012-06-14 13:06:06 +00:00
switch ( a_SaplingMeta & 0x07 )
{
2014-02-19 19:18:40 +01: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 ;
case E_META_SAPLING_ACACIA : GetAcaciaTreeImage ( a_X , a_Y , a_Z , Noise , ( int )( m_WorldAge & 0xffffffff ), Logs , Other ); break ;
case E_META_SAPLING_DARK_OAK : GetDarkoakTreeImage ( a_X , a_Y , a_Z , Noise , ( int )( m_WorldAge & 0xffffffff ), Logs , Other ); break ;
2012-06-14 13:06:06 +00:00
}
2012-07-15 13:33:43 +00:00
Other . insert ( Other . begin (), Logs . begin (), Logs . end ());
Logs . clear ();
2012-10-03 19:01:34 +00:00
GrowTreeImage ( Other );
2012-06-14 13:06:06 +00:00
}
void cWorld :: GrowTreeByBiome ( int a_X , int a_Y , int a_Z )
{
cNoise Noise ( m_Generator . GetSeed ());
2012-07-15 13:33:43 +00:00
sSetBlockVector Logs , Other ;
2014-02-03 12:26:17 -08:00
GetTreeImageByBiome ( a_X , a_Y , a_Z , Noise , ( int )( m_WorldAge & 0xffffffff ), GetBiomeAt ( a_X , a_Z ), Logs , Other );
2012-07-15 13:33:43 +00:00
Other . insert ( Other . begin (), Logs . begin (), Logs . end ());
Logs . clear ();
GrowTreeImage ( Other );
2012-06-14 13:06:06 +00: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 08:52:11 +00:00
bool cWorld :: GrowRipePlant ( int a_BlockX , int a_BlockY , int a_BlockZ , bool a_IsByBonemeal )
2012-06-14 13:06:06 +00:00
{
BLOCKTYPE BlockType ;
NIBBLETYPE BlockMeta ;
GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , BlockType , BlockMeta );
switch ( BlockType )
{
2013-04-05 13:45:00 +00:00
case E_BLOCK_CARROTS :
{
if ( a_IsByBonemeal && ! m_IsCarrotsBonemealable )
{
return false ;
}
if ( BlockMeta < 7 )
{
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , 7 );
2013-12-07 23:45:33 +00:00
BroadcastSoundParticleEffect ( 2005 , a_BlockX , a_BlockY , a_BlockZ , 0 );
2013-04-05 13:45:00 +00:00
}
return true ;
}
2012-06-14 13:06:06 +00:00
case E_BLOCK_CROPS :
{
2013-04-05 13:45:00 +00:00
if ( a_IsByBonemeal && ! m_IsCropsBonemealable )
2012-06-14 13:06:06 +00:00
{
return false ;
}
if ( BlockMeta < 7 )
{
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , 7 );
2013-12-07 23:45:33 +00:00
BroadcastSoundParticleEffect ( 2005 , a_BlockX , a_BlockY , a_BlockZ , 0 );
2012-06-14 13:06:06 +00:00
}
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 );
2013-12-07 23:45:33 +00:00
BroadcastSoundParticleEffect ( 2005 , a_BlockX , a_BlockY , a_BlockZ , 0 );
2012-06-14 13:06:06 +00:00
}
else
{
if ( a_IsByBonemeal && ! m_IsMelonBonemealable )
{
return false ;
}
GrowMelonPumpkin ( a_BlockX , a_BlockY , a_BlockZ , BlockType );
}
return true ;
}
2013-04-05 13:45:00 +00:00
case E_BLOCK_POTATOES :
{
if ( a_IsByBonemeal && ! m_IsPotatoesBonemealable )
{
return false ;
}
if ( BlockMeta < 7 )
{
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , 7 );
2013-12-07 23:45:33 +00:00
BroadcastSoundParticleEffect ( 2005 , a_BlockX , a_BlockY , a_BlockZ , 0 );
2013-04-05 13:45:00 +00:00
}
return true ;
}
2012-06-14 13:06:06 +00: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 );
2013-12-07 23:45:33 +00:00
BroadcastSoundParticleEffect ( 2005 , a_BlockX , a_BlockY , a_BlockZ , 0 );
2012-06-14 13:06:06 +00:00
}
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 ++ )
{
2014-09-08 18:56:27 +01:00
int OfsX = static_cast < int > ( r1 . randInt ( 3 ) + r1 . randInt ( 3 ) + r1 . randInt ( 3 ) + r1 . randInt ( 3 )) / 2 - 3 ;
int OfsY = static_cast < int > ( r1 . randInt ( 3 ) + r1 . randInt ( 3 )) - 3 ;
int OfsZ = static_cast < int > ( r1 . randInt ( 3 ) + r1 . randInt ( 3 ) + r1 . randInt ( 3 ) + r1 . randInt ( 3 )) / 2 - 3 ;
2012-06-14 13:06:06 +00:00
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 08:52:11 +00:00
} // switch (random spawn block type)
2012-06-14 13:06:06 +00:00
FastSetBlock ( a_BlockX + OfsX , a_BlockY + OfsY + 1 , a_BlockZ + OfsZ , SpawnType , SpawnMeta );
2013-12-07 23:45:33 +00:00
BroadcastSoundParticleEffect ( 2005 , a_BlockX + OfsX , a_BlockY + OfsY , a_BlockZ + OfsZ , 0 );
2012-06-14 13:06:06 +00:00
} // 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 08:52:11 +00: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 22:11:02 +02:00
void cWorld :: GrowMelonPumpkin ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType )
2012-06-14 13:06:06 +00:00
{
MTRand Rand ;
m_ChunkMap -> GrowMelonPumpkin ( a_BlockX , a_BlockY , a_BlockZ , a_BlockType , Rand );
}
2012-10-03 08:52:11 +00: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 );
}
2014-02-03 12:26:17 -08:00
EMCSBiome cWorld :: GetBiomeAt ( int a_BlockX , int a_BlockZ )
2012-06-14 13:06:06 +00:00
{
return m_ChunkMap -> GetBiomeAt ( a_BlockX , a_BlockZ );
}
2014-02-18 13:06:18 +01:00
bool cWorld :: SetBiomeAt ( int a_BlockX , int a_BlockZ , EMCSBiome a_Biome )
{
return m_ChunkMap -> SetBiomeAt ( a_BlockX , a_BlockZ , a_Biome );
}
bool cWorld :: SetAreaBiome ( int a_MinX , int a_MaxX , int a_MinZ , int a_MaxZ , EMCSBiome a_Biome )
{
return m_ChunkMap -> SetAreaBiome ( a_MinX , a_MaxX , a_MinZ , a_MaxZ , a_Biome );
}
bool cWorld :: SetAreaBiome ( const cCuboid & a_Area , EMCSBiome a_Biome )
{
2014-02-18 13:44:40 +01:00
return SetAreaBiome (
std :: min ( a_Area . p1 . x , a_Area . p2 . x ), std :: max ( a_Area . p1 . x , a_Area . p2 . x ),
std :: min ( a_Area . p1 . z , a_Area . p2 . z ), std :: max ( a_Area . p1 . z , a_Area . p2 . z ),
a_Biome
);
2014-02-18 13:06:18 +01:00
}
2014-05-25 13:46:34 +01:00
void cWorld :: SetBlock ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , bool a_SendToClients )
2012-06-14 13:06:06 +00:00
{
2014-09-12 19:07:20 +02:00
m_ChunkMap -> SetBlock ( a_BlockX , a_BlockY , a_BlockZ , a_BlockType , a_BlockMeta , a_SendToClients );
2012-06-14 13:06:06 +00:00
}
2013-03-03 19:05:11 +00:00
void cWorld :: SetBlockMeta ( int a_X , int a_Y , int a_Z , NIBBLETYPE a_MetaData )
2012-06-14 13:06:06 +00:00
{
m_ChunkMap -> SetBlockMeta ( a_X , a_Y , a_Z , a_MetaData );
}
2013-03-03 19:05:11 +00:00
NIBBLETYPE cWorld :: GetBlockSkyLight ( int a_X , int a_Y , int a_Z )
2012-06-14 13:06:06 +00:00
{
return m_ChunkMap -> GetBlockSkyLight ( a_X , a_Y , a_Z );
}
2012-10-20 11:40:34 +00: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 08:47:30 +00:00
bool cWorld :: GetBlockTypeMeta ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE & a_BlockType , NIBBLETYPE & a_BlockMeta )
2012-06-14 13:06:06 +00:00
{
2012-10-26 08:47:30 +00:00
return m_ChunkMap -> GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , ( BLOCKTYPE & ) a_BlockType , ( NIBBLETYPE & ) a_BlockMeta );
2012-06-14 13:06:06 +00:00
}
2012-10-26 08:47:30 +00: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 11:40:34 +00:00
{
2012-10-26 08:47:30 +00:00
return m_ChunkMap -> GetBlockInfo ( a_BlockX , a_BlockY , a_BlockZ , a_BlockType , a_Meta , a_SkyLight , a_BlockLight );
2012-10-20 11:40:34 +00:00
}
2012-10-06 16:58:31 +00: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 );
}
2013-10-24 00:30:20 +01:00
void cWorld :: SpawnItemPickups ( const cItems & a_Pickups , double a_BlockX , double a_BlockY , double a_BlockZ , double a_FlyAwaySpeed , bool IsPlayerCreated )
2012-06-14 13:06:06 +00:00
{
2013-12-09 23:43:06 +00:00
a_FlyAwaySpeed /= 100 ; // Pre-divide, so that we don't have to divide each time inside the loop
2012-06-14 13:06:06 +00:00
for ( cItems :: const_iterator itr = a_Pickups . begin (); itr != a_Pickups . end (); ++ itr )
{
2014-02-02 19:10:22 +00:00
if ( ! IsValidItem ( itr -> m_ItemType ) || itr -> m_ItemType == E_BLOCK_AIR )
2013-12-06 20:39:42 +00:00
{
// Don't spawn pickup if item isn't even valid; should prevent client crashing too
continue ;
}
2014-04-12 13:14:35 +01:00
float SpeedX = ( float )( a_FlyAwaySpeed * ( GetTickRandomNumber ( 10 ) - 5 ));
float SpeedY = ( float )( a_FlyAwaySpeed * GetTickRandomNumber ( 50 ));
float SpeedZ = ( float )( a_FlyAwaySpeed * ( GetTickRandomNumber ( 10 ) - 5 ));
2013-05-12 21:00:21 +00:00
2012-06-14 13:06:06 +00:00
cPickup * Pickup = new cPickup (
2013-09-18 22:20:08 +01:00
a_BlockX , a_BlockY , a_BlockZ ,
2013-10-24 00:30:20 +01:00
* itr , IsPlayerCreated , SpeedX , SpeedY , SpeedZ
2012-06-14 13:06:06 +00:00
);
2014-06-08 21:58:08 +02:00
Pickup -> Initialize ( * this );
2012-06-14 13:06:06 +00:00
}
}
2013-10-24 00:30:20 +01:00
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 , bool IsPlayerCreated )
2012-06-14 13:06:06 +00:00
{
for ( cItems :: const_iterator itr = a_Pickups . begin (); itr != a_Pickups . end (); ++ itr )
{
2014-02-02 19:10:22 +00:00
if ( ! IsValidItem ( itr -> m_ItemType ) || itr -> m_ItemType == E_BLOCK_AIR )
2013-12-06 20:39:42 +00:00
{
continue ;
}
2012-06-14 13:06:06 +00:00
cPickup * Pickup = new cPickup (
2013-09-18 22:20:08 +01:00
a_BlockX , a_BlockY , a_BlockZ ,
2013-10-24 00:30:20 +01:00
* itr , IsPlayerCreated , ( float ) a_SpeedX , ( float ) a_SpeedY , ( float ) a_SpeedZ
2012-06-14 13:06:06 +00:00
);
2014-06-08 21:58:08 +02:00
Pickup -> Initialize ( * this );
2012-06-14 13:06:06 +00:00
}
}
2013-12-07 14:26:52 +01:00
int cWorld :: SpawnFallingBlock ( int a_X , int a_Y , int a_Z , BLOCKTYPE BlockType , NIBBLETYPE BlockMeta )
{
cFallingBlock * FallingBlock = new cFallingBlock ( Vector3i ( a_X , a_Y , a_Z ), BlockType , BlockMeta );
2014-06-08 21:58:08 +02:00
FallingBlock -> Initialize ( * this );
2013-12-07 14:26:52 +01:00
return FallingBlock -> GetUniqueID ();
}
2013-11-26 15:37:15 +01:00
int cWorld :: SpawnExperienceOrb ( double a_X , double a_Y , double a_Z , int a_Reward )
2013-11-25 21:03:26 +01:00
{
2014-04-18 12:54:17 +01:00
if ( a_Reward < 1 )
{
return - 1 ;
}
2013-11-25 21:03:26 +01:00
cExpOrb * ExpOrb = new cExpOrb ( a_X , a_Y , a_Z , a_Reward );
2014-06-08 21:58:08 +02:00
ExpOrb -> Initialize ( * this );
2013-11-26 15:37:15 +01:00
return ExpOrb -> GetUniqueID ();
2013-11-25 21:03:26 +01:00
}
2014-01-12 18:04:41 +01:00
int cWorld :: SpawnMinecart ( double a_X , double a_Y , double a_Z , int a_MinecartType , const cItem & a_Content , int a_BlockHeight )
2014-01-12 14:33:32 +01:00
{
cMinecart * Minecart ;
switch ( a_MinecartType )
{
2014-01-12 18:04:41 +01:00
case E_ITEM_MINECART : Minecart = new cRideableMinecart ( a_X , a_Y , a_Z , a_Content , a_BlockHeight ); break ;
2014-01-12 14:33:32 +01:00
case E_ITEM_CHEST_MINECART : Minecart = new cMinecartWithChest ( a_X , a_Y , a_Z ); break ;
case E_ITEM_FURNACE_MINECART : Minecart = new cMinecartWithFurnace ( a_X , a_Y , a_Z ); break ;
case E_ITEM_MINECART_WITH_TNT : Minecart = new cMinecartWithTNT ( a_X , a_Y , a_Z ); break ;
case E_ITEM_MINECART_WITH_HOPPER : Minecart = new cMinecartWithHopper ( a_X , a_Y , a_Z ); break ;
default :
{
return - 1 ;
}
} // switch (a_MinecartType)
2014-06-08 21:58:08 +02:00
Minecart -> Initialize ( * this );
2014-01-12 15:27:50 +01:00
return Minecart -> GetUniqueID ();
2014-01-12 14:33:32 +01:00
}
2014-03-08 12:24:33 +01:00
void cWorld :: SpawnPrimedTNT ( double a_X , double a_Y , double a_Z , int a_FuseTicks , double a_InitialVelocityCoeff )
2013-06-18 19:09:51 +00:00
{
2014-03-08 12:24:33 +01:00
cTNTEntity * TNT = new cTNTEntity ( a_X , a_Y , a_Z , a_FuseTicks );
2014-06-08 21:58:08 +02:00
TNT -> Initialize ( * this );
2014-03-05 22:12:48 +00:00
TNT -> SetSpeed (
a_InitialVelocityCoeff * ( GetTickRandomNumber ( 2 ) - 1 ), /** -1, 0, 1 */
a_InitialVelocityCoeff * 2 ,
a_InitialVelocityCoeff * ( GetTickRandomNumber ( 2 ) - 1 )
2014-03-20 09:28:29 +01:00
);
2013-06-18 19:09:51 +00:00
}
2012-06-14 13:06:06 +00: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 19:05:11 +00:00
bool cWorld :: DigBlock ( int a_X , int a_Y , int a_Z )
2012-06-14 13:06:06 +00:00
{
2014-03-02 21:25:05 +02:00
cBlockHandler * Handler = cBlockInfo :: GetHandler ( GetBlock ( a_X , a_Y , a_Z ));
2014-02-01 05:06:32 -08:00
cChunkInterface ChunkInterface ( GetChunkMap ());
Handler -> OnDestroyed ( ChunkInterface , * this , a_X , a_Y , a_Z );
2012-06-14 13:06:06 +00:00
return m_ChunkMap -> DigBlock ( a_X , a_Y , a_Z );
}
2013-03-03 19:05:11 +00:00
void cWorld :: SendBlockTo ( int a_X , int a_Y , int a_Z , cPlayer * a_Player )
2012-06-14 13:06:06 +00:00
{
m_ChunkMap -> SendBlockTo ( a_X , a_Y , a_Z , a_Player );
}
2013-03-03 19:05:11 +00:00
int cWorld :: GetHeight ( int a_X , int a_Z )
2012-06-14 13:06:06 +00:00
{
return m_ChunkMap -> GetHeight ( a_X , a_Z );
}
2013-04-13 21:02:10 +00: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 19:05:11 +00:00
void cWorld :: BroadcastAttachEntity ( const cEntity & a_Entity , const cEntity * a_Vehicle )
{
return m_ChunkMap -> BroadcastAttachEntity ( a_Entity , a_Vehicle );
}
2013-07-07 13:06:06 +00: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 10:38:15 +00:00
{
2013-07-07 13:06:06 +00:00
m_ChunkMap -> BroadcastBlockAction ( a_BlockX , a_BlockY , a_BlockZ , a_Byte1 , a_Byte2 , a_BlockType , a_Exclude );
2012-08-18 10:38:15 +00:00
}
2013-07-07 13:06:06 +00: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 11:51:17 +00:00
{
2013-07-07 13:06:06 +00:00
m_ChunkMap -> BroadcastBlockBreakAnimation ( a_EntityID , a_BlockX , a_BlockY , a_BlockZ , a_Stage , a_Exclude );
2012-08-19 11:51:17 +00:00
}
2013-07-07 13:06:06 +00:00
void cWorld :: BroadcastBlockEntity ( int a_BlockX , int a_BlockY , int a_BlockZ , const cClientHandle * a_Exclude )
2012-08-19 11:51:17 +00:00
{
2013-07-07 13:06:06 +00:00
m_ChunkMap -> BroadcastBlockEntity ( a_BlockX , a_BlockY , a_BlockZ , a_Exclude );
2012-08-19 11:51:17 +00:00
}
2014-02-15 23:26:19 +01:00
void cWorld :: BroadcastChat ( const AString & a_Message , const cClientHandle * a_Exclude , eMessageType a_ChatPrefix )
2012-08-19 19:42:32 +00: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 ;
}
2014-02-07 18:58:52 +00:00
ch -> SendChat ( a_Message , a_ChatPrefix );
2012-08-19 19:42:32 +00:00
}
}
2014-02-15 23:16:44 +01:00
void cWorld :: BroadcastChat ( const cCompositeChat & a_Message , const cClientHandle * a_Exclude )
{
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 -> SendChat ( a_Message );
}
}
2013-07-07 13:06:06 +00:00
void cWorld :: BroadcastChunkData ( int a_ChunkX , int a_ChunkZ , cChunkDataSerializer & a_Serializer , const cClientHandle * a_Exclude )
2012-08-19 19:42:32 +00:00
{
2013-07-07 13:06:06 +00:00
m_ChunkMap -> BroadcastChunkData ( a_ChunkX , a_ChunkZ , a_Serializer , a_Exclude );
2012-08-19 19:42:32 +00:00
}
2014-06-27 20:56:29 +02:00
void cWorld :: BroadcastCollectEntity ( const cEntity & a_Entity , const cPlayer & a_Player , const cClientHandle * a_Exclude )
2012-08-19 19:42:32 +00:00
{
2014-06-27 20:56:29 +02:00
m_ChunkMap -> BroadcastCollectEntity ( a_Entity , a_Player , a_Exclude );
2012-08-19 19:42:32 +00:00
}
2013-07-07 13:06:06 +00:00
void cWorld :: BroadcastDestroyEntity ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-19 19:42:32 +00:00
{
2013-07-07 13:06:06 +00:00
m_ChunkMap -> BroadcastDestroyEntity ( a_Entity , a_Exclude );
2012-08-19 19:42:32 +00:00
}
2013-12-15 10:51:46 +01:00
void cWorld :: BroadcastEntityEffect ( const cEntity & a_Entity , int a_EffectID , int a_Amplifier , short a_Duration , const cClientHandle * a_Exclude )
{
m_ChunkMap -> BroadcastEntityEffect ( a_Entity , a_EffectID , a_Amplifier , a_Duration , a_Exclude );
}
2013-07-07 13:06:06 +00:00
void cWorld :: BroadcastEntityEquipment ( const cEntity & a_Entity , short a_SlotNum , const cItem & a_Item , const cClientHandle * a_Exclude )
2012-08-19 19:42:32 +00:00
{
2013-07-07 13:06:06 +00:00
m_ChunkMap -> BroadcastEntityEquipment ( a_Entity , a_SlotNum , a_Item , a_Exclude );
2012-08-19 19:42:32 +00:00
}
2013-07-07 13:06:06 +00:00
void cWorld :: BroadcastEntityHeadLook ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-19 19:42:32 +00:00
{
2013-07-07 13:06:06 +00:00
m_ChunkMap -> BroadcastEntityHeadLook ( a_Entity , a_Exclude );
2012-08-19 19:42:32 +00:00
}
2013-07-07 13:06:06 +00:00
void cWorld :: BroadcastEntityLook ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-19 19:42:32 +00:00
{
2013-07-07 13:06:06 +00:00
m_ChunkMap -> BroadcastEntityLook ( a_Entity , a_Exclude );
2012-08-19 19:42:32 +00:00
}
2013-07-07 13:06:06 +00:00
void cWorld :: BroadcastEntityMetadata ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-19 21:14:45 +00:00
{
2013-07-07 13:06:06 +00:00
m_ChunkMap -> BroadcastEntityMetadata ( a_Entity , a_Exclude );
2012-08-19 21:14:45 +00:00
}
2013-07-07 13:06:06 +00:00
void cWorld :: 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
{
2013-07-07 13:06:06 +00:00
m_ChunkMap -> BroadcastEntityRelMove ( a_Entity , a_RelX , a_RelY , a_RelZ , a_Exclude );
2012-08-19 21:14:45 +00:00
}
2013-07-07 13:06:06 +00:00
void cWorld :: 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
{
2013-07-07 13:06:06 +00:00
m_ChunkMap -> BroadcastEntityRelMoveLook ( a_Entity , a_RelX , a_RelY , a_RelZ , a_Exclude );
2012-08-24 07:58:26 +00:00
}
2013-07-07 13:06:06 +00:00
void cWorld :: BroadcastEntityStatus ( const cEntity & a_Entity , char a_Status , const cClientHandle * a_Exclude )
2012-08-24 09:49:00 +00:00
{
2013-07-07 13:06:06 +00:00
m_ChunkMap -> BroadcastEntityStatus ( a_Entity , a_Status , a_Exclude );
2012-08-24 09:49:00 +00:00
}
2013-07-07 13:06:06 +00:00
void cWorld :: BroadcastEntityVelocity ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
{
m_ChunkMap -> BroadcastEntityVelocity ( a_Entity , a_Exclude );
}
2013-12-06 23:47:07 +00:00
void cWorld :: BroadcastEntityAnimation ( const cEntity & a_Entity , char a_Animation , const cClientHandle * a_Exclude )
2013-07-07 13:06:06 +00:00
{
2013-12-06 23:47:07 +00:00
m_ChunkMap -> BroadcastEntityAnimation ( a_Entity , a_Animation , a_Exclude );
2013-07-07 13:06:06 +00:00
}
2014-09-11 17:03:09 +02:00
void cWorld :: 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
{
2014-09-11 17:03:09 +02:00
m_ChunkMap -> BroadcastParticleEffect ( a_ParticleName , a_SrcX , a_SrcY , a_SrcZ , a_OffsetX , a_OffsetY , a_OffsetZ , a_ParticleData , a_ParticleAmount , a_Exclude );
2013-12-22 14:45:25 +01:00
}
2014-09-18 18:50:17 +02:00
void cWorld :: BroadcastPlayerListAddPlayer ( const cPlayer & a_Player , const cClientHandle * a_Exclude )
2012-08-25 21:46:18 +00: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 ;
}
2014-09-18 18:50:17 +02:00
ch -> SendPlayerListAddPlayer ( a_Player );
}
}
void cWorld :: BroadcastPlayerListRemovePlayer ( const cPlayer & a_Player , const cClientHandle * a_Exclude )
{
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 -> SendPlayerListRemovePlayer ( a_Player );
}
}
void cWorld :: BroadcastPlayerListUpdateGameMode ( const cPlayer & a_Player , const cClientHandle * a_Exclude )
{
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 -> SendPlayerListUpdateGameMode ( a_Player );
}
}
void cWorld :: BroadcastPlayerListUpdatePing ( const cPlayer & a_Player , const cClientHandle * a_Exclude )
2013-12-22 14:45:25 +01:00
{
2014-09-18 18:50:17 +02: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 -> SendPlayerListUpdatePing ( a_Player );
}
2013-12-22 14:45:25 +01:00
}
2014-09-26 17:37:19 +02:00
void cWorld :: BroadcastPlayerListUpdateDisplayName ( const cPlayer & a_Player , const AString & a_CustomName , const cClientHandle * a_Exclude )
2012-08-25 21:46:18 +00: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 ;
}
2014-09-26 17:37:19 +02:00
ch -> SendPlayerListUpdateDisplayName ( a_Player , a_CustomName );
2012-08-25 21:46:18 +00:00
}
}
2013-12-15 10:51:46 +01:00
void cWorld :: BroadcastRemoveEntityEffect ( const cEntity & a_Entity , int a_EffectID , const cClientHandle * a_Exclude )
{
m_ChunkMap -> BroadcastRemoveEntityEffect ( a_Entity , a_EffectID , a_Exclude );
}
2014-01-21 15:58:17 +02:00
void cWorld :: BroadcastScoreboardObjective ( const AString & a_Name , const AString & a_DisplayName , Byte a_Mode )
{
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 ;
}
ch -> SendScoreboardObjective ( a_Name , a_DisplayName , a_Mode );
}
}
void cWorld :: BroadcastScoreUpdate ( const AString & a_Objective , const AString & a_Player , cObjective :: Score a_Score , Byte a_Mode )
{
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 ;
}
ch -> SendScoreUpdate ( a_Objective , a_Player , a_Score , a_Mode );
}
}
void cWorld :: BroadcastDisplayObjective ( const AString & a_Objective , cScoreboard :: eDisplaySlot a_Display )
{
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 ;
}
ch -> SendDisplayObjective ( a_Objective , a_Display );
}
}
2014-07-13 02:08:02 +02:00
void cWorld :: 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-08-25 21:46:18 +00:00
{
2014-07-13 02:08:02 +02:00
m_ChunkMap -> BroadcastSoundEffect ( a_SoundName , a_X , a_Y , a_Z , a_Volume , a_Pitch , a_Exclude );
2012-08-25 21:46:18 +00:00
}
2013-07-07 13:06:06 +00: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 21:46:18 +00:00
{
2013-07-07 13:06:06 +00:00
m_ChunkMap -> BroadcastSoundParticleEffect ( a_EffectID , a_SrcX , a_SrcY , a_SrcZ , a_Data , a_Exclude );
2012-08-25 21:46:18 +00:00
}
2013-07-07 13:06:06 +00:00
void cWorld :: BroadcastSpawnEntity ( cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-26 21:01:07 +00:00
{
2013-07-07 13:06:06 +00:00
m_ChunkMap -> BroadcastSpawnEntity ( a_Entity , a_Exclude );
2012-08-26 21:01:07 +00:00
}
2013-07-07 13:06:06 +00:00
void cWorld :: BroadcastTeleportEntity ( const cEntity & a_Entity , const cClientHandle * a_Exclude )
2012-08-27 17:31:16 +00: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 13:06:06 +00:00
ch -> SendTeleportEntity ( a_Entity );
2012-08-27 17:31:16 +00:00
}
}
2013-07-07 13:06:06 +00:00
void cWorld :: BroadcastThunderbolt ( int a_BlockX , int a_BlockY , int a_BlockZ , const cClientHandle * a_Exclude )
2012-09-25 09:54:36 +00:00
{
2013-07-07 13:06:06 +00:00
m_ChunkMap -> BroadcastThunderbolt ( a_BlockX , a_BlockY , a_BlockZ , a_Exclude );
2012-10-21 07:46:28 +00:00
}
2013-07-07 13:06:06 +00:00
void cWorld :: BroadcastTimeUpdate ( const cClientHandle * a_Exclude )
2012-10-21 07:46:28 +00:00
{
2013-07-07 13:06:06 +00: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 ;
}
2014-08-11 00:20:28 +02:00
ch -> SendTimeUpdate ( m_WorldAge , m_TimeOfDay , m_IsDaylightCycleEnabled );
2013-07-07 13:06:06 +00:00
}
2012-09-25 09:54:36 +00:00
}
2013-03-03 19:05:11 +00:00
void cWorld :: BroadcastUseBed ( const cEntity & a_Entity , int a_BlockX , int a_BlockY , int a_BlockZ )
2012-09-29 20:43:42 +00:00
{
m_ChunkMap -> BroadcastUseBed ( a_Entity , a_BlockX , a_BlockY , a_BlockZ );
}
2013-07-07 13:06:06 +00:00
void cWorld :: BroadcastWeather ( eWeather a_Weather , const cClientHandle * a_Exclude )
2012-08-24 07:58:26 +00:00
{
2013-07-07 13:06:06 +00: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 07:58:26 +00: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 );
}
2014-07-06 23:50:22 +01:00
void cWorld :: MarkRedstoneDirty ( int a_ChunkX , int a_ChunkZ )
2012-06-14 13:06:06 +00:00
{
2014-07-06 23:50:22 +01:00
m_ChunkMap -> MarkRedstoneDirty ( a_ChunkX , a_ChunkZ );
}
void cWorld :: MarkChunkDirty ( int a_ChunkX , int a_ChunkZ , bool a_MarkRedstoneDirty )
{
m_ChunkMap -> MarkChunkDirty ( a_ChunkX , a_ChunkZ , a_MarkRedstoneDirty );
2012-06-14 13:06:06 +00:00
}
2013-04-13 21:02:10 +00:00
void cWorld :: MarkChunkSaving ( int a_ChunkX , int a_ChunkZ )
2012-06-14 13:06:06 +00:00
{
2013-04-13 21:02:10 +00:00
m_ChunkMap -> MarkChunkSaving ( a_ChunkX , a_ChunkZ );
2012-06-14 13:06:06 +00:00
}
2013-04-13 21:02:10 +00:00
void cWorld :: MarkChunkSaved ( int a_ChunkX , int a_ChunkZ )
2012-06-14 13:06:06 +00:00
{
2013-04-13 21:02:10 +00:00
m_ChunkMap -> MarkChunkSaved ( a_ChunkX , a_ChunkZ );
2012-06-14 13:06:06 +00:00
}
2014-07-24 18:32:05 +02:00
void cWorld :: QueueSetChunkData ( const cSetChunkDataPtr & a_SetChunkData )
2012-06-14 13:06:06 +00:00
{
2014-09-05 22:16:48 +02:00
ASSERT ( IsChunkQueued ( a_SetChunkData -> GetChunkX (), a_SetChunkData -> GetChunkZ ()));
2012-06-14 13:06:06 +00:00
// Validate biomes, if needed:
2014-07-24 18:32:05 +02:00
if ( ! a_SetChunkData -> AreBiomesValid ())
2012-06-14 13:06:06 +00:00
{
// The biomes are not assigned, get them from the generator:
2014-07-24 18:32:05 +02:00
m_Generator . GenerateBiomes ( a_SetChunkData -> GetChunkX (), a_SetChunkData -> GetChunkZ (), a_SetChunkData -> GetBiomes ());
a_SetChunkData -> MarkBiomesValid ();
2012-06-14 13:06:06 +00:00
}
2014-07-24 18:32:05 +02:00
// Validate heightmap, if needed:
if ( ! a_SetChunkData -> IsHeightMapValid ())
{
a_SetChunkData -> CalculateHeightMap ();
}
// Store a copy of the data in the queue:
// TODO: If the queue is too large, wait for it to get processed. Not likely, though.
cCSLock Lock ( m_CSSetChunkDataQueue );
m_SetChunkDataQueue . push_back ( a_SetChunkData );
}
void cWorld :: SetChunkData ( cSetChunkData & a_SetChunkData )
{
ASSERT ( a_SetChunkData . AreBiomesValid ());
ASSERT ( a_SetChunkData . IsHeightMapValid ());
m_ChunkMap -> SetChunkData ( a_SetChunkData );
2013-04-01 20:56:25 +00:00
// Initialize the entities (outside the m_ChunkMap's CS, to fix FS #347):
2014-07-24 18:32:05 +02:00
cEntityList Entities ;
std :: swap ( a_SetChunkData . GetEntities (), Entities );
for ( cEntityList :: iterator itr = Entities . begin (), end = Entities . end (); itr != end ; ++ itr )
2013-04-01 18:24:05 +00:00
{
2014-06-08 21:58:08 +02:00
( * itr ) -> Initialize ( * this );
2013-04-01 18:24:05 +00:00
}
2012-06-14 13:06:06 +00:00
// If a client is requesting this chunk, send it to them:
2014-07-24 18:32:05 +02:00
int ChunkX = a_SetChunkData . GetChunkX ();
int ChunkZ = a_SetChunkData . GetChunkZ ();
if ( m_ChunkMap -> HasChunkAnyClients ( ChunkX , ChunkZ ))
{
m_ChunkSender . ChunkReady ( ChunkX , ChunkZ );
}
// Save the chunk right after generating, so that we don't have to generate it again on next run
if ( a_SetChunkData . ShouldMarkDirty ())
2012-06-14 13:06:06 +00:00
{
2014-08-28 11:36:35 +02:00
m_Storage . QueueSaveChunk ( ChunkX , ChunkZ );
2012-06-14 13:06:06 +00:00
}
}
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 21:02:10 +00:00
bool cWorld :: GetChunkData ( int a_ChunkX , int a_ChunkZ , cChunkDataCallback & a_Callback )
2012-06-14 13:06:06 +00:00
{
2013-04-13 21:02:10 +00:00
return m_ChunkMap -> GetChunkData ( a_ChunkX , a_ChunkZ , a_Callback );
2012-06-14 13:06:06 +00:00
}
2013-04-13 21:02:10 +00:00
bool cWorld :: GetChunkBlockTypes ( int a_ChunkX , int a_ChunkZ , BLOCKTYPE * a_BlockTypes )
2012-06-14 13:06:06 +00:00
{
2013-04-13 21:02:10 +00:00
return m_ChunkMap -> GetChunkBlockTypes ( a_ChunkX , a_ChunkZ , a_BlockTypes );
2012-06-14 13:06:06 +00:00
}
2014-09-05 22:16:48 +02:00
bool cWorld :: IsChunkQueued ( int a_ChunkX , int a_ChunkZ ) const
{
return m_ChunkMap -> IsChunkQueued ( a_ChunkX , a_ChunkZ );
}
2013-04-13 21:02:10 +00:00
bool cWorld :: IsChunkValid ( int a_ChunkX , int a_ChunkZ ) const
2012-06-14 13:06:06 +00:00
{
2013-04-13 21:02:10 +00:00
return m_ChunkMap -> IsChunkValid ( a_ChunkX , a_ChunkZ );
2012-06-14 13:06:06 +00:00
}
2013-04-13 21:02:10 +00:00
bool cWorld :: HasChunkAnyClients ( int a_ChunkX , int a_ChunkZ ) const
2012-06-14 13:06:06 +00:00
{
2013-04-13 21:02:10 +00:00
return m_ChunkMap -> HasChunkAnyClients ( a_ChunkX , a_ChunkZ );
2012-06-14 13:06:06 +00:00
}
2013-03-03 19:05:11 +00:00
void cWorld :: UnloadUnusedChunks ( void )
2012-06-14 13:06:06 +00:00
{
2012-11-01 21:38:20 +00:00
m_LastUnload = m_WorldAge ;
2012-06-14 13:06:06 +00:00
m_ChunkMap -> UnloadUnusedChunks ();
}
2014-02-10 20:00:07 -05:00
void cWorld :: QueueUnloadUnusedChunks ( void )
{
2014-10-10 15:33:19 +01:00
QueueTask ( make_unique < cWorld :: cTaskUnloadUnusedChunks > ());
2014-02-10 20:00:07 -05:00
}
2012-06-14 13:06:06 +00:00
2014-02-11 08:01:25 -05:00
2012-06-14 13:06:06 +00:00
void cWorld :: CollectPickupsByPlayer ( cPlayer * a_Player )
{
m_ChunkMap -> CollectPickupsByPlayer ( a_Player );
}
2013-04-13 21:02:10 +00:00
void cWorld :: AddPlayer ( cPlayer * a_Player )
2012-06-14 13:06:06 +00:00
{
2014-06-08 21:58:08 +02:00
cCSLock Lock ( m_CSPlayersToAdd );
m_PlayersToAdd . push_back ( a_Player );
2012-06-14 13:06:06 +00:00
}
2014-07-29 20:50:30 +01:00
void cWorld :: RemovePlayer ( cPlayer * a_Player , bool a_RemoveFromChunk )
2012-06-14 13:06:06 +00:00
{
2014-07-29 20:50:30 +01:00
if ( a_RemoveFromChunk )
2014-06-21 22:07:38 +01:00
{
2014-07-29 20:50:30 +01:00
// To prevent iterator invalidations when an entity goes through a portal and calls this function whilst being ticked by cChunk
// we should not change cChunk's entity list if asked not to
2014-06-21 22:07:38 +01:00
m_ChunkMap -> RemoveEntity ( a_Player );
}
2014-06-08 21:58:08 +02:00
{
cCSLock Lock ( m_CSPlayersToAdd );
m_PlayersToAdd . remove ( a_Player );
}
2013-08-13 22:45:29 +02:00
{
cCSLock Lock ( m_CSPlayers );
2014-07-14 19:49:31 +01:00
LOGD ( "Removing player %s from world \" %s \" " , a_Player -> GetName (). c_str (), m_WorldName . c_str ());
2013-08-13 22:45:29 +02:00
m_Players . remove ( a_Player );
}
// Remove the player's client from the list of clients to be ticked:
2014-06-08 21:58:08 +02:00
cClientHandle * Client = a_Player -> GetClientHandle ();
if ( Client != NULL )
2013-08-13 22:45:29 +02:00
{
2014-06-08 21:58:08 +02:00
Client -> RemoveFromWorld ();
m_ChunkMap -> RemoveClientFromChunks ( Client );
2013-08-13 22:45:29 +02:00
cCSLock Lock ( m_CSClients );
2014-06-08 21:58:08 +02:00
m_ClientsToRemove . push_back ( Client );
2013-08-13 22:45:29 +02:00
}
2012-06-14 13:06:06 +00:00
}
bool cWorld :: ForEachPlayer ( cPlayerListCallback & a_Callback )
{
// Calls the callback for each player in the list
cCSLock Lock ( m_CSPlayers );
2012-06-19 20:31:21 +00:00
for ( cPlayerList :: iterator itr = m_Players . begin (), itr2 = itr ; itr != m_Players . end (); itr = itr2 )
2012-06-14 13:06:06 +00:00
{
2012-06-19 20:31:21 +00:00
++ itr2 ;
2012-06-14 13:06:06 +00:00
if ( a_Callback . Item ( * itr ))
{
return false ;
}
} // for itr - m_Players[]
return true ;
}
2012-07-02 11:21:21 +00:00
bool cWorld :: DoWithPlayer ( const AString & a_PlayerName , cPlayerListCallback & a_Callback )
{
// Calls the callback for each player in the list
2012-06-14 13:06:06 +00:00
cCSLock Lock ( m_CSPlayers );
2012-07-02 11:21:21 +00:00
for ( cPlayerList :: iterator itr = m_Players . begin (); itr != m_Players . end (); ++ itr )
2012-06-14 13:06:06 +00:00
{
2012-07-13 19:25:53 +00:00
if ( NoCaseCompare (( * itr ) -> GetName (), a_PlayerName ) == 0 )
2012-06-14 13:06:06 +00:00
{
2012-07-02 11:21:21 +00:00
a_Callback . Item ( * itr );
return true ;
2012-06-14 13:06:06 +00:00
}
2012-07-02 11:21:21 +00:00
} // for itr - m_Players[]
return false ;
2012-06-14 13:06:06 +00:00
}
2013-02-01 19:59:58 +00:00
bool cWorld :: FindAndDoWithPlayer ( const AString & a_PlayerNameHint , cPlayerListCallback & a_Callback )
2012-08-22 23:05:12 +00:00
{
2013-02-01 19:59:58 +00:00
cPlayer * BestMatch = NULL ;
2014-05-08 20:16:35 +02:00
size_t BestRating = 0 ;
size_t NameLength = a_PlayerNameHint . length ();
2012-08-22 23:05:12 +00:00
cCSLock Lock ( m_CSPlayers );
for ( cPlayerList :: iterator itr = m_Players . begin (); itr != m_Players . end (); ++ itr )
{
2014-05-08 20:16:35 +02:00
size_t Rating = RateCompareString ( a_PlayerNameHint , ( * itr ) -> GetName ());
2013-02-01 19:59:58 +00:00
if ( Rating >= BestRating )
2012-08-22 23:05:12 +00:00
{
BestMatch = * itr ;
BestRating = Rating ;
}
2013-02-01 19:59:58 +00:00
if ( Rating == NameLength ) // Perfect match
2012-08-22 23:05:12 +00:00
{
break ;
}
} // for itr - m_Players[]
2013-02-01 19:59:58 +00:00
if ( BestMatch != NULL )
2012-08-22 23:05:12 +00:00
{
return a_Callback . Item ( BestMatch );
}
return false ;
}
2012-07-02 11:21:21 +00:00
// TODO: This interface is dangerous!
2014-02-03 20:52:11 +01:00
cPlayer * cWorld :: FindClosestPlayer ( const Vector3d & a_Pos , float a_SightLimit , bool a_CheckLineOfSight )
2012-06-14 13:06:06 +00:00
{
cTracer LineOfSight ( this );
2014-03-14 14:36:44 +01:00
double ClosestDistance = a_SightLimit ;
cPlayer * ClosestPlayer = NULL ;
2012-06-14 13:06:06 +00:00
cCSLock Lock ( m_CSPlayers );
for ( cPlayerList :: const_iterator itr = m_Players . begin (); itr != m_Players . end (); ++ itr )
{
Vector3f Pos = ( * itr ) -> GetPosition ();
2014-03-14 14:36:44 +01:00
double Distance = ( Pos - a_Pos ). Length ();
2012-06-14 13:06:06 +00:00
2013-08-24 22:43:17 +02:00
if ( Distance < ClosestDistance )
2012-06-14 13:06:06 +00:00
{
2014-07-15 12:52:02 +01:00
if ( a_CheckLineOfSight )
2014-01-25 14:42:26 +00:00
{
2014-07-20 23:10:31 +02:00
if ( ! LineOfSight . Trace ( a_Pos , ( Pos - a_Pos ), ( int )( Pos - a_Pos ). Length ()))
2014-07-15 12:52:02 +01:00
{
ClosestDistance = Distance ;
ClosestPlayer = * itr ;
}
2014-01-25 14:42:26 +00:00
}
else
2012-06-14 13:06:06 +00:00
{
2013-08-24 22:43:17 +02:00
ClosestDistance = Distance ;
ClosestPlayer = * itr ;
2012-06-14 13:06:06 +00:00
}
}
}
return ClosestPlayer ;
}
void cWorld :: SendPlayerList ( cPlayer * a_DestPlayer )
{
// Sends the playerlist to a_DestPlayer
cCSLock Lock ( m_CSPlayers );
2012-08-19 19:42:32 +00:00
for ( cPlayerList :: iterator itr = m_Players . begin (); itr != m_Players . end (); ++ itr )
2012-06-14 13:06:06 +00:00
{
cClientHandle * ch = ( * itr ) -> GetClientHandle ();
if (( ch != NULL ) && ! ch -> IsDestroyed ())
{
2014-09-18 18:50:17 +02:00
a_DestPlayer -> GetClientHandle () -> SendPlayerListAddPlayer ( * ( * itr ));
2012-06-14 13:06:06 +00:00
}
}
}
2012-06-16 08:35:07 +00:00
bool cWorld :: ForEachEntity ( cEntityCallback & a_Callback )
2012-06-14 13:06:06 +00:00
{
2013-04-13 21:02:10 +00:00
return m_ChunkMap -> ForEachEntity ( a_Callback );
2012-06-14 13:06:06 +00:00
}
2012-06-16 08:35:07 +00:00
bool cWorld :: ForEachEntityInChunk ( int a_ChunkX , int a_ChunkZ , cEntityCallback & a_Callback )
2012-06-14 13:06:06 +00:00
{
2012-06-16 08:35:07 +00:00
return m_ChunkMap -> ForEachEntityInChunk ( a_ChunkX , a_ChunkZ , a_Callback );
2012-06-14 13:06:06 +00:00
}
2014-09-03 17:00:26 +02:00
bool cWorld :: ForEachEntityInBox ( const cBoundingBox & a_Box , cEntityCallback & a_Callback )
{
return m_ChunkMap -> ForEachEntityInBox ( a_Box , a_Callback );
}
2013-03-03 19:05:11 +00:00
bool cWorld :: DoWithEntityByID ( int a_UniqueID , cEntityCallback & a_Callback )
2012-06-14 13:06:06 +00:00
{
2013-04-13 21:02:10 +00:00
return m_ChunkMap -> DoWithEntityByID ( a_UniqueID , a_Callback );
2012-06-14 13:06:06 +00:00
}
2013-04-13 21:02:10 +00:00
void cWorld :: CompareChunkClients ( int a_ChunkX1 , int a_ChunkZ1 , int a_ChunkX2 , int a_ChunkZ2 , cClientDiffCallback & a_Callback )
2012-06-14 13:06:06 +00:00
{
2013-04-13 21:02:10 +00:00
m_ChunkMap -> CompareChunkClients ( a_ChunkX1 , a_ChunkZ1 , a_ChunkX2 , a_ChunkZ2 , a_Callback );
2012-06-14 13:06:06 +00:00
}
2013-04-13 21:02:10 +00:00
bool cWorld :: AddChunkClient ( int a_ChunkX , int a_ChunkZ , cClientHandle * a_Client )
2012-06-14 13:06:06 +00:00
{
2013-04-13 21:02:10 +00:00
return m_ChunkMap -> AddChunkClient ( a_ChunkX , a_ChunkZ , a_Client );
2012-06-14 13:06:06 +00:00
}
2013-04-13 21:02:10 +00:00
void cWorld :: RemoveChunkClient ( int a_ChunkX , int a_ChunkZ , cClientHandle * a_Client )
2012-06-14 13:06:06 +00:00
{
2013-04-13 21:02:10 +00:00
m_ChunkMap -> RemoveChunkClient ( a_ChunkX , a_ChunkZ , a_Client );
2012-06-14 13:06:06 +00:00
}
void cWorld :: RemoveClientFromChunks ( cClientHandle * a_Client )
{
m_ChunkMap -> RemoveClientFromChunks ( a_Client );
}
2013-04-13 21:02:10 +00:00
void cWorld :: SendChunkTo ( int a_ChunkX , int a_ChunkZ , cClientHandle * a_Client )
2012-06-14 13:06:06 +00:00
{
2013-04-13 21:02:10 +00:00
m_ChunkSender . QueueSendChunkTo ( a_ChunkX , a_ChunkZ , a_Client );
2012-06-14 13:06:06 +00:00
}
2014-02-18 13:06:18 +01:00
void cWorld :: ForceSendChunkTo ( int a_ChunkX , int a_ChunkZ , cClientHandle * a_Client )
{
a_Client -> AddWantedChunk ( a_ChunkX , a_ChunkZ );
m_ChunkSender . QueueSendChunkTo ( a_ChunkX , a_ChunkZ , a_Client );
}
2012-06-14 13:06:06 +00:00
void cWorld :: RemoveClientFromChunkSender ( cClientHandle * a_Client )
{
m_ChunkSender . RemoveClient ( a_Client );
}
2014-08-28 11:36:35 +02:00
void cWorld :: TouchChunk ( int a_ChunkX , int a_ChunkZ )
2012-06-14 13:06:06 +00:00
{
2014-08-28 11:36:35 +02:00
m_ChunkMap -> TouchChunk ( a_ChunkX , a_ChunkZ );
2012-06-14 13:06:06 +00:00
}
2014-08-28 11:36:35 +02:00
void cWorld :: ChunkLoadFailed ( int a_ChunkX , int a_ChunkZ )
2012-06-14 13:06:06 +00:00
{
2014-08-28 11:36:35 +02:00
m_ChunkMap -> ChunkLoadFailed ( a_ChunkX , a_ChunkZ );
2012-06-14 13:06:06 +00:00
}
2013-06-12 07:14:06 +00: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 13:06:06 +00:00
{
2012-06-16 15:06:14 +00:00
AString Line1 ( a_Line1 );
AString Line2 ( a_Line2 );
AString Line3 ( a_Line3 );
AString Line4 ( a_Line4 );
2012-09-01 21:31:20 +00:00
if ( cRoot :: Get () -> GetPluginManager () -> CallHookUpdatingSign ( this , a_BlockX , a_BlockY , a_BlockZ , Line1 , Line2 , Line3 , Line4 , a_Player ))
2012-06-16 15:06:14 +00:00
{
2013-06-12 07:14:06 +00: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 15:06:14 +00:00
}
2013-06-12 07:14:06 +00: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 13:06:06 +00:00
}
2014-01-23 14:57:04 +02:00
bool cWorld :: SetCommandBlockCommand ( int a_BlockX , int a_BlockY , int a_BlockZ , const AString & a_Command )
{
class cUpdateCommandBlock : public cCommandBlockCallback
{
AString m_Command ;
public :
2014-09-08 18:56:27 +01:00
cUpdateCommandBlock ( const AString & a_CallbackCommand ) : m_Command ( a_CallbackCommand ) {}
2014-01-23 14:57:04 +02:00
virtual bool Item ( cCommandBlockEntity * a_CommandBlock ) override
{
a_CommandBlock -> SetCommand ( m_Command );
return false ;
}
} CmdBlockCB ( a_Command );
return DoWithCommandBlockAt ( a_BlockX , a_BlockY , a_BlockZ , CmdBlockCB );
}
2014-03-02 16:01:37 +01:00
bool cWorld :: IsTrapdoorOpen ( int a_BlockX , int a_BlockY , int a_BlockZ )
{
2014-03-02 16:16:22 +01:00
BLOCKTYPE Block ;
NIBBLETYPE Meta ;
GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , Block , Meta );
2014-09-30 20:04:49 +02:00
if (( Block != E_BLOCK_TRAPDOOR ) && ( Block != E_BLOCK_IRON_TRAPDOOR ))
2014-03-02 16:01:37 +01:00
{
return false ;
}
return ( Meta & 0x4 ) > 0 ;
}
bool cWorld :: SetTrapdoorOpen ( int a_BlockX , int a_BlockY , int a_BlockZ , bool a_Open )
{
2014-03-02 16:16:22 +01:00
BLOCKTYPE Block ;
NIBBLETYPE Meta ;
GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , Block , Meta );
2014-09-30 20:04:49 +02:00
if (( Block != E_BLOCK_TRAPDOOR ) && ( Block != E_BLOCK_IRON_TRAPDOOR ))
2014-03-02 16:01:37 +01:00
{
return false ;
}
bool IsOpen = ( Meta & 0x4 ) > 0 ;
if ( a_Open != IsOpen )
{
SetBlockMeta ( a_BlockX , a_BlockY , a_BlockZ , Meta ^ 0x4 );
BroadcastSoundParticleEffect ( 1003 , a_BlockX , a_BlockY , a_BlockZ , 0 );
return true ;
}
return false ;
}
2012-06-14 13:06:06 +00:00
void cWorld :: RegenerateChunk ( int a_ChunkX , int a_ChunkZ )
{
m_ChunkMap -> MarkChunkRegenerating ( a_ChunkX , a_ChunkZ );
2014-09-03 00:14:51 +02:00
m_Generator . QueueGenerateChunk ( a_ChunkX , a_ChunkZ , true );
2012-06-14 13:06:06 +00:00
}
void cWorld :: GenerateChunk ( int a_ChunkX , int a_ChunkZ )
{
2014-09-29 21:34:15 +02:00
m_ChunkMap -> TouchChunk ( a_ChunkX , a_ChunkZ );
2012-06-14 13:06:06 +00:00
}
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 16:30:17 +00: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 13:06:06 +00:00
void cWorld :: SaveAllChunks ( void )
{
2012-11-01 21:38:20 +00:00
m_LastSave = m_WorldAge ;
2012-06-14 13:06:06 +00:00
m_ChunkMap -> SaveAllChunks ();
}
2013-08-11 21:05:44 +02:00
void cWorld :: QueueSaveAllChunks ( void )
{
2014-10-10 15:33:19 +01:00
QueueTask ( make_unique < cWorld :: cTaskSaveAllChunks > ());
2013-08-11 21:05:44 +02:00
}
2014-10-10 15:33:19 +01:00
void cWorld :: QueueTask ( std :: unique_ptr < cTask > a_Task )
2013-08-11 21:05:44 +02:00
{
cCSLock Lock ( m_CSTasks );
2014-10-10 15:33:19 +01:00
m_Tasks . push_back ( std :: move ( a_Task ));
2013-08-11 21:05:44 +02:00
}
2014-01-19 23:49:19 +01:00
void cWorld :: ScheduleTask ( int a_DelayTicks , cTask * a_Task )
2014-01-14 12:17:03 -08:00
{
2014-01-19 23:49:19 +01:00
Int64 TargetTick = a_DelayTicks + m_WorldAge ;
// Insert the task into the list of scheduled tasks, ordered by its target tick
2014-01-14 12:17:03 -08:00
cCSLock Lock ( m_CSScheduledTasks );
2014-01-19 23:49:19 +01:00
for ( cScheduledTasks :: iterator itr = m_ScheduledTasks . begin (), end = m_ScheduledTasks . end (); itr != end ; ++ itr )
2014-01-14 12:17:03 -08:00
{
2014-01-19 23:49:19 +01:00
if (( * itr ) -> m_TargetTick >= TargetTick )
2014-01-14 12:17:03 -08:00
{
2014-10-10 15:33:19 +01:00
m_ScheduledTasks . insert ( itr , make_unique < cScheduledTask > ( TargetTick , a_Task ));
2014-01-14 12:17:03 -08:00
return ;
}
}
2014-10-10 15:33:19 +01:00
m_ScheduledTasks . push_back ( make_unique < cScheduledTask > ( TargetTick , a_Task ));
2014-01-14 12:17:03 -08:00
}
2013-08-11 21:05:44 +02:00
2014-06-06 22:31:16 +02:00
2012-11-01 21:38:20 +00:00
void cWorld :: AddEntity ( cEntity * a_Entity )
2012-06-14 13:06:06 +00:00
{
2014-06-06 22:31:16 +02:00
cCSLock Lock ( m_CSEntitiesToAdd );
m_EntitiesToAdd . push_back ( a_Entity );
2013-04-13 21:02:10 +00:00
}
bool cWorld :: HasEntity ( int a_UniqueID )
{
2014-06-06 22:31:16 +02:00
// Check if the entity is in the queue to be added to the world:
{
cCSLock Lock ( m_CSEntitiesToAdd );
for ( cEntityList :: const_iterator itr = m_EntitiesToAdd . begin (), end = m_EntitiesToAdd . end (); itr != end ; ++ itr )
{
if (( * itr ) -> GetUniqueID () == a_UniqueID )
{
return true ;
}
} // for itr - m_EntitiesToAdd[]
}
// Check if the entity is in the chunkmap:
2013-04-13 21:02:10 +00:00
return m_ChunkMap -> HasEntity ( a_UniqueID );
2012-06-14 13:06:06 +00:00
}
2013-08-11 19:18:06 +02:00
/*
2012-11-01 21:38:20 +00:00
unsigned int cWorld::GetNumPlayers(void)
2012-06-14 13:06:06 +00:00
{
cCSLock Lock(m_CSPlayers);
2014-07-17 16:33:09 +02:00
return m_Players.size();
2012-06-14 13:06:06 +00:00
}
2013-08-11 19:18:06 +02:00
*/
2012-06-14 13:06:06 +00: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 08:56:12 +00:00
2013-09-16 09:25:23 +02:00
void cWorld :: TickQueuedBlocks ( void )
2012-07-15 20:36:34 +00:00
{
2012-10-13 08:56:12 +00:00
if ( m_BlockTickQueue . empty ())
{
2012-07-15 20:36:34 +00:00
return ;
2012-10-13 08:56:12 +00:00
}
2012-07-15 20:36:34 +00:00
m_BlockTickQueueCopy . clear ();
m_BlockTickQueue . swap ( m_BlockTickQueueCopy );
2014-04-18 21:09:44 +02:00
for ( std :: vector < BlockTickQueueItem *>:: iterator itr = m_BlockTickQueueCopy . begin (); itr != m_BlockTickQueueCopy . end (); ++ itr )
2012-07-15 20:36:34 +00:00
{
2013-11-30 15:58:27 +01:00
BlockTickQueueItem * Block = ( * itr );
2013-09-16 09:25:23 +02:00
Block -> TicksToWait -= 1 ;
if ( Block -> TicksToWait <= 0 )
2012-07-15 20:36:34 +00:00
{
2013-09-16 09:25:23 +02:00
// TODO: Handle the case when the chunk is already unloaded
2013-11-30 15:58:27 +01:00
m_ChunkMap -> TickBlock ( Block -> X , Block -> Y , Block -> Z );
2014-07-17 22:15:34 +02:00
delete Block ; // We don't have to remove it from the vector, this will happen automatically on the next tick
2012-10-13 08:56:12 +00:00
}
else
{
2014-07-17 16:33:09 +02:00
m_BlockTickQueue . push_back ( Block ); // Keep the block in the queue
2012-07-15 20:36:34 +00:00
}
2012-10-13 08:56:12 +00:00
} // for itr - m_BlockTickQueueCopy[]
2012-07-15 20:36:34 +00:00
}
2012-10-13 08:56:12 +00:00
2013-09-16 09:25:23 +02:00
void cWorld :: QueueBlockForTick ( int a_BlockX , int a_BlockY , int a_BlockZ , int a_TicksToWait )
2012-07-15 20:36:34 +00:00
{
2012-10-13 08:56:12 +00:00
BlockTickQueueItem * Block = new BlockTickQueueItem ;
Block -> X = a_BlockX ;
Block -> Y = a_BlockY ;
Block -> Z = a_BlockZ ;
2013-09-16 09:25:23 +02:00
Block -> TicksToWait = a_TicksToWait ;
2012-07-15 20:36:34 +00:00
m_BlockTickQueue . push_back ( Block );
}
2012-10-13 08:56:12 +00:00
bool cWorld :: IsBlockDirectlyWatered ( int a_BlockX , int a_BlockY , int a_BlockZ )
2012-07-15 20:36:34 +00:00
{
2012-10-13 08:56:12 +00: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 ))
);
}
2014-09-17 18:40:10 +01:00
int cWorld :: SpawnMob ( double a_PosX , double a_PosY , double a_PosZ , eMonsterType a_MonsterType )
2012-10-28 14:57:35 +00:00
{
cMonster * Monster = NULL ;
2013-09-08 02:47:02 +02:00
2013-10-20 13:25:56 +02:00
Monster = cMonster :: NewMonsterFromType ( a_MonsterType );
if ( Monster != NULL )
{
2013-10-14 11:12:12 -06:00
Monster -> SetPosition ( a_PosX , a_PosY , a_PosZ );
2013-10-20 13:25:56 +02:00
}
2013-11-10 20:48:12 +00:00
2013-09-08 02:47:02 +02:00
return SpawnMobFinalize ( Monster );
}
2013-10-20 13:25:56 +02:00
int cWorld :: SpawnMobFinalize ( cMonster * a_Monster )
2013-09-08 02:47:02 +02:00
{
2014-07-24 12:15:48 +02:00
// Invalid cMonster object. Bail out.
2013-10-14 11:12:12 -06:00
if ( ! a_Monster )
2014-07-24 12:15:48 +02:00
{
2013-10-14 11:12:12 -06:00
return - 1 ;
2014-07-24 12:15:48 +02:00
}
// Give the mob full health.
2013-09-08 02:47:02 +02:00
a_Monster -> SetHealth ( a_Monster -> GetMaxHealth ());
2014-07-24 12:15:48 +02:00
// A plugin doesn't agree with the spawn. bail out.
2013-09-08 02:47:02 +02:00
if ( cPluginManager :: Get () -> CallHookSpawningMonster ( * this , * a_Monster ))
2013-08-08 09:13:13 +02:00
{
2013-09-08 02:47:02 +02:00
delete a_Monster ;
2014-06-19 01:49:56 -07:00
a_Monster = NULL ;
2013-08-08 09:13:13 +02:00
return - 1 ;
}
2014-07-24 12:15:48 +02:00
// Initialize the monster into the current world.
2014-06-08 21:58:08 +02:00
if ( ! a_Monster -> Initialize ( * this ))
2013-08-08 09:13:13 +02:00
{
2013-09-08 02:47:02 +02:00
delete a_Monster ;
2014-06-19 01:49:56 -07:00
a_Monster = NULL ;
2013-08-08 09:13:13 +02:00
return - 1 ;
}
2014-07-24 12:15:48 +02:00
2013-09-08 02:47:02 +02:00
BroadcastSpawnEntity ( * a_Monster );
cPluginManager :: Get () -> CallHookSpawnedMonster ( * this , * a_Monster );
2013-10-08 19:20:49 +01:00
2013-09-08 02:47:02 +02:00
return a_Monster -> GetUniqueID ();
2012-10-28 14:57:35 +00:00
}
2014-07-13 14:09:08 +01:00
int cWorld :: CreateProjectile ( double a_PosX , double a_PosY , double a_PosZ , cProjectileEntity :: eKind a_Kind , cEntity * a_Creator , const cItem * a_Item , const Vector3d * a_Speed )
2013-08-22 08:55:58 +02:00
{
2014-02-26 23:29:14 +00:00
cProjectileEntity * Projectile = cProjectileEntity :: Create ( a_Kind , a_Creator , a_PosX , a_PosY , a_PosZ , a_Item , a_Speed );
2013-08-22 08:55:58 +02:00
if ( Projectile == NULL )
{
return - 1 ;
}
2014-06-08 21:58:08 +02:00
if ( ! Projectile -> Initialize ( * this ))
2013-08-22 08:55:58 +02:00
{
delete Projectile ;
2014-06-19 01:49:56 -07:00
Projectile = NULL ;
2013-08-22 08:55:58 +02:00
return - 1 ;
}
return Projectile -> GetUniqueID ();
}
2013-07-30 22:48:59 +02:00
void cWorld :: TabCompleteUserName ( const AString & a_Text , AStringVector & a_Results )
{
2014-08-01 22:15:14 +01:00
typedef std :: pair < AString :: size_type , AString > pair_t ;
size_t LastSpace = a_Text . find_last_of ( " " ); // Find the position of the last space
AString LastWord = a_Text . substr ( LastSpace + 1 , a_Text . length ()); // Find the last word
if ( LastWord . empty ())
{
return ;
}
std :: vector < pair_t > UsernamesByWeight ;
2013-07-31 11:16:11 +02:00
cCSLock Lock ( m_CSPlayers );
for ( cPlayerList :: iterator itr = m_Players . begin (), end = m_Players . end (); itr != end ; ++ itr )
{
2014-03-01 21:25:27 +00:00
AString PlayerName (( * itr ) -> GetName ());
2014-09-02 19:12:35 +02:00
if (( * itr ) -> HasCustomName ())
{
PlayerName = ( * itr ) -> GetCustomName ();
}
2014-08-01 22:15:14 +01:00
AString :: size_type Found = PlayerName . find ( LastWord ); // Try to find last word in playername
2014-03-01 21:25:27 +00:00
if ( Found == AString :: npos )
2013-07-31 11:16:11 +02:00
{
2014-07-17 19:13:23 +02:00
continue ; // No match
2013-07-31 11:16:11 +02:00
}
2014-09-02 19:12:35 +02:00
2014-08-01 22:15:14 +01:00
UsernamesByWeight . push_back ( std :: make_pair ( Found , PlayerName )); // Match! Store it with the position of the match as a weight
}
Lock . Unlock ();
std :: sort ( UsernamesByWeight . begin (), UsernamesByWeight . end ()); // Sort lexicographically (by the first value, then second), so higher weights (usernames with match closer to start) come first (#1274)
/* TODO: Uncomment once migrated to C++11
std::transform(
UsernamesByWeight.begin(),
UsernamesByWeight.end(),
std::back_inserter(a_Results),
[](const pair_t & p) { return p.first; }
);
*/
a_Results . reserve ( UsernamesByWeight . size ());
for ( std :: vector < pair_t >:: const_iterator itr = UsernamesByWeight . begin (); itr != UsernamesByWeight . end (); ++ itr )
{
a_Results . push_back ( itr -> second );
2013-07-31 11:16:11 +02:00
}
2013-07-30 22:48:59 +02:00
}
2014-07-10 18:18:32 +02:00
void cWorld :: SetChunkAlwaysTicked ( int a_ChunkX , int a_ChunkZ , bool a_AlwaysTicked )
{
m_ChunkMap -> SetChunkAlwaysTicked ( a_ChunkX , a_ChunkZ , a_AlwaysTicked );
}
2014-09-11 17:48:21 +01:00
cRedstoneSimulator < cChunk , cWorld > * cWorld :: InitializeRedstoneSimulator ( cIniFile & a_IniFile )
2014-02-07 22:13:55 +01:00
{
2014-07-31 23:11:51 +02:00
AString SimulatorName = a_IniFile . GetValueSet ( "Physics" , "RedstoneSimulator" , "Incremental" );
2014-02-07 22:13:55 +01:00
if ( SimulatorName . empty ())
{
2014-07-31 23:11:51 +02:00
LOGWARNING ( "[Physics] RedstoneSimulator not present or empty in %s, using the default of \" Incremental \" ." , GetIniFileName (). c_str ());
SimulatorName = "Incremental" ;
2014-02-07 22:13:55 +01:00
}
2014-09-11 17:48:21 +01:00
cRedstoneSimulator < cChunk , cWorld > * res = NULL ;
2014-02-07 22:13:55 +01:00
2014-07-31 23:11:51 +02:00
if ( NoCaseCompare ( SimulatorName , "Incremental" ) == 0 )
2014-02-07 22:13:55 +01:00
{
2014-09-16 20:29:31 +01:00
res = MakeIncrementalRedstoneSimulator ( * this );
2014-02-07 22:13:55 +01:00
}
2014-02-07 22:59:08 +01:00
else if ( NoCaseCompare ( SimulatorName , "noop" ) == 0 )
2014-02-07 22:13:55 +01:00
{
res = new cRedstoneNoopSimulator ( * this );
}
m_SimulatorManager -> RegisterSimulator ( res , 1 );
return res ;
}
2012-10-13 09:53:28 +00: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 );
2014-07-31 18:21:45 +02:00
AString SimulatorName = a_IniFile . GetValueSet ( "Physics" , SimulatorNameKey , "Vanilla" );
2012-10-13 09:53:28 +00:00
if ( SimulatorName . empty ())
{
2014-03-05 15:54:38 +02:00
LOGWARNING ( "[Physics] %s not present or empty in %s, using the default of \" Vanilla \" ." , SimulatorNameKey . c_str (), GetIniFileName (). c_str ());
SimulatorName = "Vanilla" ;
2012-10-13 09:53:28 +00:00
}
cFluidSimulator * res = NULL ;
2012-10-14 20:10:03 +00:00
bool IsWater = ( strcmp ( a_FluidName , "Water" ) == 0 ); // Used for defaults
2012-10-15 20:16:43 +00:00
int Rate = 1 ;
2013-03-14 20:46:40 +00:00
if (
2013-03-14 19:44:27 +00:00
( NoCaseCompare ( SimulatorName , "vaporize" ) == 0 ) ||
( NoCaseCompare ( SimulatorName , "vaporise" ) == 0 )
)
{
res = new cVaporizeFluidSimulator ( * this , a_SimulateBlock , a_StationaryBlock );
}
2013-03-14 20:03:42 +00: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 09:53:28 +00:00
else
{
2013-03-14 20:46:40 +00: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 );
2014-09-08 18:56:27 +01:00
if (( Falloff > 15 ) || ( Falloff < 0 ))
{
LOGWARNING ( "Falloff for %s simulator is out of range, assuming default of %d" , a_FluidName , IsWater ? 1 : 2 );
2014-09-09 12:18:20 +01:00
Falloff = IsWater ? 1 : 2 ;
2014-09-08 18:56:27 +01:00
}
2014-03-05 15:54:38 +02:00
if ( NoCaseCompare ( SimulatorName , "floody" ) == 0 )
{
2014-09-08 18:56:27 +01:00
res = new cFloodyFluidSimulator ( * this , a_SimulateBlock , a_StationaryBlock , static_cast < NIBBLETYPE > ( Falloff ), TickDelay , NumNeighborsForSource );
2014-03-05 15:54:38 +02:00
}
else if ( NoCaseCompare ( SimulatorName , "vanilla" ) == 0 )
{
2014-09-08 18:56:27 +01:00
res = new cVanillaFluidSimulator ( * this , a_SimulateBlock , a_StationaryBlock , static_cast < NIBBLETYPE > ( Falloff ), TickDelay , NumNeighborsForSource );
2014-03-05 15:54:38 +02:00
}
else
{
// The simulator name doesn't match anything we have, issue a warning:
LOGWARNING ( "%s [Physics]:%s specifies an unknown simulator, using the default \" Vanilla \" ." , GetIniFileName (). c_str (), SimulatorNameKey . c_str ());
2014-09-08 18:56:27 +01:00
res = new cVanillaFluidSimulator ( * this , a_SimulateBlock , a_StationaryBlock , static_cast < NIBBLETYPE > ( Falloff ), TickDelay , NumNeighborsForSource );
2014-03-05 15:54:38 +02:00
}
2012-10-13 09:53:28 +00:00
}
2012-10-15 20:16:43 +00:00
m_SimulatorManager -> RegisterSimulator ( res , Rate );
2012-10-13 09:53:28 +00:00
return res ;
}
2013-08-11 21:05:44 +02:00
2014-02-11 14:38:28 -05:00
2014-02-13 21:36:24 +02:00
2014-06-08 21:58:08 +02:00
void cWorld :: AddQueuedPlayers ( void )
{
ASSERT ( m_TickThread . IsCurrentThread ());
// Grab the list of players to add, it has to be locked to access it:
cPlayerList PlayersToAdd ;
{
cCSLock Lock ( m_CSPlayersToAdd );
std :: swap ( PlayersToAdd , m_PlayersToAdd );
}
// Add all the players in the grabbed list:
{
cCSLock Lock ( m_CSPlayers );
for ( cPlayerList :: iterator itr = PlayersToAdd . begin (), end = PlayersToAdd . end (); itr != end ; ++ itr )
{
2014-08-14 01:03:30 +02:00
ASSERT ( std :: find ( m_Players . begin (), m_Players . end (), * itr ) == m_Players . end ()); // Is it already in the list? HOW?
2014-06-12 15:21:07 +01:00
LOGD ( "Adding player %s to world \" %s \" ." , ( * itr ) -> GetName (). c_str (), m_WorldName . c_str ());
2014-07-22 17:26:48 +01:00
2014-06-08 21:58:08 +02:00
m_Players . push_back ( * itr );
( * itr ) -> SetWorld ( this );
// Add to chunkmap, if not already there (Spawn vs MoveToWorld):
2014-06-10 18:25:53 +02:00
m_ChunkMap -> AddEntityIfNotPresent ( * itr );
2014-06-08 21:58:08 +02:00
} // for itr - PlayersToAdd[]
} // Lock(m_CSPlayers)
// Add all the players' clienthandles:
{
cCSLock Lock ( m_CSClients );
for ( cPlayerList :: iterator itr = PlayersToAdd . begin (), end = PlayersToAdd . end (); itr != end ; ++ itr )
{
cClientHandle * Client = ( * itr ) -> GetClientHandle ();
if ( Client != NULL )
{
m_Clients . push_back ( Client );
}
} // for itr - PlayersToAdd[]
} // Lock(m_CSClients)
// Stream chunks to all eligible clients:
for ( cPlayerList :: iterator itr = PlayersToAdd . begin (), end = PlayersToAdd . end (); itr != end ; ++ itr )
{
cClientHandle * Client = ( * itr ) -> GetClientHandle ();
if ( Client != NULL )
{
Client -> StreamChunks ();
2014-06-12 15:21:07 +01:00
Client -> SendPlayerMoveLook ();
Client -> SendHealth ();
Client -> SendWholeInventory ( * ( * itr ) -> GetWindow ());
2014-06-08 21:58:08 +02:00
}
} // for itr - PlayersToAdd[]
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-08-11 21:05:44 +02:00
// cWorld::cTaskSaveAllChunks:
void cWorld :: cTaskSaveAllChunks :: Run ( cWorld & a_World )
{
a_World . SaveAllChunks ();
}
2013-10-29 10:44:51 -06:00
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2014-02-10 20:00:07 -05:00
// cWorld::cTaskUnloadUnusedChunks
2013-08-11 21:05:44 +02:00
2014-02-10 20:00:07 -05:00
void cWorld :: cTaskUnloadUnusedChunks :: Run ( cWorld & a_World )
{
a_World . UnloadUnusedChunks ();
}
2013-08-11 21:05:44 +02:00
2013-10-29 10:44:51 -06:00
2014-02-11 14:38:28 -05:00
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2014-05-25 13:46:34 +01:00
// cWorld::cTaskSendBlockTo
2014-05-29 11:57:06 +01:00
cWorld :: cTaskSendBlockToAllPlayers :: cTaskSendBlockToAllPlayers ( std :: vector < Vector3i > & a_SendQueue ) :
m_SendQueue ( a_SendQueue )
2014-05-25 13:46:34 +01:00
{
}
void cWorld :: cTaskSendBlockToAllPlayers :: Run ( cWorld & a_World )
{
class cPlayerCallback :
public cPlayerListCallback
{
public :
2014-09-08 18:56:27 +01:00
cPlayerCallback ( std :: vector < Vector3i > & a_SendQueue , cWorld & a_CallbackWorld ) :
2014-05-29 11:57:06 +01:00
m_SendQueue ( a_SendQueue ),
2014-09-08 18:56:27 +01:00
m_World ( a_CallbackWorld )
2014-05-25 13:46:34 +01:00
{
}
virtual bool Item ( cPlayer * a_Player )
{
2014-05-29 11:57:06 +01:00
for ( std :: vector < Vector3i >:: const_iterator itr = m_SendQueue . begin (); itr != m_SendQueue . end (); ++ itr )
{
m_World . SendBlockTo ( itr -> x , itr -> y , itr -> z , a_Player );
}
2014-05-25 13:46:34 +01:00
return false ;
}
private :
2014-05-29 11:57:06 +01:00
std :: vector < Vector3i > m_SendQueue ;
2014-05-25 13:46:34 +01:00
cWorld & m_World ;
2014-05-29 11:57:06 +01:00
} PlayerCallback ( m_SendQueue , a_World );
2014-05-25 13:46:34 +01:00
a_World . ForEachPlayer ( PlayerCallback );
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2014-01-10 22:22:54 +01:00
// cWorld::cChunkGeneratorCallbacks:
cWorld :: cChunkGeneratorCallbacks :: cChunkGeneratorCallbacks ( cWorld & a_World ) :
m_World ( & a_World )
{
}
void cWorld :: cChunkGeneratorCallbacks :: OnChunkGenerated ( cChunkDesc & a_ChunkDesc )
{
cChunkDef :: BlockNibbles BlockMetas ;
a_ChunkDesc . CompressBlockMetas ( BlockMetas );
2014-08-29 19:19:27 +03:00
cSetChunkDataPtr SetChunkData ( new cSetChunkData (
2014-01-10 22:22:54 +01:00
a_ChunkDesc . GetChunkX (), a_ChunkDesc . GetChunkZ (),
a_ChunkDesc . GetBlockTypes (), BlockMetas ,
NULL , NULL , // We don't have lighting, chunk will be lighted when needed
& a_ChunkDesc . GetHeightMap (), & a_ChunkDesc . GetBiomeMap (),
a_ChunkDesc . GetEntities (), a_ChunkDesc . GetBlockEntities (),
true
2014-08-29 19:19:27 +03:00
));
SetChunkData -> RemoveInvalidBlockEntities ();
m_World -> QueueSetChunkData ( SetChunkData );
2014-01-10 22:22:54 +01:00
}
bool cWorld :: cChunkGeneratorCallbacks :: IsChunkValid ( int a_ChunkX , int a_ChunkZ )
{
return m_World -> IsChunkValid ( a_ChunkX , a_ChunkZ );
}
2014-09-05 22:16:48 +02:00
bool cWorld :: cChunkGeneratorCallbacks :: IsChunkQueued ( int a_ChunkX , int a_ChunkZ )
{
return m_World -> IsChunkQueued ( a_ChunkX , a_ChunkZ );
}
2014-01-10 22:22:54 +01:00
bool cWorld :: cChunkGeneratorCallbacks :: HasChunkAnyClients ( int a_ChunkX , int a_ChunkZ )
{
return m_World -> HasChunkAnyClients ( a_ChunkX , a_ChunkZ );
}
void cWorld :: cChunkGeneratorCallbacks :: CallHookChunkGenerating ( cChunkDesc & a_ChunkDesc )
{
cPluginManager :: Get () -> CallHookChunkGenerating (
m_World , a_ChunkDesc . GetChunkX (), a_ChunkDesc . GetChunkZ (), & a_ChunkDesc
);
}
void cWorld :: cChunkGeneratorCallbacks :: CallHookChunkGenerated ( cChunkDesc & a_ChunkDesc )
{
cPluginManager :: Get () -> CallHookChunkGenerated (
m_World , a_ChunkDesc . GetChunkX (), a_ChunkDesc . GetChunkZ (), & a_ChunkDesc
);
}