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"
2014-10-23 15:15:10 +02:00
#include "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"
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):
2016-05-29 10:30:47 +02:00
#include "Entities/Boat.h"
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"
2015-06-26 17:24:51 -05:00
#include "Simulator/IncrementalRedstoneSimulator/IncrementalRedstoneSimulator.h"
2012-10-13 09:53:28 +00:00
#include "Simulator/SandSimulator.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-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
2015-03-21 17:17:26 +00:00
#include "Broadcaster.h"
2015-05-30 00:18:52 +01:00
#include "SpawnPrepare.h"
2015-07-06 15:41:08 -06:00
#include "FastRandom.h"
2012-06-14 13:06:06 +00:00
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
////////////////////////////////////////////////////////////////////////////////
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 )
{
2014-10-20 18:59:40 +01:00
auto LastTime = std :: chrono :: steady_clock :: now ();
2015-01-11 01:54:18 +00:00
auto TickTime = std :: chrono :: duration_cast < std :: chrono :: milliseconds > ( cTickTime ( 1 ));
2013-08-19 22:23:25 +02:00
2013-08-11 19:18:06 +02:00
while ( ! m_ShouldTerminate )
2013-03-11 17:15:34 +00:00
{
2014-10-20 18:59:40 +01:00
auto NowTime = std :: chrono :: steady_clock :: now ();
2015-01-11 21:12:26 +00:00
auto WaitTime = std :: chrono :: duration_cast < std :: chrono :: milliseconds > ( NowTime - LastTime );
m_World . Tick ( WaitTime , TickTime );
2015-01-11 01:54:18 +00:00
TickTime = std :: chrono :: duration_cast < std :: chrono :: milliseconds > ( std :: chrono :: steady_clock :: now () - NowTime );
2016-01-12 15:04:59 +02:00
2015-01-11 01:54:18 +00:00
if ( TickTime < cTickTime ( 1 ))
2013-03-11 17:15:34 +00:00
{
2015-01-11 01:54:18 +00:00
// Stretch tick time until it's at least 1 tick
std :: this_thread :: sleep_for ( cTickTime ( 1 ) - TickTime );
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
2015-03-21 15:20:31 +01:00
cWorld :: cWorld ( const AString & a_WorldName , eDimension a_Dimension , const AString & a_LinkedOverworldName ) :
2013-08-11 19:18:06 +02:00
m_WorldName ( a_WorldName ),
2015-03-21 15:20:31 +01:00
m_LinkedOverworldName ( a_LinkedOverworldName ),
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 ),
2015-01-23 13:51:07 +03:00
m_SpawnX ( 0 ),
m_SpawnY ( 0 ),
m_SpawnZ ( 0 ),
m_BroadcastDeathMessages ( true ),
m_BroadcastAchievementMessages ( true ),
2014-08-10 16:46:03 +02:00
m_IsDaylightCycleEnabled ( true ),
2012-11-01 21:38:20 +00:00
m_WorldAge ( 0 ),
m_TimeOfDay ( 0 ),
m_LastTimeUpdate ( 0 ),
2014-12-21 22:19:22 +01:00
m_LastUnload ( 0 ),
2014-12-21 23:02:02 -05:00
m_LastSave ( 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-10-20 21:55:07 +01:00
m_WaterSimulator ( nullptr ),
2014-10-10 15:33:19 +01:00
m_LavaSimulator ( nullptr ),
m_FireSimulator (),
2014-10-20 21:55:07 +01:00
m_RedstoneSimulator ( nullptr ),
2014-08-21 21:53:25 +02:00
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 ),
2015-09-28 01:02:17 +02:00
m_WeatherInterval ( 24000 ), // Guaranteed 1 game-day of sunshine at server start :)
m_MaxSunnyTicks ( 180000 ), // 150 real-world minutes -+
m_MinSunnyTicks ( 12000 ), // 10 real-world minutes |
m_MaxRainTicks ( 24000 ), // 20 real-world minutes +- all values adapted from Vanilla 1.7.2
m_MinRainTicks ( 12000 ), // 10 real-world minutes |
m_MaxThunderStormTicks ( 15600 ), // 13 real-world minutes |
m_MinThunderStormTicks ( 3600 ), // 3 real-world minutes -+
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 ),
2016-05-29 22:10:35 +02:00
m_IsBigFlowerBonemealable ( true ),
m_IsTallGrassBonemealable ( true ),
2014-08-21 21:53:25 +02:00
m_bCommandBlocksEnabled ( true ),
m_bUseChatPrefixes ( false ),
m_TNTShrapnelLevel ( slNone ),
2014-10-30 21:24:10 +01:00
m_MaxViewDistance ( 12 ),
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 ),
2015-06-10 15:16:05 +01:00
m_ChunkSender ( * 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-10-20 21:55:07 +01:00
delete m_WaterSimulator ; m_WaterSimulator = nullptr ;
delete m_LavaSimulator ; m_LavaSimulator = nullptr ;
delete m_RedstoneSimulator ; m_RedstoneSimulator = nullptr ;
2013-01-12 04:46:01 +00:00
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 ();
2014-12-07 16:14:27 +01:00
// Explicitly destroy the chunkmap, so that it's guaranteed to be destroyed before the other internals
// This fixes crashes on stopping the server, because chunk destructor deletes entities and those access the world.
m_ChunkMap . reset ();
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 );
2015-05-15 18:10:34 -07:00
BroadcastSoundEffect ( "ambient.weather.thunder" , a_BlockX , a_BlockY , a_BlockZ , 50 , 1 );
2013-02-13 19:22:08 +00:00
}
2014-03-02 21:04:01 +01:00
int cWorld :: GetDefaultWeatherInterval ( eWeather a_Weather )
{
switch ( a_Weather )
{
case eWeather_Sunny :
{
2015-09-28 01:02:17 +02:00
auto dif = m_MaxSunnyTicks - m_MinSunnyTicks + 1 ;
return m_MinSunnyTicks + ( m_TickRand . randInt () % dif );
2014-03-02 21:04:01 +01:00
}
case eWeather_Rain :
{
2015-09-28 01:02:17 +02:00
auto dif = m_MaxRainTicks - m_MinRainTicks + 1 ;
return m_MinRainTicks + ( m_TickRand . randInt () % dif );
2014-03-02 21:04:01 +01:00
}
case eWeather_ThunderStorm :
{
2015-09-28 01:02:17 +02:00
auto dif = m_MaxThunderStormTicks - m_MinThunderStormTicks + 1 ;
return m_MinThunderStormTicks + ( m_TickRand . randInt () % dif );
2014-03-02 21:04:01 +01:00
}
2014-04-24 20:25:03 -07:00
}
2015-09-28 01:02:17 +02:00
#ifndef __clang__
ASSERT ( ! "Unknown weather" );
return - 1 ;
#endif
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 ;
}
2016-01-12 15:04:59 +02:00
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 );
2016-01-12 15:04:59 +02:00
2014-03-02 21:04:01 +01:00
// 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 ;
}
2016-01-12 15:04:59 +02:00
2013-02-13 19:22:08 +00:00
m_Weather = a_NewWeather ;
BroadcastWeather ( m_Weather );
2016-01-12 15:04:59 +02:00
2013-02-13 19:22:08 +00:00
// 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 )
{
2015-11-13 15:08:16 +00:00
// For the debugging builds, don't make the server build too much world upon start:
#if defined(_DEBUG) || defined(ANDROID_NDK)
const int DefaultViewDist = 9 ;
#else
const int DefaultViewDist = 20 ; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is
#endif // _DEBUG
2014-07-17 19:13:23 +02:00
if ( ! m_IsSpawnExplicitlySet )
2013-11-26 21:54:40 +00:00
{
2015-11-13 15:08:16 +00:00
// Spawn position wasn't already explicitly set, enumerate random solid-land coordinate and then write it to the world configuration:
GenerateRandomSpawn ( DefaultViewDist );
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-05-09 18:31:48 +02:00
cIniFile IniFile ;
IniFile . ReadFile ( m_IniFileName );
int ViewDist = IniFile . GetValueSetI ( "SpawnPosition" , "PregenerateDistance" , DefaultViewDist );
IniFile . WriteFile ( m_IniFileName );
2014-01-02 12:32:55 +00:00
2015-11-13 15:08:16 +00:00
int ChunkX = 0 , ChunkZ = 0 ;
cChunkDef :: BlockToChunk ( FloorC ( m_SpawnX ), FloorC ( m_SpawnZ ), ChunkX , ChunkZ );
2015-05-30 00:18:52 +01:00
cSpawnPrepare :: PrepareChunks ( * this , ChunkX , ChunkZ , ViewDist );
2016-01-12 15:04:59 +02:00
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 ;
}
2016-01-12 15:04:59 +02:00
2013-08-04 16:08:09 +02:00
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 );
2014-10-30 21:24:10 +01:00
SetMaxViewDistance ( IniFile . GetValueSetI ( "SpawnPosition" , "MaxViewDistance" , 12 ));
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 );
2016-05-29 22:10:35 +02:00
m_IsBigFlowerBonemealable = IniFile . GetValueSetB ( "Plants" , "IsBigFlowerBonemealable" , true );
m_IsTallGrassBonemealable = IniFile . GetValueSetB ( "Plants" , "IsTallGrassBonemealable" , true );
2014-03-20 09:28:29 +01:00
m_IsDeepSnowEnabled = IniFile . GetValueSetB ( "Physics" , "DeepSnow" , true );
m_ShouldLavaSpawnFire = IniFile . GetValueSetB ( "Physics" , "ShouldLavaSpawnFire" , true );
2015-07-29 09:04:03 -06:00
int TNTShrapnelLevel = IniFile . GetValueSetI ( "Physics" , "TNTShrapnelLevel" , static_cast < 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 );
2015-06-13 14:57:51 +02:00
m_MinNetherPortalWidth = IniFile . GetValueSetI ( "Mechanics" , "MinNetherPortalWidth" , 2 );
m_MaxNetherPortalWidth = IniFile . GetValueSetI ( "Mechanics" , "MaxNetherPortalWidth" , 21 );
m_MinNetherPortalHeight = IniFile . GetValueSetI ( "Mechanics" , "MinNetherPortalHeight" , 3 );
m_MaxNetherPortalHeight = IniFile . GetValueSetI ( "Mechanics" , "MaxNetherPortalHeight" , 21 );
2014-03-20 09:28:29 +01:00
m_VillagersShouldHarvestCrops = IniFile . GetValueSetB ( "Monsters" , "VillagersShouldHarvestCrops" , true );
2014-08-10 16:48:20 +02:00
m_IsDaylightCycleEnabled = IniFile . GetValueSetB ( "General" , "IsDaylightCycleEnabled" , true );
2015-07-29 09:04:03 -06:00
int GameMode = IniFile . GetValueSetI ( "General" , "Gamemode" , static_cast < int > ( m_GameMode ));
int Weather = IniFile . GetValueSetI ( "General" , "Weather" , static_cast < int > ( m_Weather ));
2015-09-28 01:02:17 +02:00
// Load the weather frequency data:
if ( m_Dimension == dimOverworld )
{
m_MaxSunnyTicks = IniFile . GetValueSetI ( "Weather" , "MaxSunnyTicks" , m_MaxSunnyTicks );
m_MinSunnyTicks = IniFile . GetValueSetI ( "Weather" , "MinSunnyTicks" , m_MinSunnyTicks );
m_MaxRainTicks = IniFile . GetValueSetI ( "Weather" , "MaxRainTicks" , m_MaxRainTicks );
m_MinRainTicks = IniFile . GetValueSetI ( "Weather" , "MinRainTicks" , m_MinRainTicks );
m_MaxThunderStormTicks = IniFile . GetValueSetI ( "Weather" , "MaxThunderStormTicks" , m_MaxThunderStormTicks );
m_MinThunderStormTicks = IniFile . GetValueSetI ( "Weather" , "MinThunderStormTicks" , m_MinThunderStormTicks );
if ( m_MaxSunnyTicks < m_MinSunnyTicks )
{
std :: swap ( m_MaxSunnyTicks , m_MinSunnyTicks );
}
if ( m_MaxRainTicks < m_MinRainTicks )
{
std :: swap ( m_MaxRainTicks , m_MinRainTicks );
}
if ( m_MaxThunderStormTicks < m_MinThunderStormTicks )
{
std :: swap ( m_MaxThunderStormTicks , m_MinThunderStormTicks );
}
}
2016-01-12 15:04:59 +02:00
2014-07-22 10:33:16 +01:00
if ( GetDimension () == dimOverworld )
{
2016-02-03 23:37:35 +02:00
AString MyNetherName = GetName () + "_nether" ;
AString MyEndName = GetName () + "_end" ;
if ( cRoot :: Get () -> GetWorld ( MyNetherName ) == nullptr )
{
MyNetherName = "" ;
}
if ( cRoot :: Get () -> GetWorld ( MyEndName ) == nullptr )
{
MyEndName = "" ;
}
m_LinkedNetherWorldName = IniFile . GetValueSet ( "LinkedWorlds" , "NetherWorldName" , MyNetherName );
m_LinkedEndWorldName = IniFile . GetValueSet ( "LinkedWorlds" , "EndWorldName" , MyEndName );
2014-07-22 10:33:16 +01:00
}
2014-07-20 10:46:45 +01:00
else
{
2015-03-21 15:20:31 +01:00
m_LinkedOverworldName = IniFile . GetValueSet ( "LinkedWorlds" , "OverworldName" , GetLinkedOverworldName ());
2014-07-20 10:46:45 +01:00
}
2016-01-12 15:04:59 +02:00
2016-04-18 22:29:39 +03:00
// If we are linked to one or more worlds that do not exist, unlink them
2016-02-04 16:06:37 +02:00
cRoot * Root = cRoot :: Get ();
if ( GetDimension () == dimOverworld )
{
if (( ! m_LinkedNetherWorldName . empty ()) && ( Root -> GetWorld ( m_LinkedNetherWorldName ) == nullptr ))
{
2016-04-18 22:29:39 +03:00
IniFile . SetValue ( "LinkedWorlds" , "NetherWorldName" , "" );
LOG ( "%s Is linked to a nonexisting nether world called \" %s \" . The server has modified \" %s/world.ini \" and removed this invalid link." ,
GetName (). c_str (), m_LinkedNetherWorldName . c_str (), GetName (). c_str ());
m_LinkedNetherWorldName = "" ;
2016-02-04 16:06:37 +02:00
}
if (( ! m_LinkedEndWorldName . empty ()) && ( Root -> GetWorld ( m_LinkedEndWorldName ) == nullptr ))
{
2016-04-18 22:29:39 +03:00
IniFile . SetValue ( "LinkedWorlds" , "EndWorldName" , "" );
LOG ( "%s Is linked to a nonexisting end world called \" %s \" . The server has modified \" %s/world.ini \" and removed this invalid link." ,
GetName (). c_str (), m_LinkedEndWorldName . c_str (), GetName (). c_str ());
m_LinkedEndWorldName = "" ;
2016-02-04 16:06:37 +02:00
}
}
else
{
if (( ! m_LinkedOverworldName . empty ()) && ( Root -> GetWorld ( m_LinkedOverworldName ) == nullptr ))
{
2016-04-18 22:29:39 +03:00
IniFile . SetValue ( "LinkedWorlds" , "OverworldName" , "" );
LOG ( "%s Is linked to a nonexisting overworld called \" %s \" . The server has modified \" %s/world.ini \" and removed this invalid link." ,
GetName (). c_str (), m_LinkedOverworldName . c_str (), GetName (). c_str ());
m_LinkedOverworldName = "" ;
2016-02-04 16:06:37 +02:00
}
}
2016-04-18 22:29:39 +03:00
2016-02-04 16:06:37 +02:00
2014-03-20 09:28:29 +01:00
// Adjust the enum-backed variables into their respective bounds:
2015-07-29 09:04:03 -06:00
m_GameMode = static_cast < eGameMode > ( Clamp < int > ( GameMode , gmSurvival , gmSpectator ));
m_TNTShrapnelLevel = static_cast < eShrapnelLevel > ( Clamp < int > ( TNTShrapnelLevel , slNone , slAll ));
m_Weather = static_cast < eWeather > ( Clamp < int > ( Weather , wSunny , wStorm ));
2013-08-11 20:16:41 +02:00
2014-07-21 22:49:06 +01:00
InitialiseGeneratorDefaults ( IniFile );
InitialiseAndLoadMobSpawningValues ( IniFile );
2015-01-11 21:12:26 +00:00
SetTimeOfDay ( IniFile . GetValueSetI ( "General" , "TimeInTicks" , GetTimeOfDay ()));
2013-08-11 20:16:41 +02:00
2015-05-15 13:57:27 +01:00
m_ChunkMap = cpp14 :: make_unique < cChunkMap > ( this );
2016-01-12 15:04:59 +02:00
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:
2015-05-15 13:57:27 +01:00
m_SimulatorManager = cpp14 :: 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 );
2015-05-15 13:57:27 +01:00
m_SandSimulator = cpp14 :: make_unique < cSandSimulator > ( * this , IniFile );
m_FireSimulator = cpp14 :: 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 );
2015-06-10 15:16:05 +01:00
m_ChunkSender . Start ();
2013-08-11 20:16:41 +02:00
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)
2015-01-11 21:12:26 +00:00
m_LastSpawnMonster . insert ( std :: map < cMonster :: eFamily , cTickTimeLong >:: value_type ( cMonster :: mfHostile , cTickTimeLong ( 0 )));
m_LastSpawnMonster . insert ( std :: map < cMonster :: eFamily , cTickTimeLong >:: value_type ( cMonster :: mfPassive , cTickTimeLong ( 0 )));
m_LastSpawnMonster . insert ( std :: map < cMonster :: eFamily , cTickTimeLong >:: value_type ( cMonster :: mfAmbient , cTickTimeLong ( 0 )));
m_LastSpawnMonster . insert ( std :: map < cMonster :: eFamily , cTickTimeLong >:: value_type ( cMonster :: mfWater , cTickTimeLong ( 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 ());
}
}
2015-11-13 15:08:16 +00:00
void cWorld :: GenerateRandomSpawn ( int a_MaxSpawnRadius )
2013-11-26 21:54:40 +00:00
{
LOGD ( "Generating random spawnpoint..." );
2015-11-13 15:08:16 +00:00
// Number of checks to make sure we have a valid biome
// 100 checks will check across 400 chunks, we should have
// a valid biome by then.
static const int BiomeCheckCount = 100 ;
// Make sure we are in a valid biome
Vector3i BiomeOffset = Vector3i ( 0 , 0 , 0 );
for ( int BiomeCheckIndex = 0 ; BiomeCheckIndex < BiomeCheckCount ; ++ BiomeCheckIndex )
2013-11-26 21:54:40 +00:00
{
2015-11-13 15:08:16 +00:00
EMCSBiome Biome = GetBiomeAt ( BiomeOffset . x , BiomeOffset . z );
if (( Biome == EMCSBiome :: biOcean ) || ( Biome == EMCSBiome :: biFrozenOcean ))
{
BiomeOffset += Vector3d ( cChunkDef :: Width * 4 , 0 , 0 );
continue ;
}
2015-03-12 19:37:39 +01:00
2015-11-13 15:08:16 +00:00
// Found a usable biome
// Spawn chunks so we can find a nice spawn.
int ChunkX = 0 , ChunkZ = 0 ;
cChunkDef :: BlockToChunk ( BiomeOffset . x , BiomeOffset . z , ChunkX , ChunkZ );
cSpawnPrepare :: PrepareChunks ( * this , ChunkX , ChunkZ , a_MaxSpawnRadius );
break ;
}
2016-01-12 15:04:59 +02:00
2015-11-13 15:08:16 +00:00
// Check 0, 0 first.
double SpawnY = 0.0 ;
if ( CanSpawnAt ( BiomeOffset . x , SpawnY , BiomeOffset . z ))
{
m_SpawnX = BiomeOffset . x + 0.5 ;
m_SpawnY = SpawnY ;
m_SpawnZ = BiomeOffset . z + 0.5 ;
LOGINFO ( "Generated spawnpoint position at {%.2f, %.2f, %.2f}" , m_SpawnX , m_SpawnY , m_SpawnZ );
return ;
}
// A search grid (searches clockwise around the origin)
2015-12-20 11:01:17 +01:00
static const int HalfChunk = static_cast < int > ( cChunkDef :: Width / 2.0f );
2015-11-13 15:08:16 +00:00
static const Vector3i ChunkOffset [] =
{
Vector3i ( 0 , 0 , HalfChunk ),
Vector3i ( HalfChunk , 0 , HalfChunk ),
Vector3i ( HalfChunk , 0 , 0 ),
Vector3i ( HalfChunk , 0 , - HalfChunk ),
Vector3i ( 0 , 0 , - HalfChunk ),
Vector3i ( - HalfChunk , 0 , - HalfChunk ),
Vector3i ( - HalfChunk , 0 , 0 ),
Vector3i ( - HalfChunk , 0 , HalfChunk ),
};
static const int PerRadiSearchCount = ARRAYCOUNT ( ChunkOffset );
for ( int RadiusOffset = 1 ; RadiusOffset < ( a_MaxSpawnRadius * 2 ); ++ RadiusOffset )
{
for ( int SearchGridIndex = 0 ; SearchGridIndex < PerRadiSearchCount ; ++ SearchGridIndex )
2014-09-26 13:04:34 +02:00
{
2015-11-13 15:08:16 +00:00
const Vector3i PotentialSpawn = BiomeOffset + ( ChunkOffset [ SearchGridIndex ] * RadiusOffset );
2016-01-12 15:04:59 +02:00
2015-11-13 15:08:16 +00:00
if ( CanSpawnAt ( PotentialSpawn . x , SpawnY , PotentialSpawn . z ))
2015-03-12 19:37:39 +01:00
{
2015-11-13 15:08:16 +00:00
m_SpawnX = PotentialSpawn . x + 0.5 ;
m_SpawnY = SpawnY ;
m_SpawnZ = PotentialSpawn . z + 0.5 ;
int ChunkX , ChunkZ ;
cChunkDef :: BlockToChunk ( static_cast < int > ( m_SpawnX ), static_cast < int > ( m_SpawnZ ), ChunkX , ChunkZ );
cSpawnPrepare :: PrepareChunks ( * this , ChunkX , ChunkZ , a_MaxSpawnRadius );
LOGINFO ( "Generated spawnpoint position at {%.2f, %.2f, %.2f}" , m_SpawnX , m_SpawnY , m_SpawnZ );
return ;
2015-03-12 19:37:39 +01:00
}
2014-09-26 13:04:34 +02:00
}
2015-11-13 15:08:16 +00:00
}
m_SpawnY = GetHeight ( static_cast < int > ( m_SpawnX ), static_cast < int > ( m_SpawnZ ));
LOGWARNING ( "Did not find an acceptable spawnpoint. Generated a random spawnpoint position at {%.2f, %.2f, %.2f}" , m_SpawnX , m_SpawnY , m_SpawnZ );
}
bool cWorld :: CanSpawnAt ( double a_X , double & a_Y , double a_Z )
{
// All this blocks can only be found above ground.
// Apart from netherrack (as the Nether is technically a massive cave)
static const BLOCKTYPE ValidSpawnBlocks [] =
{
E_BLOCK_GRASS ,
E_BLOCK_SAND ,
E_BLOCK_SNOW ,
E_BLOCK_SNOW_BLOCK ,
E_BLOCK_NETHERRACK
};
static const int ValidSpawnBlocksCount = ARRAYCOUNT ( ValidSpawnBlocks );
2015-12-20 11:01:17 +01:00
// Increase this by two, because we need two more blocks for body and head
static const int HighestSpawnPoint = GetHeight ( static_cast < int > ( a_X ), static_cast < int > ( a_Z )) + 2 ;
static const int LowestSpawnPoint = static_cast < int > ( HighestSpawnPoint / 2.0f );
2015-11-13 15:08:16 +00:00
for ( int PotentialY = HighestSpawnPoint ; PotentialY > LowestSpawnPoint ; -- PotentialY )
{
BLOCKTYPE HeadBlock = GetBlock ( static_cast < int > ( a_X ), PotentialY , static_cast < int > ( a_Z ));
// Is this block safe for spawning
if ( HeadBlock != E_BLOCK_AIR )
{
continue ;
}
BLOCKTYPE BodyBlock = GetBlock ( static_cast < int > ( a_X ), PotentialY - 1 , static_cast < int > ( a_Z ));
// Is this block safe for spawning
if ( BodyBlock != E_BLOCK_AIR )
{
continue ;
}
BLOCKTYPE FloorBlock = GetBlock ( static_cast < int > ( a_X ), PotentialY - 2 , static_cast < int > ( a_Z ));
// Early out - Is the floor block air
if ( FloorBlock == E_BLOCK_AIR )
2013-11-26 21:54:40 +00:00
{
2015-11-13 15:08:16 +00:00
continue ;
2013-11-26 21:54:40 +00:00
}
2015-11-13 15:08:16 +00:00
// Is the floor block ok
bool ValidSpawnBlock = false ;
for ( int BlockIndex = 0 ; BlockIndex < ValidSpawnBlocksCount ; ++ BlockIndex )
2013-11-26 21:54:40 +00:00
{
2015-11-13 15:08:16 +00:00
ValidSpawnBlock |= ( ValidSpawnBlocks [ BlockIndex ] == FloorBlock );
2013-11-26 21:54:40 +00:00
}
2015-11-13 15:08:16 +00:00
if ( ! ValidSpawnBlock )
{
continue ;
}
if ( ! CheckPlayerSpawnPoint ( static_cast < int > ( a_X ), PotentialY - 1 , static_cast < int > ( a_Z )))
{
continue ;
}
a_Y = PotentialY - 1.0 ;
return true ;
2015-03-12 19:37:39 +01:00
}
2015-11-13 15:08:16 +00:00
return false ;
2015-03-12 19:37:39 +01:00
}
2013-11-26 21:54:40 +00:00
2015-03-12 19:37:39 +01:00
bool cWorld :: CheckPlayerSpawnPoint ( int a_PosX , int a_PosY , int a_PosZ )
{
2015-11-13 15:08:16 +00:00
// Check height bounds
if ( ! cChunkDef :: IsValidHeight ( a_PosY ))
2015-09-29 18:10:04 +01:00
{
return false ;
}
2015-11-13 15:08:16 +00:00
// Check that surrounding blocks are neither solid or liquid
static const Vector3i SurroundingCoords [] =
2015-03-12 19:37:39 +01:00
{
2015-11-13 15:08:16 +00:00
Vector3i ( 0 , 0 , 1 ),
Vector3i ( 1 , 0 , 1 ),
Vector3i ( 1 , 0 , 0 ),
Vector3i ( 1 , 0 , - 1 ),
Vector3i ( 0 , 0 , - 1 ),
Vector3i ( - 1 , 0 , - 1 ),
Vector3i ( - 1 , 0 , 0 ),
Vector3i ( - 1 , 0 , 1 ),
2015-03-12 19:37:39 +01:00
};
2015-11-13 15:08:16 +00:00
static const int SurroundingCoordsCount = ARRAYCOUNT ( SurroundingCoords );
2015-03-12 19:37:39 +01:00
2015-11-13 15:08:16 +00:00
for ( int CoordIndex = 0 ; CoordIndex < SurroundingCoordsCount ; ++ CoordIndex )
2015-03-12 19:37:39 +01:00
{
2015-11-13 15:08:16 +00:00
const int XPos = a_PosX + SurroundingCoords [ CoordIndex ]. x ;
const int ZPos = a_PosZ + SurroundingCoords [ CoordIndex ]. z ;
const BLOCKTYPE BlockType = GetBlock ( XPos , a_PosY , ZPos );
if ( cBlockInfo :: IsSolid ( BlockType ) || IsBlockLiquid ( BlockType ))
2015-03-12 19:37:39 +01:00
{
return false ;
}
}
2015-11-13 15:08:16 +00:00
2015-03-12 19:37:39 +01:00
return true ;
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 ;
2016-01-12 15:04:59 +02:00
2014-04-25 16:55:38 -07:00
case eWeather_Rain :
{
2015-05-09 09:25:09 +02:00
// 1 / 8 chance of turning into a thunderstorm
2014-04-25 16:55:38 -07:00
return (( m_TickRand . randInt () % 256 ) < 32 ) ? eWeather_ThunderStorm : eWeather_Sunny ;
}
}
2015-09-28 01:02:17 +02:00
#ifndef __clang__
ASSERT ( ! "Unknown weather" );
return eWeather_Sunny ;
#endif
2014-04-25 16:55:38 -07:00
}
2014-07-21 22:49:06 +01:00
void cWorld :: InitialiseGeneratorDefaults ( cIniFile & a_IniFile )
{
switch ( GetDimension ())
{
case dimEnd :
{
2014-11-25 23:03:33 +00:00
a_IniFile . GetValueSet ( "Generator" , "Generator" , "Composable" );
a_IniFile . GetValueSet ( "Generator" , "BiomeGen" , "Constant" );
a_IniFile . GetValueSet ( "Generator" , "ConstantBiome" , "End" );
a_IniFile . GetValueSet ( "Generator" , "ShapeGen" , "End" );
2014-07-21 22:49:06 +01:00
a_IniFile . GetValueSet ( "Generator" , "CompositionGen" , "End" );
break ;
}
case dimOverworld :
{
2014-11-25 23:03:33 +00:00
a_IniFile . GetValueSet ( "Generator" , "Generator" , "Composable" );
a_IniFile . GetValueSet ( "Generator" , "BiomeGen" , "Grown" );
a_IniFile . GetValueSet ( "Generator" , "ShapeGen" , "BiomalNoise3D" );
a_IniFile . GetValueSet ( "Generator" , "CompositionGen" , "Biomal" );
2015-05-17 19:36:18 +02:00
a_IniFile . GetValueSet ( "Generator" , "Finishers" , "RoughRavines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, TallGrass, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, NaturalPatches, PreSimulator, Animals" );
2014-07-21 22:49:06 +01:00
break ;
}
case dimNether :
{
2014-11-25 23:03:33 +00:00
a_IniFile . GetValueSet ( "Generator" , "Generator" , "Composable" );
a_IniFile . GetValueSet ( "Generator" , "BiomeGen" , "Constant" );
a_IniFile . GetValueSet ( "Generator" , "ConstantBiome" , "Nether" );
a_IniFile . GetValueSet ( "Generator" , "ShapeGen" , "HeightMap" );
a_IniFile . GetValueSet ( "Generator" , "HeightGen" , "Flat" );
a_IniFile . GetValueSet ( "Generator" , "FlatHeight" , "128" );
a_IniFile . GetValueSet ( "Generator" , "CompositionGen" , "Nether" );
2015-12-30 01:14:18 +01:00
a_IniFile . GetValueSet ( "Generator" , "Finishers" , "SoulsandRims, WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherOreNests, PieceStructures: NetherFort, GlowStone, PreSimulator" );
2014-07-21 22:49:06 +01:00
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 )
{
2016-04-30 00:57:10 -04:00
case dimOverworld : DefaultMonsters = "bat, cavespider, chicken, cow, creeper, guardian, horse, mooshroom, ocelot, pig, rabbit, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie" ; break ; // TODO Re-add Enderman when bugs are fixed
2014-07-21 22:49:06 +01:00
case dimNether : DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombie, zombiepigman" ; break ;
2016-04-30 00:57:10 -04:00
case dimEnd : DefaultMonsters = "" ; break ; // TODO Re-add Enderman when bugs are fixed
2014-09-08 21:31:47 +01:00
case dimNotSet : ASSERT ( ! "Dimension not set" ); break ;
2014-07-21 22:49:06 +01:00
}
2016-01-12 15:04:59 +02:00
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 );
2015-01-24 20:17:00 +01:00
for ( auto itr = m_Clients . begin (); itr != m_Clients . end (); ++ itr )
2013-08-14 19:56:29 +02:00
{
( * itr ) -> Destroy ();
} // 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
{
2015-03-21 15:20:31 +01:00
IniFile . SetValue ( "LinkedWorlds" , "NetherWorldName" , m_LinkedNetherWorldName );
IniFile . SetValue ( "LinkedWorlds" , "EndWorldName" , m_LinkedEndWorldName );
2014-06-04 20:00:55 +01:00
}
2014-06-10 20:43:27 +01:00
else
{
2015-03-21 15:20:31 +01:00
IniFile . SetValue ( "LinkedWorlds" , "OverworldName" , m_LinkedOverworldName );
2014-06-10 20:43:27 +01:00
}
2015-03-21 15:20:31 +01:00
IniFile . SetValueI ( "Physics" , "TNTShrapnelLevel" , static_cast < int > ( m_TNTShrapnelLevel ));
2014-06-04 20:00:55 +01:00
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 );
2015-03-21 15:20:31 +01:00
IniFile . SetValueI ( "General" , "Weather" , static_cast < int > ( m_Weather ));
2015-01-11 21:12:26 +00:00
IniFile . SetValueI ( "General" , "TimeInTicks" , GetTimeOfDay ());
2014-06-04 20:00:55 +01:00
IniFile . WriteFile ( m_IniFileName );
2016-01-12 15:04:59 +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
}
2015-01-11 21:12:26 +00:00
void cWorld :: Tick ( std :: chrono :: milliseconds a_Dt , std :: chrono :: milliseconds 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 );
2016-01-12 15:04:59 +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
2015-01-11 21:12:26 +00:00
m_WorldAge += a_Dt ;
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
{
2015-01-17 22:24:25 +00:00
// We need sub-tick precision here, that's why we store the time in milliseconds and calculate ticks off of it
2015-01-11 21:12:26 +00:00
m_TimeOfDay += a_Dt ;
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)
2015-01-11 21:12:26 +00:00
if ( m_TimeOfDay > std :: chrono :: minutes ( 20 ))
2014-08-07 01:07:32 +02:00
{
2015-01-11 21:12:26 +00:00
m_TimeOfDay -= std :: chrono :: minutes ( 20 );
2014-08-07 01:07:32 +02:00
}
2012-11-01 21:38:20 +00: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)
2015-01-11 21:12:26 +00:00
if ( m_LastTimeUpdate < m_WorldAge - cTickTime ( 40 ))
2014-08-07 01:07:32 +02:00
{
BroadcastTimeUpdate ();
2015-01-11 21:12:26 +00:00
m_LastTimeUpdate = std :: chrono :: duration_cast < cTickTimeLong > ( m_WorldAge );
2014-08-07 01:07:32 +02:00
}
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 );
2016-03-29 21:23:53 +03:00
for ( auto & Entity : m_EntitiesToAdd )
2014-06-06 22:31:16 +02:00
{
2016-03-29 21:23:53 +03:00
Entity -> SetWorld ( this );
m_ChunkMap -> AddEntity ( Entity );
ASSERT ( ! Entity -> IsTicking ());
Entity -> SetIsTicking ( true );
2014-06-06 22:31:16 +02:00
}
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 );
2016-02-07 19:07:14 +02:00
TickMobs ( a_Dt );
2015-06-30 15:50:15 +01:00
m_MapManager . TickMaps ();
2012-07-15 20:36:34 +00:00
2015-01-18 11:25:16 +01:00
TickClients ( static_cast < float > ( a_Dt . count ()));
2013-09-16 09:25:23 +02:00
TickQueuedBlocks ();
2013-08-11 21:05:44 +02:00
TickQueuedTasks ();
2016-01-12 15:04:59 +02:00
2015-01-18 11:25:16 +01:00
GetSimulatorManager () -> Simulate ( static_cast < float > ( a_Dt . count ()));
2012-06-14 13:06:06 +00:00
2015-01-18 11:25:16 +01:00
TickWeather ( static_cast < float > ( a_Dt . count ()));
2012-06-14 13:06:06 +00:00
2015-01-11 21:12:26 +00:00
if ( m_WorldAge - m_LastSave > std :: chrono :: minutes ( 5 )) // Save each 5 minutes
2012-06-14 13:06:06 +00:00
{
SaveAllChunks ();
}
2015-06-30 15:50:15 +01:00
if ( m_WorldAge - m_LastUnload > std :: chrono :: seconds ( 10 )) // Unload every 10 seconds
2012-06-14 13:06:06 +00:00
{
UnloadUnusedChunks ();
}
}
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
{
2015-05-13 23:05:44 -04:00
CastThunderbolt ( 0 , 0 , 0 ); // TODO: find random positions near players to cast thunderbolts.
2012-06-14 13:06:06 +00:00
}
}
}
2015-01-11 21:12:26 +00:00
void cWorld :: TickMobs ( std :: chrono :: milliseconds 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 ];
2015-01-11 21:12:26 +00:00
cTickTime SpawnDelay = cTickTime ( 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 ;
}
2015-01-11 21:12:26 +00:00
m_LastSpawnMonster [ Family ] = std :: chrono :: duration_cast < cTickTimeLong > ( m_WorldAge );
2013-10-20 14:16:21 +02:00
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)
2016-04-06 12:16:14 +03:00
class cCallback : public cEntityCallback
{
virtual bool Item ( cEntity * a_Entity ) override
{
if ( ! a_Entity -> IsMob ())
{
return false ;
}
if ( ! a_Entity -> IsTicking ())
{
return false ;
}
auto Monster = static_cast < cMonster *> ( a_Entity );
ASSERT ( Monster -> GetParentChunk () != nullptr ); // A ticking entity must have a valid parent chunk
// Tick close mobs
if ( Monster -> GetParentChunk () -> HasAnyClients ())
{
Monster -> Tick ( m_Dt , * ( a_Entity -> GetParentChunk ()));
}
// Destroy far hostile mobs
else if (( Monster -> GetMobFamily () == cMonster :: eFamily :: mfHostile ))
{
if ( Monster -> GetMobType () != eMonsterType :: mtWolf )
{
Monster -> Destroy ( true );
}
else
{
auto Wolf = static_cast < cWolf *> ( Monster );
if ( Wolf -> IsAngry ())
{
Monster -> Destroy ( true );
}
}
}
return false ;
}
public :
std :: chrono :: milliseconds m_Dt ;
} Callback ;
Callback . m_Dt = a_Dt ;
ForEachEntity ( Callback );
2012-06-14 13:06:06 +00:00
}
2013-08-11 21:05:44 +02:00
void cWorld :: TickQueuedTasks ( 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
2015-09-25 18:56:49 +01:00
decltype ( m_Tasks ) Tasks ;
2014-01-14 12:17:03 -08:00
{
2015-09-25 18:56:49 +01:00
cCSLock Lock ( m_CSTasks );
if ( m_Tasks . empty ())
2014-10-13 14:49:18 +02:00
{
2015-09-25 18:56:49 +01:00
return ;
}
// Partition everything to be executed by returning false to move to end of list if time reached
auto MoveBeginIterator = std :: partition ( m_Tasks . begin (), m_Tasks . end (), [ this ]( const decltype ( m_Tasks ) :: value_type & a_Task )
2014-10-13 14:49:18 +02:00
{
2015-09-25 18:56:49 +01:00
if ( a_Task . first < std :: chrono :: duration_cast < cTickTimeLong > ( m_WorldAge ). count ())
{
return false ;
}
return true ;
2014-10-13 14:49:18 +02:00
}
2015-09-25 18:56:49 +01:00
);
// Cut all the due tasks from m_Tasks into Tasks:
Tasks . insert (
Tasks . end (),
std :: make_move_iterator ( MoveBeginIterator ),
std :: make_move_iterator ( m_Tasks . end ())
);
m_Tasks . erase ( MoveBeginIterator , m_Tasks . end ());
2014-01-14 12:17:03 -08:00
}
2015-09-25 18:56:49 +01:00
// Execute each task:
for ( const auto & Task : Tasks )
2014-01-14 12:17:03 -08:00
{
2015-09-25 18:56:49 +01:00
Task . second ( * 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 )
{
2015-01-24 20:17:00 +01:00
cClientHandlePtrs RemoveClients ;
2013-08-13 22:45:29 +02:00
{
cCSLock Lock ( m_CSClients );
2016-01-12 15:04:59 +02:00
2013-08-14 13:43:55 +02:00
// Remove clients scheduled for removal:
2015-01-24 20:17:00 +01:00
for ( auto itr = m_ClientsToRemove . begin (), end = m_ClientsToRemove . end (); itr != end ; ++ itr )
2013-08-14 13:43:55 +02:00
{
2015-01-24 20:17:00 +01:00
for ( auto itrC = m_Clients . begin (), endC = m_Clients . end (); itrC != endC ; ++ itrC )
{
if ( itrC -> get () == * itr )
{
m_Clients . erase ( itrC );
break ;
}
}
2013-08-14 13:43:55 +02:00
} // for itr - m_ClientsToRemove[]
m_ClientsToRemove . clear ();
2016-01-12 15:04:59 +02:00
2013-08-14 13:43:55 +02:00
// Add clients scheduled for adding:
2015-01-24 20:17:00 +01:00
for ( auto itr = m_ClientsToAdd . begin (), end = m_ClientsToAdd . end (); itr != end ; ++ itr )
2013-08-14 13:43:55 +02:00
{
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 ();
2016-01-12 15:04:59 +02:00
2013-08-13 22:45:29 +02:00
// Tick the clients, take out those that have been destroyed into RemoveClients
2015-01-24 20:17:00 +01:00
for ( auto itr = m_Clients . begin (); itr != m_Clients . end ();)
2013-08-13 22:45:29 +02:00
{
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[]
}
2015-01-24 20:17:00 +01:00
// Delete the clients queued for removal:
RemoveClients . clear ();
2013-08-13 22:45:29 +02:00
}
2013-10-30 23:33:42 +01:00
void cWorld :: UpdateSkyDarkness ( void )
{
2015-01-11 21:12:26 +00:00
int TempTime = std :: chrono :: duration_cast < cTickTime > ( m_TimeOfDay ). count ();
2013-10-30 23:33:42 +01:00
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
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 );
}
2015-09-24 10:48:33 +02:00
bool cWorld :: ForEachBrewingstandInChunk ( int a_ChunkX , int a_ChunkZ , cBrewingstandCallback & a_Callback )
{
return m_ChunkMap -> ForEachBrewingstandInChunk ( 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 ;
}
2016-01-12 15:04:59 +02:00
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 );
2015-07-29 09:04:03 -06:00
BroadcastSoundEffect ( "random.explode" , static_cast < double > ( a_BlockX ), static_cast < double > ( a_BlockY ), static_cast < 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-10-20 21:55:07 +01:00
if ( ch == nullptr )
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 ;
2015-07-29 09:04:03 -06:00
ch -> SendExplosion ( a_BlockX , a_BlockY , a_BlockZ , static_cast < 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 );
}
2015-05-09 11:16:56 +02:00
bool cWorld :: DoWithBeaconAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cBeaconCallback & a_Callback )
2014-07-30 22:19:51 +02:00
{
return m_ChunkMap -> DoWithBeaconAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback );
}
2015-09-24 10:48:33 +02:00
bool cWorld :: DoWithBrewingstandAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cBrewingstandCallback & a_Callback )
{
return m_ChunkMap -> DoWithBrewingstandAt ( 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 );
}
2015-06-10 15:16:05 +01:00
bool cWorld :: DoWithChunk ( int a_ChunkX , int a_ChunkZ , std :: function < bool ( cChunk & ) > a_Callback )
{
struct cCallBackWrapper : cChunkCallback
{
cCallBackWrapper ( std :: function < bool ( cChunk & ) > a_InnerCallback ) :
m_Callback ( a_InnerCallback )
{
}
2016-01-12 15:04:59 +02:00
2015-06-10 15:16:05 +01:00
virtual bool Item ( cChunk * a_Chunk )
{
return m_Callback ( * a_Chunk );
}
private :
std :: function < bool ( cChunk & ) > m_Callback ;
} callback ( a_Callback );
return m_ChunkMap -> DoWithChunk ( a_ChunkX , a_ChunkZ , callback );
}
2015-03-21 17:17:26 +00:00
bool cWorld :: DoWithChunkAt ( Vector3i a_BlockPos , std :: function < bool ( cChunk & ) > a_Callback )
{
return m_ChunkMap -> DoWithChunkAt ( a_BlockPos , 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 ;
2015-07-29 09:04:03 -06:00
auto WorldAge = static_cast < int > ( std :: chrono :: duration_cast < cTickTimeLong > ( m_WorldAge ). count () & 0xffffffff );
2012-06-14 13:06:06 +00:00
switch ( a_SaplingMeta & 0x07 )
{
2015-01-11 21:12:26 +00:00
case E_META_SAPLING_APPLE : GetAppleTreeImage ( a_X , a_Y , a_Z , Noise , WorldAge , Logs , Other ); break ;
case E_META_SAPLING_BIRCH : GetBirchTreeImage ( a_X , a_Y , a_Z , Noise , WorldAge , Logs , Other ); break ;
case E_META_SAPLING_CONIFER : GetConiferTreeImage ( a_X , a_Y , a_Z , Noise , WorldAge , Logs , Other ); break ;
case E_META_SAPLING_ACACIA : GetAcaciaTreeImage ( a_X , a_Y , a_Z , Noise , WorldAge , Logs , Other ); break ;
2015-07-13 11:02:00 -06:00
case E_META_SAPLING_JUNGLE :
{
bool IsLarge = GetLargeTreeAdjustment ( * this , a_X , a_Y , a_Z , a_SaplingMeta );
GetJungleTreeImage ( a_X , a_Y , a_Z , Noise , WorldAge , Logs , Other , IsLarge );
break ;
}
case E_META_SAPLING_DARK_OAK :
{
if ( ! GetLargeTreeAdjustment ( * this , a_X , a_Y , a_Z , a_SaplingMeta ))
{
return ;
}
GetDarkoakTreeImage ( a_X , a_Y , a_Z , Noise , WorldAge , 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 ;
2015-07-29 09:04:03 -06:00
GetTreeImageByBiome ( a_X , a_Y , a_Z , Noise , static_cast < int > ( std :: chrono :: duration_cast < cTickTimeLong > ( m_WorldAge ). count () & 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
2016-01-12 15:04:59 +02:00
2012-06-14 13:06:06 +00:00
// Make a copy of the log blocks:
sSetBlockVector b2 ;
for ( sSetBlockVector :: const_iterator itr = a_Blocks . begin (); itr != a_Blocks . end (); ++ itr )
{
2014-12-24 07:20:17 +01:00
if ( itr -> m_BlockType == E_BLOCK_LOG )
2012-06-14 13:06:06 +00:00
{
b2 . push_back ( * itr );
}
} // for itr - a_Blocks[]
2016-01-12 15:04:59 +02:00
2012-06-14 13:06:06 +00:00
// Query blocktypes and metas at those log blocks:
if ( ! GetBlocks ( b2 , false ))
{
return ;
}
2016-01-12 15:04:59 +02:00
2012-06-14 13:06:06 +00:00
// 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 )
{
2014-12-24 07:20:17 +01:00
switch ( itr -> m_BlockType )
2012-06-14 13:06:06 +00:00
{
CASE_TREE_ALLOWED_BLOCKS :
{
break ;
}
default :
{
return ;
}
}
} // for itr - b2[]
2016-01-12 15:04:59 +02:00
2012-06-14 13:06:06 +00:00
// 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
{
2015-07-06 15:41:08 -06:00
cFastRandom random ;
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 :
{
2016-05-29 10:30:47 +02:00
if (( a_IsByBonemeal && ! m_IsCarrotsBonemealable ) || ( BlockMeta >= 7 ))
2013-04-05 13:45:00 +00:00
{
return false ;
}
2016-05-29 10:30:47 +02:00
if ( ! a_IsByBonemeal )
2013-04-05 13:45:00 +00:00
{
2016-05-29 10:30:47 +02:00
++ BlockMeta ;
}
else
{
BlockMeta += random . NextInt ( 4 ) + 2 ;
BlockMeta = std :: min ( BlockMeta , static_cast < NIBBLETYPE > ( 7 ));
2013-04-05 13:45:00 +00:00
}
2016-05-29 10:30:47 +02:00
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , BlockMeta );
BroadcastSoundParticleEffect ( EffectID :: PARTICLE_HAPPY_VILLAGER , a_BlockX , a_BlockY , a_BlockZ , 0 );
return true ;
2013-04-05 13:45:00 +00:00
}
2014-12-13 20:31:37 +01:00
case E_BLOCK_COCOA_POD :
{
NIBBLETYPE TypeMeta = BlockMeta & 0x03 ;
2014-12-14 13:14:48 +01:00
int GrowState = BlockMeta >> 2 ;
2014-12-13 20:31:37 +01:00
2016-05-29 10:30:47 +02:00
if ( GrowState >= 2 )
2014-12-13 20:31:37 +01:00
{
2016-05-29 10:30:47 +02:00
return false ;
2014-12-13 20:31:37 +01:00
}
2016-05-29 10:30:47 +02:00
++ GrowState ;
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , static_cast < NIBBLETYPE > ( GrowState << 2 | TypeMeta ));
BroadcastSoundParticleEffect ( EffectID :: PARTICLE_HAPPY_VILLAGER , a_BlockX , a_BlockY , a_BlockZ , 0 );
return true ;
2014-12-13 20:31:37 +01:00
}
2012-06-14 13:06:06 +00:00
case E_BLOCK_CROPS :
{
2016-05-29 10:30:47 +02:00
if (( a_IsByBonemeal && ! m_IsCropsBonemealable ) || ( BlockMeta >= 7 ))
2012-06-14 13:06:06 +00:00
{
return false ;
}
2016-05-29 10:30:47 +02:00
if ( ! a_IsByBonemeal )
2012-06-14 13:06:06 +00:00
{
2016-05-29 10:30:47 +02:00
++ BlockMeta ;
}
else
{
BlockMeta += random . NextInt ( 4 ) + 2 ;
BlockMeta = std :: min ( BlockMeta , static_cast < NIBBLETYPE > ( 7 ));
2012-06-14 13:06:06 +00:00
}
2016-05-29 10:30:47 +02:00
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , BlockMeta );
BroadcastSoundParticleEffect ( EffectID :: PARTICLE_HAPPY_VILLAGER , a_BlockX , a_BlockY , a_BlockZ , 0 );
return true ;
2012-06-14 13:06:06 +00:00
}
2016-01-12 15:04:59 +02:00
2012-06-14 13:06:06 +00:00
case E_BLOCK_MELON_STEM :
{
if ( BlockMeta < 7 )
{
if ( a_IsByBonemeal && ! m_IsMelonStemBonemealable )
{
return false ;
}
2015-07-06 15:41:08 -06:00
if ( ! a_IsByBonemeal )
{
++ BlockMeta ;
}
else
{
BlockMeta += random . NextInt ( 4 ) + 2 ;
BlockMeta = std :: min ( BlockMeta , static_cast < NIBBLETYPE > ( 7 ));
}
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , BlockMeta );
2012-06-14 13:06:06 +00:00
}
else
{
if ( a_IsByBonemeal && ! m_IsMelonBonemealable )
{
return false ;
}
2016-05-29 22:10:35 +02:00
if ( ! GrowMelonPumpkin ( a_BlockX , a_BlockY , a_BlockZ , BlockType ))
{
return false ;
}
2012-06-14 13:06:06 +00:00
}
2016-05-29 22:10:35 +02:00
BroadcastSoundParticleEffect ( EffectID :: PARTICLE_HAPPY_VILLAGER , a_BlockX , a_BlockY , a_BlockZ , 0 );
return true ;
2012-06-14 13:06:06 +00:00
}
2016-01-12 15:04:59 +02:00
2013-04-05 13:45:00 +00:00
case E_BLOCK_POTATOES :
{
2016-05-29 10:30:47 +02:00
if (( a_IsByBonemeal && ! m_IsPotatoesBonemealable ) || ( BlockMeta >= 7 ))
2013-04-05 13:45:00 +00:00
{
return false ;
}
2016-05-29 10:30:47 +02:00
if ( ! a_IsByBonemeal )
2013-04-05 13:45:00 +00:00
{
2016-05-29 10:30:47 +02:00
++ BlockMeta ;
}
else
{
BlockMeta += random . NextInt ( 4 ) + 2 ;
BlockMeta = std :: min ( BlockMeta , static_cast < NIBBLETYPE > ( 7 ));
2013-04-05 13:45:00 +00:00
}
2016-05-29 10:30:47 +02:00
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , BlockMeta );
BroadcastSoundParticleEffect ( EffectID :: PARTICLE_HAPPY_VILLAGER , a_BlockX , a_BlockY , a_BlockZ , 0 );
return true ;
2013-04-05 13:45:00 +00:00
}
2016-01-12 15:04:59 +02:00
2012-06-14 13:06:06 +00:00
case E_BLOCK_PUMPKIN_STEM :
{
if ( BlockMeta < 7 )
{
if ( a_IsByBonemeal && ! m_IsPumpkinStemBonemealable )
{
return false ;
}
2015-07-06 15:41:08 -06:00
if ( ! a_IsByBonemeal )
{
++ BlockMeta ;
}
else
{
BlockMeta += random . NextInt ( 4 ) + 2 ;
BlockMeta = std :: min ( BlockMeta , static_cast < NIBBLETYPE > ( 7 ));
}
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , BlockMeta );
2012-06-14 13:06:06 +00:00
}
else
{
if ( a_IsByBonemeal && ! m_IsPumpkinBonemealable )
{
return false ;
}
2016-05-29 22:10:35 +02:00
if ( ! GrowMelonPumpkin ( a_BlockX , a_BlockY , a_BlockZ , BlockType ))
{
return false ;
}
2012-06-14 13:06:06 +00:00
}
2016-05-29 22:10:35 +02:00
BroadcastSoundParticleEffect ( EffectID :: PARTICLE_HAPPY_VILLAGER , a_BlockX , a_BlockY , a_BlockZ , 0 );
return true ;
2012-06-14 13:06:06 +00:00
}
2016-01-12 15:04:59 +02:00
2012-06-14 13:06:06 +00:00
case E_BLOCK_SAPLING :
{
if ( a_IsByBonemeal && ! m_IsSaplingBonemealable )
{
return false ;
}
2015-07-06 15:41:08 -06:00
NIBBLETYPE TypeMeta = BlockMeta & 0x07 ;
int GrowState = BlockMeta >> 3 ;
if ( GrowState < 1 )
{
// Non-bonemeal forces a growth, while bonemeal only has a chance of growing it
if ( ! a_IsByBonemeal )
{
++ GrowState ;
}
else if ( random . NextInt ( 99 ) < 45 )
{
++ GrowState ;
}
FastSetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , static_cast < NIBBLETYPE > ( GrowState << 3 | TypeMeta ));
}
else if ( random . NextInt ( 99 ) < 45 )
{
GrowTreeFromSapling ( a_BlockX , a_BlockY , a_BlockZ , BlockMeta );
}
2016-05-29 22:10:35 +02:00
BroadcastSoundParticleEffect ( EffectID :: PARTICLE_HAPPY_VILLAGER , a_BlockX , a_BlockY , a_BlockZ , 0 );
return true ;
2012-06-14 13:06:06 +00:00
}
2016-01-12 15:04:59 +02:00
2012-06-14 13:06:06 +00:00
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 );
2015-11-23 23:39:19 +00:00
BroadcastSoundParticleEffect ( EffectID :: PARTICLE_HAPPY_VILLAGER , a_BlockX + OfsX , a_BlockY + OfsY , a_BlockZ + OfsZ , 0 );
2012-06-14 13:06:06 +00:00
} // for i - 50 times
return true ;
}
2016-01-12 15:04:59 +02:00
2012-06-14 13:06:06 +00:00
case E_BLOCK_SUGARCANE :
{
if ( a_IsByBonemeal && ! m_IsSugarcaneBonemealable )
{
return false ;
}
2016-05-29 22:10:35 +02:00
if ( m_ChunkMap -> GrowSugarcane ( a_BlockX , a_BlockY , a_BlockZ , 1 ) == 0 )
{
return false ;
}
BroadcastSoundParticleEffect ( EffectID :: PARTICLE_HAPPY_VILLAGER , a_BlockX , a_BlockY , a_BlockZ , 0 );
return true ;
2012-06-14 13:06:06 +00:00
}
2016-01-12 15:04:59 +02:00
2012-06-14 13:06:06 +00:00
case E_BLOCK_CACTUS :
{
if ( a_IsByBonemeal && ! m_IsCactusBonemealable )
{
return false ;
}
2016-05-29 22:10:35 +02:00
if ( m_ChunkMap -> GrowCactus ( a_BlockX , a_BlockY , a_BlockZ , 1 ) == 0 )
{
return false ;
}
BroadcastSoundParticleEffect ( EffectID :: PARTICLE_HAPPY_VILLAGER , a_BlockX , a_BlockY , a_BlockZ , 0 );
return true ;
2012-06-14 13:06:06 +00:00
}
2016-05-29 22:10:35 +02:00
case E_BLOCK_TALL_GRASS :
{
if ( a_IsByBonemeal && ! m_IsTallGrassBonemealable )
{
return false ;
}
if ( ! m_ChunkMap -> GrowTallGrass ( a_BlockX , a_BlockY , a_BlockZ ))
{
return false ;
}
BroadcastSoundParticleEffect ( EffectID :: PARTICLE_HAPPY_VILLAGER , a_BlockX , a_BlockY , a_BlockZ , 0 );
return true ;
}
case E_BLOCK_BIG_FLOWER :
{
if ( a_IsByBonemeal && ! m_IsBigFlowerBonemealable )
{
return false ;
}
if ( BlockMeta & 8 ) // the upper flower block does not save the type of the flower
{
GetBlockTypeMeta ( a_BlockX , a_BlockY - 1 , a_BlockZ , BlockType , BlockMeta );
if ( BlockType != E_BLOCK_BIG_FLOWER )
{
return false ;
}
}
if (
( BlockMeta == E_META_BIG_FLOWER_DOUBLE_TALL_GRASS ) ||
( BlockMeta == E_META_BIG_FLOWER_LARGE_FERN )
) // tall grass and fern do not work
{
return false ;
}
// spawn flower item
BroadcastSoundParticleEffect ( EffectID :: PARTICLE_HAPPY_VILLAGER , a_BlockX , a_BlockY , a_BlockZ , 0 );
cItems FlowerItem ;
FlowerItem . Add ( E_BLOCK_BIG_FLOWER , 1 , BlockMeta );
SpawnItemPickups ( FlowerItem , a_BlockX + 0.5 , a_BlockY + 0.5 , a_BlockZ + 0.5 );
return true ;
}
2012-06-14 13:06:06 +00:00
} // switch (BlockType)
return false ;
}
2016-05-29 10:30:47 +02:00
int cWorld :: GrowCactus ( int a_BlockX , int a_BlockY , int a_BlockZ , int a_NumBlocksToGrow )
2012-10-03 08:52:11 +00:00
{
2016-05-29 10:30:47 +02:00
return m_ChunkMap -> GrowCactus ( a_BlockX , a_BlockY , a_BlockZ , a_NumBlocksToGrow );
2012-10-03 08:52:11 +00:00
}
2016-05-29 10:30:47 +02:00
bool cWorld :: GrowMelonPumpkin ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType )
2012-06-14 13:06:06 +00:00
{
MTRand Rand ;
2016-05-29 10:30:47 +02:00
return m_ChunkMap -> GrowMelonPumpkin ( a_BlockX , a_BlockY , a_BlockZ , a_BlockType , Rand );
2012-06-14 13:06:06 +00:00
}
2016-05-29 10:30:47 +02:00
int cWorld :: GrowSugarcane ( int a_BlockX , int a_BlockY , int a_BlockZ , int a_NumBlocksToGrow )
2012-10-03 08:52:11 +00:00
{
2016-05-29 10:30:47 +02:00
return m_ChunkMap -> GrowSugarcane ( a_BlockX , a_BlockY , a_BlockZ , a_NumBlocksToGrow );
2012-10-03 08:52:11 +00:00
}
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
}
2016-04-06 16:16:16 +03:00
2016-07-06 12:39:56 +02:00
void cWorld :: SetBlockMeta ( int a_X , int a_Y , int a_Z , NIBBLETYPE a_MetaData , bool a_ShouldMarkDirty , bool a_ShouldInformClients )
2012-06-14 13:06:06 +00:00
{
2016-07-06 12:39:56 +02:00
m_ChunkMap -> SetBlockMeta ( a_X , a_Y , a_Z , a_MetaData , a_ShouldMarkDirty , a_ShouldInformClients );
2012-06-14 13:06:06 +00:00
}
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
{
2015-07-29 09:04:03 -06:00
return m_ChunkMap -> GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , a_BlockType , 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-12-05 12:56:53 +01: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 ;
}
2015-07-29 09:04:03 -06:00
float SpeedX = static_cast < float > ( a_FlyAwaySpeed * ( GetTickRandomNumber ( 10 ) - 5 ));
float SpeedY = static_cast < float > ( a_FlyAwaySpeed * GetTickRandomNumber ( 50 ));
float SpeedZ = static_cast < float > ( a_FlyAwaySpeed * ( GetTickRandomNumber ( 10 ) - 5 ));
2016-01-12 15:04:59 +02: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-12-05 12:56:53 +01: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 ,
2015-07-29 09:04:03 -06:00
* itr , IsPlayerCreated , static_cast < float > ( a_SpeedX ), static_cast < float > ( a_SpeedY ), static_cast < 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
}
}
2015-03-21 15:18:17 +01:00
UInt32 cWorld :: SpawnFallingBlock ( int a_X , int a_Y , int a_Z , BLOCKTYPE BlockType , NIBBLETYPE BlockMeta )
2013-12-07 14:26:52 +01:00
{
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 ();
}
2015-03-21 15:18:17 +01:00
UInt32 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 )
{
2015-03-21 15:18:17 +01:00
LOGWARNING ( "%s: Attempting to create an experience orb with non-positive reward!" , __FUNCTION__ );
return cEntity :: INVALID_ID ;
2014-04-18 12:54:17 +01:00
}
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
}
2015-03-21 15:18:17 +01:00
UInt32 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 :
{
2015-03-21 15:18:17 +01:00
return cEntity :: INVALID_ID ;
2014-01-12 14:33:32 +01:00
}
} // 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
}
2016-05-29 10:30:47 +02:00
UInt32 cWorld :: SpawnBoat ( double a_X , double a_Y , double a_Z )
{
cBoat * Boat = new cBoat ( a_X , a_Y , a_Z );
if ( Boat == nullptr )
{
return cEntity :: INVALID_ID ;
}
if ( ! Boat -> Initialize ( * this ))
{
delete Boat ;
return cEntity :: INVALID_ID ;
}
return Boat -> GetUniqueID ();
}
2015-03-21 15:18:17 +01:00
UInt32 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
);
2015-03-21 15:18:17 +01:00
return TNT -> GetUniqueID ();
2013-06-18 19:09:51 +00:00
}
2014-12-24 07:20:17 +01:00
void cWorld :: SetBlocks ( const sSetBlockVector & a_Blocks )
{
m_ChunkMap -> SetBlocks ( a_Blocks );
}
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 );
}
2016-05-14 12:12:42 -07:00
void cWorld :: BroadcastAttachEntity ( const cEntity & a_Entity , const cEntity & a_Vehicle )
2013-03-03 19:05:11 +00:00
{
2016-05-14 12:12:42 -07:00
m_ChunkMap -> BroadcastAttachEntity ( a_Entity , a_Vehicle );
2013-03-03 19:05:11 +00:00
}
2015-05-19 12:28:31 +01:00
void cWorld :: BroadcastBlockAction ( int a_BlockX , int a_BlockY , int a_BlockZ , Byte a_Byte1 , Byte a_Byte2 , BLOCKTYPE a_BlockType , const cClientHandle * a_Exclude )
2012-08-18 10:38:15 +00:00
{
2015-07-29 09:04:03 -06:00
m_ChunkMap -> BroadcastBlockAction ( a_BlockX , a_BlockY , a_BlockZ , static_cast < char > ( a_Byte1 ), static_cast < char > ( a_Byte2 ), a_BlockType , a_Exclude );
2012-08-18 10:38:15 +00:00
}
2015-03-21 15:18:17 +01:00
void cWorld :: BroadcastBlockBreakAnimation ( UInt32 a_EntityID , int a_BlockX , int a_BlockY , int a_BlockZ , char a_Stage , const cClientHandle * a_Exclude )
2012-08-19 11:51:17 +00:00
{
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 ();
2014-10-20 21:55:07 +01:00
if (( ch == a_Exclude ) || ( ch == nullptr ) || ! ch -> IsLoggedIn () || ch -> IsDestroyed ())
2012-08-19 19:42:32 +00:00
{
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 ();
2014-10-20 21:55:07 +01:00
if (( ch == a_Exclude ) || ( ch == nullptr ) || ! ch -> IsLoggedIn () || ch -> IsDestroyed ())
2014-02-15 23:16:44 +01:00
{
continue ;
}
ch -> SendChat ( a_Message );
}
}
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
}
2016-05-14 12:12:42 -07:00
void cWorld :: BroadcastDetachEntity ( const cEntity & a_Entity , const cEntity & a_PreviousVehicle )
{
m_ChunkMap -> BroadcastDetachEntity ( a_Entity , a_PreviousVehicle );
}
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
}
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 ();
2014-10-20 21:55:07 +01:00
if (( ch == a_Exclude ) || ( ch == nullptr ) || ! ch -> IsLoggedIn () || ch -> IsDestroyed ())
2012-08-25 21:46:18 +00:00
{
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 ();
2014-10-20 21:55:07 +01:00
if (( ch == a_Exclude ) || ( ch == nullptr ) || ! ch -> IsLoggedIn () || ch -> IsDestroyed ())
2014-09-18 18:50:17 +02:00
{
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 ();
2014-10-20 21:55:07 +01:00
if (( ch == a_Exclude ) || ( ch == nullptr ) || ! ch -> IsLoggedIn () || ch -> IsDestroyed ())
2014-09-18 18:50:17 +02:00
{
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 ();
2014-10-20 21:55:07 +01:00
if (( ch == a_Exclude ) || ( ch == nullptr ) || ! ch -> IsLoggedIn () || ch -> IsDestroyed ())
2014-09-18 18:50:17 +02:00
{
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 ();
2014-10-20 21:55:07 +01:00
if (( ch == a_Exclude ) || ( ch == nullptr ) || ! ch -> IsLoggedIn () || ch -> IsDestroyed ())
2012-08-25 21:46:18 +00:00
{
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 ();
2014-10-20 21:55:07 +01:00
if (( ch == nullptr ) || ! ch -> IsLoggedIn () || ch -> IsDestroyed ())
2014-01-21 15:58:17 +02:00
{
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 ();
2014-10-20 21:55:07 +01:00
if (( ch == nullptr ) || ! ch -> IsLoggedIn () || ch -> IsDestroyed ())
2014-01-21 15:58:17 +02:00
{
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 ();
2014-10-20 21:55:07 +01:00
if (( ch == nullptr ) || ! ch -> IsLoggedIn () || ch -> IsDestroyed ())
2014-01-21 15:58:17 +02:00
{
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
}
2015-11-23 23:39:19 +00:00
void cWorld :: BroadcastSoundParticleEffect ( const EffectID 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 ();
2014-10-20 21:55:07 +01:00
if (( ch == a_Exclude ) || ( ch == nullptr ) || ! ch -> IsLoggedIn () || ch -> IsDestroyed ())
2012-08-27 17:31:16 +00:00
{
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 ();
2014-10-20 21:55:07 +01:00
if (( ch == a_Exclude ) || ( ch == nullptr ) || ! ch -> IsLoggedIn () || ch -> IsDestroyed ())
2013-07-07 13:06:06 +00:00
{
continue ;
}
2015-01-11 21:12:26 +00:00
ch -> SendTimeUpdate ( std :: chrono :: duration_cast < cTickTimeLong > ( m_WorldAge ). count (), std :: chrono :: duration_cast < cTickTimeLong > ( m_TimeOfDay ). count (), 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 ();
2014-10-20 21:55:07 +01:00
if (( ch == a_Exclude ) || ( ch == nullptr ) || ! ch -> IsLoggedIn () || ch -> IsDestroyed ())
2013-07-07 13:06:06 +00:00
{
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 );
}
2016-04-18 13:30:23 +03:00
void cWorld :: MarkChunkDirty ( int a_ChunkX , int a_ChunkZ )
2012-06-14 13:06:06 +00:00
{
2016-04-18 13:30:23 +03:00
m_ChunkMap -> MarkChunkDirty ( a_ChunkX , a_ChunkZ );
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
}
2016-01-12 15:04:59 +02:00
2014-07-24 18:32:05 +02:00
// Validate heightmap, if needed:
if ( ! a_SetChunkData -> IsHeightMapValid ())
{
a_SetChunkData -> CalculateHeightMap ();
}
2016-01-12 15:04:59 +02:00
2014-07-24 18:32:05 +02:00
// 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 ());
2016-01-12 15:04:59 +02:00
2014-07-24 18:32:05 +02:00
m_ChunkMap -> SetChunkData ( a_SetChunkData );
2016-01-12 15:04:59 +02:00
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
}
2016-01-12 15:04:59 +02: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 ();
2015-06-10 15:16:05 +01:00
cChunkSender & ChunkSender = m_ChunkSender ;
DoWithChunk (
ChunkX , ChunkZ ,
[ & ChunkSender ] ( cChunk & a_Chunk ) -> bool
{
if ( a_Chunk . HasAnyClients ())
{
ChunkSender . QueueSendChunkTo (
a_Chunk . GetPosX (),
a_Chunk . GetPosZ (),
2015-06-22 21:27:13 +01:00
cChunkSender :: E_CHUNK_PRIORITY_MEDIUM ,
2015-06-10 15:16:05 +01:00
a_Chunk . GetAllClients ()
);
}
return true ;
}
);
2014-07-24 18:32:05 +02:00
// 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
{
2015-01-11 21:12:26 +00:00
m_LastUnload = std :: chrono :: duration_cast < cTickTimeLong > ( 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 )
{
2015-09-25 18:56:49 +01:00
QueueTask ([]( cWorld & a_World ) { a_World . UnloadUnusedChunks (); });
2014-02-10 20:00:07 -05:00
}
2012-06-14 13:06:06 +00:00
2014-02-11 08:01:25 -05:00
2014-10-15 19:01:55 +02:00
void cWorld :: CollectPickupsByPlayer ( cPlayer & a_Player )
2012-06-14 13:06:06 +00:00
{
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 );
}
2016-01-12 15:04:59 +02:00
2013-08-13 22:45:29 +02:00
// 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 ();
2014-10-20 21:55:07 +01:00
if ( Client != nullptr )
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 ;
2016-02-07 19:07:14 +02:00
if ( ! ( * itr ) -> IsTicking ())
2016-02-01 22:49:34 +02:00
{
continue ;
}
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 )
{
2014-11-30 11:11:47 +01:00
// Calls the callback for the specified 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
{
2016-02-07 19:07:14 +02:00
if ( ! ( * itr ) -> IsTicking ())
2016-02-01 22:49:34 +02:00
{
continue ;
}
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
{
2014-10-20 21:55:07 +01:00
cPlayer * BestMatch = nullptr ;
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 )
{
2016-02-07 19:07:14 +02:00
if ( ! ( * itr ) -> IsTicking ())
2016-02-01 22:49:34 +02:00
{
continue ;
}
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[]
2014-10-20 21:55:07 +01:00
if ( BestMatch != nullptr )
2012-08-22 23:05:12 +00:00
{
return a_Callback . Item ( BestMatch );
}
return false ;
}
2014-11-05 21:57:38 +01:00
bool cWorld :: DoWithPlayerByUUID ( const AString & a_PlayerUUID , cPlayerListCallback & a_Callback )
2014-11-02 21:01:23 +01:00
{
cCSLock Lock ( m_CSPlayers );
for ( cPlayerList :: iterator itr = m_Players . begin (); itr != m_Players . end (); ++ itr )
{
2016-02-07 19:07:14 +02:00
if ( ! ( * itr ) -> IsTicking ())
2016-02-01 22:49:34 +02:00
{
continue ;
}
2014-11-05 21:57:38 +01:00
if (( * itr ) -> GetUUID () == a_PlayerUUID )
{
2014-11-06 08:55:33 +01:00
return a_Callback . Item ( * itr );
2014-11-02 21:01:23 +01:00
}
}
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 ;
2014-10-20 21:55:07 +01:00
cPlayer * ClosestPlayer = nullptr ;
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 )
{
2016-02-07 19:07:14 +02:00
if ( ! ( * itr ) -> IsTicking ())
2016-02-01 22:49:34 +02:00
{
continue ;
}
2012-06-14 13:06:06 +00:00
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
{
2015-07-29 09:04:03 -06:00
if ( ! LineOfSight . Trace ( a_Pos , ( Pos - a_Pos ), static_cast < 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 ();
2014-10-20 21:55:07 +01:00
if (( ch != nullptr ) && ! ch -> IsDestroyed ())
2012-06-14 13:06:06 +00:00
{
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 );
}
2015-03-21 15:18:17 +01:00
bool cWorld :: DoWithEntityByID ( UInt32 a_UniqueID , cEntityCallback & a_Callback )
2012-06-14 13:06:06 +00:00
{
2015-03-18 15:35:19 +01:00
// First check the entities-to-add:
{
cCSLock Lock ( m_CSEntitiesToAdd );
for ( auto & ent : m_EntitiesToAdd )
{
if ( ent -> GetUniqueID () == a_UniqueID )
{
a_Callback . Item ( ent );
return true ;
}
} // for ent - m_EntitiesToAdd[]
}
// Then check the chunkmap:
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 );
}
2014-10-06 21:27:53 +02:00
void cWorld :: SendChunkTo ( int a_ChunkX , int a_ChunkZ , cChunkSender :: eChunkPriority a_Priority , cClientHandle * a_Client )
2012-06-14 13:06:06 +00:00
{
2014-10-06 21:27:53 +02:00
m_ChunkSender . QueueSendChunkTo ( a_ChunkX , a_ChunkZ , a_Priority , a_Client );
2012-06-14 13:06:06 +00:00
}
2014-10-06 21:27:53 +02:00
void cWorld :: ForceSendChunkTo ( int a_ChunkX , int a_ChunkZ , cChunkSender :: eChunkPriority a_Priority , cClientHandle * a_Client )
2014-02-18 13:06:18 +01:00
{
a_Client -> AddWantedChunk ( a_ChunkX , a_ChunkZ );
2014-10-06 21:27:53 +02:00
m_ChunkSender . QueueSendChunkTo ( a_ChunkX , a_ChunkZ , a_Priority , a_Client );
2014-02-18 13:06:18 +01:00
}
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
}
2015-05-30 11:11:17 +01:00
void cWorld :: PrepareChunk ( int a_ChunkX , int a_ChunkZ , std :: unique_ptr < cChunkCoordCallback > a_CallAfter )
2014-12-10 22:35:16 +01:00
{
2015-05-30 11:11:17 +01:00
m_ChunkMap -> PrepareChunk ( a_ChunkX , a_ChunkZ , std :: move ( a_CallAfter ));
2014-12-10 22:35:16 +01: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 );
2014-11-15 15:16:52 +01:00
2014-10-15 19:01:55 +02: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 ;
}
2014-11-15 15:16:52 +01:00
2013-06-12 07:14:06 +00:00
if ( m_ChunkMap -> SetSignLines ( a_BlockX , a_BlockY , a_BlockZ , Line1 , Line2 , Line3 , Line4 ))
{
2014-10-15 19:01:55 +02:00
cRoot :: Get () -> GetPluginManager () -> CallHookUpdatedSign ( * this , a_BlockX , a_BlockY , a_BlockZ , Line1 , Line2 , Line3 , Line4 , a_Player );
2013-06-12 07:14:06 +00:00
return true ;
2012-06-16 15:06:14 +00:00
}
2013-06-12 07:14:06 +00:00
2014-11-15 15:16:52 +01:00
return false ;
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 ) {}
2016-01-12 15:04:59 +02:00
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 ;
}
2016-01-12 15:04:59 +02:00
2014-03-02 16:01:37 +01:00
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 ;
}
2016-01-12 15:04:59 +02:00
2015-04-26 00:38:41 +01:00
bool IsOpen = ( Meta & 0x4 ) != 0 ;
2014-03-02 16:01:37 +01:00
if ( a_Open != IsOpen )
{
SetBlockMeta ( a_BlockX , a_BlockY , a_BlockZ , Meta ^ 0x4 );
2015-11-23 23:39:19 +00:00
BroadcastSoundParticleEffect ( EffectID :: SFX_RANDOM_DOOR_OPEN_CLOSE , a_BlockX , a_BlockY , a_BlockZ , 0 );
2014-03-02 16:01:37 +01:00
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 );
2016-01-12 15:04:59 +02:00
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-12-10 22:35:16 +01:00
m_ChunkMap -> GenerateChunk ( a_ChunkX , a_ChunkZ );
2012-06-14 13:06:06 +00:00
}
2015-05-30 11:11:17 +01:00
void cWorld :: QueueLightChunk ( int a_ChunkX , int a_ChunkZ , std :: unique_ptr < cChunkCoordCallback > a_Callback )
2012-06-14 13:06:06 +00:00
{
2015-05-30 11:11:17 +01:00
m_Lighting . QueueChunk ( a_ChunkX , a_ChunkZ , std :: move ( a_Callback ));
2012-06-14 13:06:06 +00:00
}
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 );
}
2015-09-24 15:43:31 +01:00
bool cWorld :: ForEachLoadedChunk ( std :: function < bool ( int , int ) > a_Callback )
{
return m_ChunkMap -> ForEachLoadedChunk ( a_Callback );
}
2012-06-14 13:06:06 +00:00
void cWorld :: SaveAllChunks ( void )
{
2015-01-11 21:12:26 +00:00
m_LastSave = std :: chrono :: duration_cast < cTickTimeLong > ( 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 )
{
2015-09-25 18:56:49 +01:00
QueueTask ([]( cWorld & a_World ) { a_World . SaveAllChunks (); });
2013-08-11 21:05:44 +02:00
}
2015-09-25 18:56:49 +01:00
void cWorld :: QueueTask ( std :: function < void ( cWorld & ) > a_Task )
2013-08-11 21:05:44 +02:00
{
cCSLock Lock ( m_CSTasks );
2015-09-25 18:56:49 +01:00
m_Tasks . emplace_back ( 0 , a_Task );
2013-08-11 21:05:44 +02:00
}
2014-01-19 23:49:19 +01:00
2015-09-25 18:56:49 +01:00
void cWorld :: ScheduleTask ( int a_DelayTicks , std :: function < void ( cWorld & ) > a_Task )
2014-01-14 12:17:03 -08:00
{
2015-01-11 21:12:26 +00:00
Int64 TargetTick = a_DelayTicks + std :: chrono :: duration_cast < cTickTimeLong > ( m_WorldAge ). count ();
2015-09-25 18:56:49 +01:00
// Insert the task into the list of scheduled tasks
2014-01-14 12:17:03 -08:00
{
2015-09-25 18:56:49 +01:00
cCSLock Lock ( m_CSTasks );
m_Tasks . emplace_back ( 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
{
2016-02-07 19:07:14 +02:00
a_Entity -> SetWorld ( this );
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
}
2015-03-21 15:18:17 +01:00
bool cWorld :: HasEntity ( UInt32 a_UniqueID )
2013-04-13 21:02:10 +00:00
{
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:
2014-12-07 16:14:27 +01:00
if ( m_ChunkMap . get () == nullptr )
{
// Chunkmap has already been destroyed, there are no entities anymore.
return false ;
}
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 );
2015-07-29 09:04:03 -06:00
a_NumInLightingQueue = static_cast < int > ( m_Lighting . GetQueueLength ());
2012-06-14 13:06:06 +00:00
}
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 ;
2016-01-12 15:04:59 +02:00
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 ))
);
}
2015-07-16 15:06:54 +02:00
UInt32 cWorld :: SpawnMob ( double a_PosX , double a_PosY , double a_PosZ , eMonsterType a_MonsterType , bool a_Baby )
2012-10-28 14:57:35 +00:00
{
2014-10-20 21:55:07 +01:00
cMonster * Monster = nullptr ;
2013-09-08 02:47:02 +02:00
2013-10-20 13:25:56 +02:00
Monster = cMonster :: NewMonsterFromType ( a_MonsterType );
2015-03-21 15:18:17 +01:00
if ( Monster == nullptr )
2013-10-20 13:25:56 +02:00
{
2015-03-21 15:18:17 +01:00
return cEntity :: INVALID_ID ;
2013-10-20 13:25:56 +02:00
}
2015-03-21 15:18:17 +01:00
Monster -> SetPosition ( a_PosX , a_PosY , a_PosZ );
2016-01-12 15:04:59 +02:00
2015-07-16 15:06:54 +02:00
if ( a_Baby )
{
Monster -> SetAge ( - 1 );
}
2013-09-08 02:47:02 +02:00
return SpawnMobFinalize ( Monster );
}
2015-03-21 15:18:17 +01:00
UInt32 cWorld :: SpawnMobFinalize ( cMonster * a_Monster )
2013-09-08 02:47:02 +02:00
{
2015-03-21 15:18:17 +01:00
ASSERT ( a_Monster != nullptr );
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-10-20 21:55:07 +01:00
a_Monster = nullptr ;
2015-03-21 15:18:17 +01:00
return cEntity :: INVALID_ID ;
2013-08-08 09:13:13 +02:00
}
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-10-20 21:55:07 +01:00
a_Monster = nullptr ;
2015-03-21 15:18:17 +01:00
return cEntity :: INVALID_ID ;
2013-08-08 09:13:13 +02:00
}
2014-07-24 12:15:48 +02:00
2013-09-08 02:47:02 +02:00
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
}
2015-03-21 15:18:17 +01:00
UInt32 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 );
2014-10-20 21:55:07 +01:00
if ( Projectile == nullptr )
2013-08-22 08:55:58 +02:00
{
2015-03-21 15:18:17 +01:00
return cEntity :: INVALID_ID ;
2013-08-22 08:55:58 +02:00
}
2014-06-08 21:58:08 +02:00
if ( ! Projectile -> Initialize ( * this ))
2013-08-22 08:55:58 +02:00
{
delete Projectile ;
2014-10-20 21:55:07 +01:00
Projectile = nullptr ;
2015-03-21 15:18:17 +01:00
return cEntity :: INVALID_ID ;
2013-08-22 08:55:58 +02:00
}
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 ();
}
2016-02-05 21:27:31 +01:00
AString :: size_type Found = StrToLower ( PlayerName ). find ( StrToLower ( 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-10-25 21:54:00 +01:00
cRedstoneSimulator * 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
}
2016-01-12 15:04:59 +02:00
2014-10-25 21:54:00 +01:00
cRedstoneSimulator * res = nullptr ;
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-10-25 21:54:00 +01:00
res = new cIncrementalRedstoneSimulator ( * 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 );
}
2016-01-12 15:04:59 +02:00
2015-06-26 17:24:51 -05:00
m_SimulatorManager -> RegisterSimulator ( res , 2 /* Two game ticks is a redstone tick */ );
2016-01-12 15:04:59 +02:00
2014-02-07 22:13:55 +01:00
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
}
2016-01-12 15:04:59 +02:00
2014-10-20 21:55:07 +01:00
cFluidSimulator * res = nullptr ;
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 );
2016-01-12 15:04:59 +02:00
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
}
2016-01-12 15:04:59 +02: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 );
}
2016-01-12 15:04:59 +02:00
2014-06-08 21:58:08 +02:00
// Add all the players in the grabbed list:
{
cCSLock Lock ( m_CSPlayers );
2016-03-31 20:40:58 +03:00
for ( auto Player : PlayersToAdd )
2014-06-08 21:58:08 +02:00
{
2016-03-29 21:23:53 +03:00
ASSERT ( std :: find ( m_Players . begin (), m_Players . end (), Player ) == m_Players . end ()); // Is it already in the list? HOW?
LOGD ( "Adding player %s to world \" %s \" ." , Player -> GetName (). c_str (), m_WorldName . c_str ());
2014-07-22 17:26:48 +01:00
2016-03-29 21:23:53 +03:00
m_Players . push_back ( Player );
Player -> SetWorld ( this );
2014-06-08 21:58:08 +02:00
// Add to chunkmap, if not already there (Spawn vs MoveToWorld):
2016-03-29 21:23:53 +03:00
m_ChunkMap -> AddEntityIfNotPresent ( Player );
ASSERT ( ! Player -> IsTicking ());
Player -> SetIsTicking ( true );
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 )
{
2015-01-24 20:17:00 +01:00
cClientHandlePtr Client = ( * itr ) -> GetClientHandlePtr ();
2014-10-20 21:55:07 +01:00
if ( Client != nullptr )
2014-06-08 21:58:08 +02:00
{
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 ();
2014-10-20 21:55:07 +01:00
if ( Client != nullptr )
2014-06-08 21:58:08 +02:00
{
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
////////////////////////////////////////////////////////////////////////////////
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 ,
2014-10-20 21:55:07 +01:00
nullptr , nullptr , // We don't have lighting, chunk will be lighted when needed
2014-01-10 22:22:54 +01:00
& a_ChunkDesc . GetHeightMap (), & a_ChunkDesc . GetBiomeMap (),
2015-04-27 21:18:21 +02:00
std :: move ( a_ChunkDesc . GetEntities ()), std :: move ( a_ChunkDesc . GetBlockEntities ()),
2014-01-10 22:22:54 +01:00
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 (
2014-10-15 19:01:55 +02:00
* m_World , a_ChunkDesc . GetChunkX (), a_ChunkDesc . GetChunkZ (), & a_ChunkDesc
2014-01-10 22:22:54 +01:00
);
}
void cWorld :: cChunkGeneratorCallbacks :: CallHookChunkGenerated ( cChunkDesc & a_ChunkDesc )
{
cPluginManager :: Get () -> CallHookChunkGenerated (
2014-10-15 19:01:55 +02:00
* m_World , a_ChunkDesc . GetChunkX (), a_ChunkDesc . GetChunkZ (), & a_ChunkDesc
2014-01-10 22:22:54 +01:00
);
}
2015-03-21 17:17:26 +00:00
cBroadcaster cWorld :: GetBroadcaster ()
{
return cBroadcaster ( this );
}