2017-09-19 04:34:08 -04:00
2012-06-14 09:06:06 -04:00
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2012-09-23 18:09:57 -04:00
# include "World.h"
2020-04-03 02:57:01 -04:00
# include "BlockInfo.h"
2012-09-23 18:09:57 -04:00
# include "ClientHandle.h"
# include "Server.h"
# include "Root.h"
2014-10-23 09:15:10 -04:00
# include "IniFile.h"
2014-01-10 16:22:54 -05:00
# include "Generating/ChunkDesc.h"
2019-09-01 15:47:03 -04:00
# include "Generating/ComposableGenerator.h"
2014-07-24 12:32:05 -04:00
# include "SetChunkData.h"
2017-01-17 16:38:04 -05:00
# include "DeadlockDetect.h"
2017-05-11 08:34:36 -04:00
# include "LineBlockTracer.h"
2017-08-25 08:43:18 -04:00
# include "UUID.h"
2019-10-11 05:02:53 -04:00
# include "BlockInServerPluginInterface.h"
2014-02-13 14:36:24 -05:00
2014-02-20 09:38:37 -05:00
// Serializers
2014-01-22 08:49:21 -05:00
# include "WorldStorage/ScoreboardSerializer.h"
2012-10-13 05:53:28 -04:00
2013-08-22 02:55:58 -04:00
// Entities (except mobs):
2013-11-25 15:03:26 -05:00
# include "Entities/ExpOrb.h"
2013-12-07 08:26:52 -05:00
# include "Entities/FallingBlock.h"
2014-01-12 08:33:32 -05:00
# include "Entities/Minecart.h"
2013-08-22 02:55:58 -04:00
# include "Entities/Pickup.h"
# include "Entities/Player.h"
# include "Entities/TNTEntity.h"
2014-01-23 07:57:04 -05:00
# include "BlockEntities/CommandBlockEntity.h"
2014-07-30 16:19:51 -04:00
# include "BlockEntities/BeaconEntity.h"
2014-01-23 07:57:04 -05:00
2012-10-13 05:53:28 -04:00
// Simulators:
2012-10-14 13:06:21 -04:00
# include "Simulator/FloodyFluidSimulator.h"
2012-10-13 05:53:28 -04:00
# include "Simulator/FluidSimulator.h"
# include "Simulator/FireSimulator.h"
2013-03-14 16:03:42 -04:00
# include "Simulator/NoopFluidSimulator.h"
2014-02-07 16:13:55 -05:00
# include "Simulator/NoopRedstoneSimulator.h"
2015-06-26 18:24:51 -04:00
# include "Simulator/IncrementalRedstoneSimulator/IncrementalRedstoneSimulator.h"
2012-10-13 05:53:28 -04:00
# include "Simulator/SandSimulator.h"
2014-03-05 08:54:38 -05:00
# include "Simulator/VanillaFluidSimulator.h"
2013-03-14 15:44:27 -04:00
# include "Simulator/VaporizeFluidSimulator.h"
2012-09-23 16:53:08 -04:00
// Mobs:
2013-09-07 14:07:56 -04:00
# include "Mobs/IncludeAllMonsters.h"
2013-09-07 16:19:56 -04:00
# include "MobCensus.h"
2013-09-07 20:47:02 -04:00
# include "MobSpawner.h"
2012-09-23 16:53:08 -04:00
2012-09-23 16:14:04 -04:00
# include "Generating/Trees.h"
2013-12-08 06:17:54 -05:00
# include "Bindings/PluginManager.h"
2012-09-29 09:59:32 -04:00
# include "Blocks/BlockHandler.h"
2012-06-14 09:06:06 -04:00
# ifndef _WIN32
# include <stdlib.h>
# endif
2015-05-29 19:18:52 -04:00
# include "SpawnPrepare.h"
2015-07-06 17:41:08 -04:00
# include "FastRandom.h"
2018-07-24 17:30:49 -04:00
# include "OpaqueWorld.h"
2012-06-14 09:06:06 -04:00
2013-10-30 18:33:42 -04: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 09:06:06 -04:00
2018-07-24 17:30:49 -04:00
namespace World
{
// Implement conversion functions from OpaqueWorld.h
cBroadcastInterface * GetBroadcastInterface ( cWorld * a_World ) { return a_World ; }
cForEachChunkProvider * GetFECProvider ( cWorld * a_World ) { return a_World ; }
cWorldInterface * GetWorldInterface ( cWorld * a_World ) { return a_World ; }
cChunkInterface GetChunkInterface ( cWorld & a_World )
{
return { a_World . GetChunkMap ( ) } ;
}
}
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2013-06-15 11:29:20 -04:00
// cWorld::cLock:
cWorld : : cLock : : cLock ( cWorld & a_World ) :
2020-04-13 12:38:06 -04:00
Super ( & ( a_World . m_ChunkMap - > GetCS ( ) ) )
2013-06-15 11:29:20 -04:00
{
}
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2013-08-11 13:18:06 -04:00
// cWorld::cTickThread:
2012-06-14 09:06:06 -04:00
2013-08-11 13:18:06 -04:00
cWorld : : cTickThread : : cTickThread ( cWorld & a_World ) :
2020-04-13 12:38:06 -04:00
Super ( Printf ( " WorldTickThread: %s " , a_World . GetName ( ) . c_str ( ) ) ) ,
2013-08-11 13:18:06 -04:00
m_World ( a_World )
2012-06-14 09:06:06 -04:00
{
2013-08-11 13:18:06 -04:00
}
2012-06-14 09:06:06 -04:00
2013-08-11 13:18:06 -04:00
void cWorld : : cTickThread : : Execute ( void )
{
2014-10-20 13:59:40 -04:00
auto LastTime = std : : chrono : : steady_clock : : now ( ) ;
2015-01-10 20:54:18 -05:00
auto TickTime = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( cTickTime ( 1 ) ) ;
2013-08-19 16:23:25 -04:00
2013-08-11 13:18:06 -04:00
while ( ! m_ShouldTerminate )
2013-03-11 13:15:34 -04:00
{
2014-10-20 13:59:40 -04:00
auto NowTime = std : : chrono : : steady_clock : : now ( ) ;
2015-01-11 16:12:26 -05:00
auto WaitTime = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( NowTime - LastTime ) ;
m_World . Tick ( WaitTime , TickTime ) ;
2015-01-10 20:54:18 -05:00
TickTime = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( std : : chrono : : steady_clock : : now ( ) - NowTime ) ;
2016-01-12 08:04:59 -05:00
2015-01-10 20:54:18 -05:00
if ( TickTime < cTickTime ( 1 ) )
2013-03-11 13:15:34 -04:00
{
2015-01-10 20:54:18 -05:00
// Stretch tick time until it's at least 1 tick
std : : this_thread : : sleep_for ( cTickTime ( 1 ) - TickTime ) ;
2013-03-11 13:15:34 -04:00
}
2013-08-19 16:23:25 -04:00
LastTime = NowTime ;
2012-06-14 09:06:06 -04:00
}
2013-08-11 13:18:06 -04:00
}
2012-06-14 09:06:06 -04:00
2012-07-15 16:36:34 -04:00
2012-06-14 09:06:06 -04:00
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2012-06-14 09:06:06 -04:00
// cWorld:
2012-10-14 13:06:21 -04:00
2017-09-22 11:55:42 -04:00
cWorld : : cWorld (
const AString & a_WorldName , const AString & a_DataPath ,
cDeadlockDetect & a_DeadlockDetect , const AStringVector & a_WorldNames ,
eDimension a_Dimension , const AString & a_LinkedOverworldName
) :
2013-08-11 13:18:06 -04:00
m_WorldName ( a_WorldName ) ,
2017-09-07 08:41:16 -04:00
m_DataPath ( a_DataPath ) ,
2015-03-21 10:20:31 -04:00
m_LinkedOverworldName ( a_LinkedOverworldName ) ,
2017-09-07 08:41:16 -04:00
m_IniFileName ( m_DataPath + " /world.ini " ) ,
2013-08-11 14:16:41 -04:00
m_StorageSchema ( " Default " ) ,
2014-01-30 12:02:37 -05:00
# ifdef __arm__
2014-01-25 16:29:27 -05:00
m_StorageCompressionFactor ( 0 ) ,
2014-01-25 09:42:26 -05:00
# else
2014-01-17 14:01:14 -05:00
m_StorageCompressionFactor ( 6 ) ,
2014-01-25 09:42:26 -05:00
# endif
2017-09-07 08:41:16 -04:00
m_IsSavingEnabled ( true ) ,
2014-06-12 13:56:48 -04:00
m_Dimension ( a_Dimension ) ,
2013-12-20 13:10:07 -05:00
m_IsSpawnExplicitlySet ( false ) ,
2015-01-23 05:51:07 -05:00
m_SpawnX ( 0 ) ,
2017-09-22 11:55:42 -04:00
m_SpawnY ( cChunkDef : : Height ) ,
2015-01-23 05:51:07 -05:00
m_SpawnZ ( 0 ) ,
m_BroadcastDeathMessages ( true ) ,
m_BroadcastAchievementMessages ( true ) ,
2014-08-10 10:46:03 -04:00
m_IsDaylightCycleEnabled ( true ) ,
2012-11-01 17:38:20 -04:00
m_WorldAge ( 0 ) ,
m_TimeOfDay ( 0 ) ,
m_LastTimeUpdate ( 0 ) ,
2016-09-03 11:38:29 -04:00
m_LastChunkCheck ( 0 ) ,
2014-12-21 23:02:02 -05:00
m_LastSave ( 0 ) ,
2013-12-20 13:10:07 -05:00
m_SkyDarkness ( 0 ) ,
2017-09-22 11:55:42 -04:00
m_GameMode ( gmSurvival ) ,
2014-08-21 15:53:25 -04:00
m_bEnabledPVP ( false ) ,
m_IsDeepSnowEnabled ( false ) ,
m_ShouldLavaSpawnFire ( true ) ,
m_VillagersShouldHarvestCrops ( true ) ,
2014-10-10 10:33:19 -04:00
m_SimulatorManager ( ) ,
m_SandSimulator ( ) ,
2014-10-20 16:55:07 -04:00
m_WaterSimulator ( nullptr ) ,
2014-10-10 10:33:19 -04:00
m_LavaSimulator ( nullptr ) ,
m_FireSimulator ( ) ,
2014-10-20 16:55:07 -04:00
m_RedstoneSimulator ( nullptr ) ,
2014-08-21 15:53:25 -04:00
m_MaxPlayers ( 10 ) ,
2014-10-10 10:33:19 -04:00
m_ChunkMap ( ) ,
2014-08-21 15:53:25 -04:00
m_bAnimals ( true ) ,
2012-10-25 15:20:29 -04:00
m_Weather ( eWeather_Sunny ) ,
2015-09-27 19:02:17 -04: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 15:53:25 -04:00
m_MaxCactusHeight ( 3 ) ,
m_MaxSugarcaneHeight ( 4 ) ,
2020-03-26 13:06:15 -04:00
/* TODO: Enable when functionality exists again
2017-02-14 05:13:55 -05:00
m_IsBeetrootsBonemealable ( true ) ,
2014-08-21 15:53:25 -04:00
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 16:10:35 -04:00
m_IsBigFlowerBonemealable ( true ) ,
m_IsTallGrassBonemealable ( true ) ,
2020-03-26 13:06:15 -04:00
*/
2014-08-21 15:53:25 -04:00
m_bCommandBlocksEnabled ( true ) ,
m_bUseChatPrefixes ( false ) ,
m_TNTShrapnelLevel ( slNone ) ,
2014-10-30 16:24:10 -04:00
m_MaxViewDistance ( 12 ) ,
2014-02-05 12:43:49 -05:00
m_Scoreboard ( this ) ,
2014-02-23 08:03:40 -05:00
m_MapManager ( this ) ,
2014-01-10 16:22:54 -05:00
m_GeneratorCallbacks ( * this ) ,
2015-06-10 10:16:05 -04:00
m_ChunkSender ( * this ) ,
2017-09-22 12:16:47 -04:00
m_Lighting ( * this ) ,
2014-07-23 16:12:59 -04:00
m_TickThread ( * this )
2012-06-14 09:06:06 -04:00
{
2013-08-11 15:05:44 -04:00
LOGD ( " cWorld::cWorld( \" %s \" ) " , a_WorldName . c_str ( ) ) ;
2012-06-14 09:06:06 -04:00
2020-05-07 15:14:00 -04:00
cFile : : CreateFolderRecursive ( m_DataPath ) ;
2014-01-22 08:49:21 -05:00
2020-04-12 18:04:30 -04:00
m_ChunkMap = cpp14 : : make_unique < cChunkMap > ( this ) ;
m_ChunkMap - > TrackInDeadlockDetect ( a_DeadlockDetect , m_WorldName ) ;
2014-01-22 08:49:21 -05:00
// Load the scoreboard
2017-09-07 08:41:16 -04:00
cScoreboardSerializer Serializer ( m_DataPath , & m_Scoreboard ) ;
2014-01-22 08:49:21 -05:00
Serializer . Load ( ) ;
2017-09-22 11:55:42 -04:00
// Track the CSs used by this world in the deadlock detector:
a_DeadlockDetect . TrackCriticalSection ( m_CSClients , Printf ( " World %s clients " , m_WorldName . c_str ( ) ) ) ;
a_DeadlockDetect . TrackCriticalSection ( m_CSTasks , Printf ( " World %s tasks " , m_WorldName . c_str ( ) ) ) ;
// Load world settings from the ini file
cIniFile IniFile ;
if ( ! IniFile . ReadFile ( m_IniFileName ) )
{
LOGWARNING ( " Cannot read world settings from \" %s \" , defaults will be used. " , m_IniFileName . c_str ( ) ) ;
// TODO: More descriptions for each key
IniFile . AddHeaderComment ( " This is the per-world configuration file, managing settings such as generators, simulators, and spawn points " ) ;
IniFile . AddKeyComment ( " LinkedWorlds " , " This section governs portal world linkage; leave a value blank to disabled that associated method of teleportation " ) ;
}
// 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 ( ) ) ) ) ;
int UnusedDirtyChunksCap = IniFile . GetValueSetI ( " General " , " UnusedChunkCap " , 1000 ) ;
if ( UnusedDirtyChunksCap < 0 )
{
UnusedDirtyChunksCap * = - 1 ;
IniFile . SetValueI ( " General " , " UnusedChunkCap " , UnusedDirtyChunksCap ) ;
}
m_UnusedDirtyChunksCap = static_cast < size_t > ( UnusedDirtyChunksCap ) ;
m_BroadcastDeathMessages = IniFile . GetValueSetB ( " Broadcasting " , " BroadcastDeathMessages " , true ) ;
m_BroadcastAchievementMessages = IniFile . GetValueSetB ( " Broadcasting " , " BroadcastAchievementMessages " , true ) ;
SetMaxViewDistance ( IniFile . GetValueSetI ( " SpawnPosition " , " MaxViewDistance " , 12 ) ) ;
// Try to find the "SpawnPosition" key and coord values in the world configuration, set the flag if found
int KeyNum = IniFile . FindKey ( " SpawnPosition " ) ;
m_IsSpawnExplicitlySet =
(
( KeyNum > = 0 ) & &
(
( IniFile . FindValue ( KeyNum , " X " ) > = 0 ) & &
( IniFile . FindValue ( KeyNum , " Y " ) > = 0 ) & &
( IniFile . FindValue ( KeyNum , " Z " ) > = 0 )
)
) ;
if ( m_IsSpawnExplicitlySet )
{
LOGD ( " Spawnpoint explicitly set! " ) ;
m_SpawnX = IniFile . GetValueF ( " SpawnPosition " , " X " , m_SpawnX ) ;
m_SpawnY = IniFile . GetValueF ( " SpawnPosition " , " Y " , m_SpawnY ) ;
m_SpawnZ = IniFile . GetValueF ( " SpawnPosition " , " Z " , m_SpawnZ ) ;
}
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 ) ;
2020-03-26 13:06:15 -04:00
/* TODO: Enable when functionality exists again
2017-09-22 11:55:42 -04:00
m_IsBeetrootsBonemealable = IniFile . GetValueSetB ( " Plants " , " IsBeetrootsBonemealable " , true ) ;
m_IsCactusBonemealable = IniFile . GetValueSetB ( " Plants " , " IsCactusBonemealable " , false ) ;
m_IsCarrotsBonemealable = IniFile . GetValueSetB ( " Plants " , " IsCarrotsBonemealable " , true ) ;
m_IsCropsBonemealable = IniFile . GetValueSetB ( " Plants " , " IsCropsBonemealable " , true ) ;
m_IsGrassBonemealable = IniFile . GetValueSetB ( " Plants " , " IsGrassBonemealable " , true ) ;
m_IsMelonStemBonemealable = IniFile . GetValueSetB ( " Plants " , " IsMelonStemBonemealable " , true ) ;
m_IsMelonBonemealable = IniFile . GetValueSetB ( " Plants " , " IsMelonBonemealable " , false ) ;
m_IsPotatoesBonemealable = IniFile . GetValueSetB ( " Plants " , " IsPotatoesBonemealable " , true ) ;
m_IsPumpkinStemBonemealable = IniFile . GetValueSetB ( " Plants " , " IsPumpkinStemBonemealable " , true ) ;
m_IsPumpkinBonemealable = IniFile . GetValueSetB ( " Plants " , " IsPumpkinBonemealable " , false ) ;
m_IsSaplingBonemealable = IniFile . GetValueSetB ( " Plants " , " IsSaplingBonemealable " , true ) ;
m_IsSugarcaneBonemealable = IniFile . GetValueSetB ( " Plants " , " IsSugarcaneBonemealable " , false ) ;
m_IsBigFlowerBonemealable = IniFile . GetValueSetB ( " Plants " , " IsBigFlowerBonemealable " , true ) ;
m_IsTallGrassBonemealable = IniFile . GetValueSetB ( " Plants " , " IsTallGrassBonemealable " , true ) ;
2020-03-26 13:06:15 -04:00
*/
2017-09-22 11:55:42 -04:00
m_IsDeepSnowEnabled = IniFile . GetValueSetB ( " Physics " , " DeepSnow " , true ) ;
m_ShouldLavaSpawnFire = IniFile . GetValueSetB ( " Physics " , " ShouldLavaSpawnFire " , true ) ;
int TNTShrapnelLevel = IniFile . GetValueSetI ( " Physics " , " TNTShrapnelLevel " , static_cast < int > ( slAll ) ) ;
m_bCommandBlocksEnabled = IniFile . GetValueSetB ( " Mechanics " , " CommandBlocksEnabled " , false ) ;
m_bEnabledPVP = IniFile . GetValueSetB ( " Mechanics " , " PVPEnabled " , true ) ;
m_bUseChatPrefixes = IniFile . GetValueSetB ( " Mechanics " , " UseChatPrefixes " , true ) ;
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 ) ;
m_VillagersShouldHarvestCrops = IniFile . GetValueSetB ( " Monsters " , " VillagersShouldHarvestCrops " , true ) ;
m_IsDaylightCycleEnabled = IniFile . GetValueSetB ( " General " , " IsDaylightCycleEnabled " , true ) ;
int GameMode = IniFile . GetValueSetI ( " General " , " Gamemode " , static_cast < int > ( m_GameMode ) ) ;
int Weather = IniFile . GetValueSetI ( " General " , " Weather " , static_cast < int > ( m_Weather ) ) ;
m_WorldAge = std : : chrono : : milliseconds ( IniFile . GetValueSetI ( " General " , " WorldAgeMS " , 0LL ) ) ;
// 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 ) ;
}
}
auto WorldExists = [ & ] ( const AString & a_CheckWorldName )
{
return ( std : : find ( a_WorldNames . begin ( ) , a_WorldNames . end ( ) , a_CheckWorldName ) ! = a_WorldNames . end ( ) ) ;
} ;
if ( a_Dimension = = dimOverworld )
{
AString MyNetherName = GetName ( ) + " _nether " ;
AString MyEndName = GetName ( ) + " _the_end " ;
if ( ! WorldExists ( MyNetherName ) )
{
MyNetherName . clear ( ) ;
}
if ( ! WorldExists ( MyEndName ) )
{
MyEndName = GetName ( ) + " _end " ;
if ( ! WorldExists ( MyEndName ) )
{
MyEndName . clear ( ) ;
}
}
m_LinkedNetherWorldName = IniFile . GetValueSet ( " LinkedWorlds " , " NetherWorldName " , MyNetherName ) ;
m_LinkedEndWorldName = IniFile . GetValueSet ( " LinkedWorlds " , " EndWorldName " , MyEndName ) ;
}
else
{
m_LinkedOverworldName = IniFile . GetValueSet ( " LinkedWorlds " , " OverworldName " , GetLinkedOverworldName ( ) ) ;
}
// If we are linked to one or more worlds that do not exist, unlink them
if ( a_Dimension = = dimOverworld )
{
if ( ! m_LinkedNetherWorldName . empty ( ) & & ! WorldExists ( m_LinkedNetherWorldName ) )
{
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 . clear ( ) ;
}
if ( ! m_LinkedEndWorldName . empty ( ) & & ! WorldExists ( m_LinkedEndWorldName ) )
{
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 . clear ( ) ;
}
}
else
{
if ( ! m_LinkedOverworldName . empty ( ) & & ! WorldExists ( m_LinkedOverworldName ) )
{
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 . clear ( ) ;
}
}
// Adjust the enum-backed variables into their respective bounds:
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 ) ) ;
2019-09-01 15:47:03 -04:00
cComposableGenerator : : InitializeGeneratorDefaults ( IniFile , m_Dimension ) ;
InitializeAndLoadMobSpawningValues ( IniFile ) ;
2017-09-22 11:55:42 -04:00
SetTimeOfDay ( IniFile . GetValueSetI ( " General " , " TimeInTicks " , GetTimeOfDay ( ) ) ) ;
// preallocate some memory for ticking blocks so we don't need to allocate that often
m_BlockTickQueue . reserve ( 1000 ) ;
m_BlockTickQueueCopy . reserve ( 1000 ) ;
// Simulators:
m_SimulatorManager = cpp14 : : make_unique < cSimulatorManager > ( * this ) ;
m_WaterSimulator = InitializeFluidSimulator ( IniFile , " Water " , E_BLOCK_WATER , E_BLOCK_STATIONARY_WATER ) ;
m_LavaSimulator = InitializeFluidSimulator ( IniFile , " Lava " , E_BLOCK_LAVA , E_BLOCK_STATIONARY_LAVA ) ;
m_SandSimulator = cpp14 : : make_unique < cSandSimulator > ( * this , IniFile ) ;
m_FireSimulator = cpp14 : : make_unique < cFireSimulator > ( * this , IniFile ) ;
m_RedstoneSimulator = InitializeRedstoneSimulator ( IniFile ) ;
// Water, Lava and Redstone simulators get registered in their initialize function.
m_SimulatorManager - > RegisterSimulator ( m_SandSimulator . get ( ) , 1 ) ;
m_SimulatorManager - > RegisterSimulator ( m_FireSimulator . get ( ) , 1 ) ;
2017-09-22 12:16:47 -04:00
m_Storage . Initialize ( * this , m_StorageSchema , m_StorageCompressionFactor ) ;
2017-09-22 11:55:42 -04:00
m_Generator . Initialize ( m_GeneratorCallbacks , m_GeneratorCallbacks , IniFile ) ;
m_MapManager . LoadMapData ( ) ;
// Save any changes that the defaults may have done to the ini file:
if ( ! IniFile . WriteFile ( m_IniFileName ) )
{
LOGWARNING ( " Could not write world config to %s " , m_IniFileName . c_str ( ) ) ;
}
// Init of the spawn monster time (as they are supposed to have different spawn rate)
m_LastSpawnMonster . emplace ( cMonster : : mfHostile , cTickTimeLong ( 0 ) ) ;
m_LastSpawnMonster . emplace ( cMonster : : mfPassive , cTickTimeLong ( 0 ) ) ;
m_LastSpawnMonster . emplace ( cMonster : : mfAmbient , cTickTimeLong ( 0 ) ) ;
m_LastSpawnMonster . emplace ( cMonster : : mfWater , cTickTimeLong ( 0 ) ) ;
2012-06-14 09:06:06 -04:00
}
2013-01-11 23:46:01 -05:00
cWorld : : ~ cWorld ( )
{
2014-10-20 16:55:07 -04:00
delete m_WaterSimulator ; m_WaterSimulator = nullptr ;
delete m_LavaSimulator ; m_LavaSimulator = nullptr ;
delete m_RedstoneSimulator ; m_RedstoneSimulator = nullptr ;
2013-01-11 23:46:01 -05:00
}
2017-09-19 10:12:54 -04:00
void cWorld : : CastThunderbolt ( int a_BlockX , int a_BlockY , int a_BlockZ )
2013-02-13 14:22:08 -05:00
{
2017-09-19 10:12:54 -04:00
LOG ( " CastThunderbolt(int, int, int) is deprecated, use CastThunderbolt(Vector3i) instead " ) ;
CastThunderbolt ( { a_BlockX , a_BlockY , a_BlockZ } ) ;
}
void cWorld : : CastThunderbolt ( Vector3i a_Block )
{
BroadcastThunderbolt ( a_Block ) ;
BroadcastSoundEffect ( " entity.lightning.thunder " , a_Block , 50 , 1 ) ;
2013-02-13 14:22:08 -05:00
}
2014-03-02 15:04:01 -05:00
int cWorld : : GetDefaultWeatherInterval ( eWeather a_Weather )
{
2017-06-13 15:35:30 -04:00
auto & Random = GetRandomProvider ( ) ;
2014-03-02 15:04:01 -05:00
switch ( a_Weather )
{
case eWeather_Sunny :
{
2017-06-13 15:35:30 -04:00
return Random . RandInt ( m_MinSunnyTicks , m_MaxSunnyTicks ) ;
2014-03-02 15:04:01 -05:00
}
case eWeather_Rain :
{
2017-06-13 15:35:30 -04:00
return Random . RandInt ( m_MinRainTicks , m_MaxRainTicks ) ;
2014-03-02 15:04:01 -05:00
}
case eWeather_ThunderStorm :
{
2017-06-13 15:35:30 -04:00
return Random . RandInt ( m_MinThunderStormTicks , m_MaxThunderStormTicks ) ;
2014-03-02 15:04:01 -05:00
}
2014-04-24 23:25:03 -04:00
}
2018-02-04 18:07:12 -05:00
UNREACHABLE ( " Unsupported weather " ) ;
2014-03-02 15:04:01 -05:00
}
2013-02-13 14:22:08 -05:00
void cWorld : : SetWeather ( eWeather a_NewWeather )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
// Do the plugins agree? Do they want a different weather?
2014-03-02 15:04:01 -05:00
if ( cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookWeatherChanging ( * this , a_NewWeather ) )
{
m_WeatherInterval = GetDefaultWeatherInterval ( m_Weather ) ;
return ;
}
2016-01-12 08:04:59 -05:00
2013-02-13 14:22:08 -05:00
// Set new period for the selected weather:
2014-03-02 15:04:01 -05:00
m_WeatherInterval = GetDefaultWeatherInterval ( a_NewWeather ) ;
2016-01-12 08:04:59 -05:00
2014-03-02 15:04:01 -05:00
// The weather can't be found:
2014-03-03 14:55:04 -05:00
if ( m_WeatherInterval < 0 )
2012-06-14 09:06:06 -04:00
{
2014-03-02 15:04:01 -05:00
return ;
}
2016-01-12 08:04:59 -05:00
2013-02-13 14:22:08 -05:00
m_Weather = a_NewWeather ;
BroadcastWeather ( m_Weather ) ;
2016-01-12 08:04:59 -05:00
2013-02-13 14:22:08 -05:00
// Let the plugins know about the change:
cPluginManager : : Get ( ) - > CallHookWeatherChanged ( * this ) ;
2012-06-14 09:06:06 -04:00
}
2013-02-13 14:22:08 -05:00
void cWorld : : ChangeWeather ( void )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
// In the next tick the weather will be changed
m_WeatherInterval = 0 ;
2012-06-14 09:06:06 -04:00
}
2012-08-25 17:46:18 -04:00
2017-12-26 16:25:57 -05:00
bool cWorld : : IsWeatherWetAtXYZ ( Vector3i a_Pos )
{
if ( ( a_Pos . y < 0 ) | | ! IsWeatherWetAt ( a_Pos . x , a_Pos . z ) )
{
return false ;
}
if ( a_Pos . y > = cChunkDef : : Height )
{
return true ;
}
for ( int y = GetHeight ( a_Pos . x , a_Pos . z ) ; y > = a_Pos . y ; y - - )
{
auto BlockType = GetBlock ( { a_Pos . x , y , a_Pos . z } ) ;
if ( cBlockInfo : : IsRainBlocker ( BlockType ) )
{
return false ;
}
}
return true ;
}
2020-04-17 05:36:37 -04:00
void cWorld : : SetNextBlockToTick ( const Vector3i a_BlockPos )
2012-06-14 09:06:06 -04:00
{
2020-04-17 05:36:37 -04:00
return m_ChunkMap - > SetNextBlockToTick ( a_BlockPos ) ;
2012-06-14 09:06:06 -04:00
}
2012-08-25 17:46:18 -04:00
2016-08-15 04:41:32 -04:00
bool cWorld : : SetSpawn ( double a_X , double a_Y , double a_Z )
{
cIniFile IniFile ;
IniFile . ReadFile ( m_IniFileName ) ;
IniFile . SetValueF ( " SpawnPosition " , " X " , a_X ) ;
IniFile . SetValueF ( " SpawnPosition " , " Y " , a_Y ) ;
IniFile . SetValueF ( " SpawnPosition " , " Z " , a_Z ) ;
if ( IniFile . WriteFile ( m_IniFileName ) )
{
m_SpawnX = a_X ;
m_SpawnY = a_Y ;
m_SpawnZ = a_Z ;
2018-09-24 16:33:39 -04:00
FLOGD ( " Spawn set at {0} " , Vector3d { m_SpawnX , m_SpawnY , m_SpawnZ } ) ;
2016-08-15 04:41:32 -04:00
return true ;
}
else
{
LOGWARNING ( " Couldn't write new spawn settings to \" %s \" . " , m_IniFileName . c_str ( ) ) ;
}
return false ;
}
2012-06-14 09:06:06 -04:00
void cWorld : : InitializeSpawn ( void )
{
2015-11-13 10:08:16 -05:00
// For the debugging builds, don't make the server build too much world upon start:
2016-11-07 17:15:07 -05:00
# if defined(_DEBUG) || defined(ANDROID)
const int DefaultViewDist = 9 ;
2015-11-13 10:08:16 -05:00
# else
2016-11-07 17:15:07 -05:00
const int DefaultViewDist = 20 ; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is
2015-11-13 10:08:16 -05:00
# endif // _DEBUG
2014-07-17 13:13:23 -04:00
if ( ! m_IsSpawnExplicitlySet )
2013-11-26 16:54:40 -05:00
{
2015-11-13 10:08:16 -05: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 16:54:40 -05:00
}
2014-05-09 12:31:48 -04:00
cIniFile IniFile ;
IniFile . ReadFile ( m_IniFileName ) ;
int ViewDist = IniFile . GetValueSetI ( " SpawnPosition " , " PregenerateDistance " , DefaultViewDist ) ;
IniFile . WriteFile ( m_IniFileName ) ;
2014-01-02 07:32:55 -05:00
2015-11-13 10:08:16 -05:00
int ChunkX = 0 , ChunkZ = 0 ;
cChunkDef : : BlockToChunk ( FloorC ( m_SpawnX ) , FloorC ( m_SpawnZ ) , ChunkX , ChunkZ ) ;
2015-05-29 19:18:52 -04:00
cSpawnPrepare : : PrepareChunks ( * this , ChunkX , ChunkZ , ViewDist ) ;
2012-06-14 09:06:06 -04:00
}
2017-09-22 11:55:42 -04:00
void cWorld : : Start ( )
2012-07-15 16:07:38 -04:00
{
2017-09-22 12:16:47 -04:00
m_Lighting . Start ( ) ;
m_Storage . Start ( ) ;
2017-09-22 11:55:42 -04:00
m_Generator . Start ( ) ;
2015-06-10 10:16:05 -04:00
m_ChunkSender . Start ( ) ;
2013-08-11 14:16:41 -04:00
m_TickThread . Start ( ) ;
}
2015-11-13 10:08:16 -05:00
void cWorld : : GenerateRandomSpawn ( int a_MaxSpawnRadius )
2013-11-26 16:54:40 -05:00
{
LOGD ( " Generating random spawnpoint... " ) ;
2015-11-13 10:08:16 -05: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 16:54:40 -05:00
{
2015-11-13 10:08:16 -05: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 14:37:39 -04:00
2015-11-13 10:08:16 -05: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 08:04:59 -05:00
2015-11-13 10:08:16 -05:00
// Check 0, 0 first.
double SpawnY = 0.0 ;
if ( CanSpawnAt ( BiomeOffset . x , SpawnY , BiomeOffset . z ) )
{
2016-08-15 04:41:32 -04:00
SetSpawn ( BiomeOffset . x + 0.5 , SpawnY , BiomeOffset . z + 0.5 ) ;
2015-11-13 10:08:16 -05:00
2018-09-24 16:33:39 -04:00
FLOGINFO ( " World \" {0} \" : Generated spawnpoint position at {1:.2f} " , m_WorldName , Vector3d { m_SpawnX , m_SpawnY , m_SpawnZ } ) ;
2015-11-13 10:08:16 -05:00
return ;
}
// A search grid (searches clockwise around the origin)
2015-12-20 05:01:17 -05:00
static const int HalfChunk = static_cast < int > ( cChunkDef : : Width / 2.0f ) ;
2015-11-13 10:08:16 -05: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 07:04:34 -04:00
{
2015-11-13 10:08:16 -05:00
const Vector3i PotentialSpawn = BiomeOffset + ( ChunkOffset [ SearchGridIndex ] * RadiusOffset ) ;
2016-01-12 08:04:59 -05:00
2015-11-13 10:08:16 -05:00
if ( CanSpawnAt ( PotentialSpawn . x , SpawnY , PotentialSpawn . z ) )
2015-03-12 14:37:39 -04:00
{
2016-08-15 04:41:32 -04:00
SetSpawn ( PotentialSpawn . x + 0.5 , SpawnY , PotentialSpawn . z + 0.5 ) ;
2015-11-13 10:08:16 -05:00
int ChunkX , ChunkZ ;
cChunkDef : : BlockToChunk ( static_cast < int > ( m_SpawnX ) , static_cast < int > ( m_SpawnZ ) , ChunkX , ChunkZ ) ;
cSpawnPrepare : : PrepareChunks ( * this , ChunkX , ChunkZ , a_MaxSpawnRadius ) ;
2018-09-24 16:33:39 -04:00
FLOGINFO ( " World \" {0} \" :Generated spawnpoint position at {1:.2f} " , m_WorldName , Vector3d { m_SpawnX , m_SpawnY , m_SpawnZ } ) ;
2015-11-13 10:08:16 -05:00
return ;
2015-03-12 14:37:39 -04:00
}
2014-09-26 07:04:34 -04:00
}
2015-11-13 10:08:16 -05:00
}
m_SpawnY = GetHeight ( static_cast < int > ( m_SpawnX ) , static_cast < int > ( m_SpawnZ ) ) ;
2018-09-24 16:33:39 -04:00
FLOGWARNING ( " World \" {0} \" : Did not find an acceptable spawnpoint. Generated a random spawnpoint position at {1:.2f} " , m_WorldName , Vector3d { m_SpawnX , m_SpawnY , m_SpawnZ } ) ;
2015-11-13 10:08:16 -05:00
}
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 05:01:17 -05: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 10:08:16 -05: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 16:54:40 -05:00
{
2015-11-13 10:08:16 -05:00
continue ;
2013-11-26 16:54:40 -05:00
}
2015-11-13 10:08:16 -05:00
// Is the floor block ok
bool ValidSpawnBlock = false ;
for ( int BlockIndex = 0 ; BlockIndex < ValidSpawnBlocksCount ; + + BlockIndex )
2013-11-26 16:54:40 -05:00
{
2015-11-13 10:08:16 -05:00
ValidSpawnBlock | = ( ValidSpawnBlocks [ BlockIndex ] = = FloorBlock ) ;
2013-11-26 16:54:40 -05:00
}
2015-11-13 10:08:16 -05: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 14:37:39 -04:00
}
2015-11-13 10:08:16 -05:00
return false ;
2015-03-12 14:37:39 -04:00
}
2013-11-26 16:54:40 -05:00
2015-03-12 14:37:39 -04:00
bool cWorld : : CheckPlayerSpawnPoint ( int a_PosX , int a_PosY , int a_PosZ )
{
2015-11-13 10:08:16 -05:00
// Check height bounds
if ( ! cChunkDef : : IsValidHeight ( a_PosY ) )
2015-09-29 13:10:04 -04:00
{
return false ;
}
2015-11-13 10:08:16 -05:00
// Check that surrounding blocks are neither solid or liquid
static const Vector3i SurroundingCoords [ ] =
2015-03-12 14:37:39 -04:00
{
2015-11-13 10:08:16 -05: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 14:37:39 -04:00
} ;
2015-11-13 10:08:16 -05:00
static const int SurroundingCoordsCount = ARRAYCOUNT ( SurroundingCoords ) ;
2015-03-12 14:37:39 -04:00
2015-11-13 10:08:16 -05:00
for ( int CoordIndex = 0 ; CoordIndex < SurroundingCoordsCount ; + + CoordIndex )
2015-03-12 14:37:39 -04:00
{
2015-11-13 10:08:16 -05: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 14:37:39 -04:00
{
return false ;
}
}
2015-11-13 10:08:16 -05:00
2015-03-12 14:37:39 -04:00
return true ;
2013-11-26 16:54:40 -05:00
}
2014-04-25 19:55:38 -04:00
eWeather cWorld : : ChooseNewWeather ( )
{
// Pick a new weather. Only reasonable transitions allowed:
switch ( m_Weather )
{
case eWeather_Sunny :
2018-02-04 18:07:12 -05:00
case eWeather_ThunderStorm :
{
return eWeather_Rain ;
}
2014-04-25 19:55:38 -04:00
case eWeather_Rain :
{
2015-05-09 03:25:09 -04:00
// 1 / 8 chance of turning into a thunderstorm
2017-06-13 15:35:30 -04:00
return GetRandomProvider ( ) . RandBool ( 0.125 ) ? eWeather_ThunderStorm : eWeather_Sunny ;
2014-04-25 19:55:38 -04:00
}
}
2018-02-04 18:07:12 -05:00
UNREACHABLE ( " Unsupported weather " ) ;
2014-04-25 19:55:38 -04:00
}
2019-09-01 15:47:03 -04:00
void cWorld : : InitializeAndLoadMobSpawningValues ( cIniFile & a_IniFile )
2014-07-21 17:49:06 -04:00
{
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
2020-04-04 07:44:17 -04:00
case dimNether : DefaultMonsters = " blaze, ghast, magmacube, witherskeleton, 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 16:31:47 -04:00
case dimNotSet : ASSERT ( ! " Dimension not set " ) ; break ;
2014-07-21 17:49:06 -04:00
}
2016-01-12 08:04:59 -05:00
2014-07-21 17:49:06 -04: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 13:40:10 -04:00
eMonsterType ToAdd = cMonster : : StringToMobType ( * itr ) ;
if ( ToAdd ! = mtInvalidType )
2014-07-21 17:49:06 -04: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 ( ) ) ;
}
}
}
2017-01-17 16:38:04 -05:00
void cWorld : : Stop ( cDeadlockDetect & a_DeadlockDetect )
2012-07-15 16:07:38 -04:00
{
2013-08-14 13:56:29 -04:00
// Delete the clients that have been in this world:
{
cCSLock Lock ( m_CSClients ) ;
2015-01-24 14:17:00 -05:00
for ( auto itr = m_Clients . begin ( ) ; itr ! = m_Clients . end ( ) ; + + itr )
2013-08-14 13:56:29 -04:00
{
( * itr ) - > Destroy ( ) ;
} // for itr - m_Clients[]
m_Clients . clear ( ) ;
}
2014-06-04 15:00:55 -04:00
// Write settings to file; these are all plugin changeable values - keep updated!
cIniFile IniFile ;
IniFile . ReadFile ( m_IniFileName ) ;
2014-07-22 12:26:48 -04:00
if ( GetDimension ( ) = = dimOverworld )
2014-06-04 15:00:55 -04:00
{
2015-03-21 10:20:31 -04:00
IniFile . SetValue ( " LinkedWorlds " , " NetherWorldName " , m_LinkedNetherWorldName ) ;
IniFile . SetValue ( " LinkedWorlds " , " EndWorldName " , m_LinkedEndWorldName ) ;
2014-06-04 15:00:55 -04:00
}
2014-06-10 15:43:27 -04:00
else
{
2015-03-21 10:20:31 -04:00
IniFile . SetValue ( " LinkedWorlds " , " OverworldName " , m_LinkedOverworldName ) ;
2014-06-10 15:43:27 -04:00
}
2015-03-21 10:20:31 -04:00
IniFile . SetValueI ( " Physics " , " TNTShrapnelLevel " , static_cast < int > ( m_TNTShrapnelLevel ) ) ;
2014-06-04 15:00:55 -04:00
IniFile . SetValueB ( " Mechanics " , " CommandBlocksEnabled " , m_bCommandBlocksEnabled ) ;
IniFile . SetValueB ( " Mechanics " , " UseChatPrefixes " , m_bUseChatPrefixes ) ;
2014-08-10 10:48:20 -04:00
IniFile . SetValueB ( " General " , " IsDaylightCycleEnabled " , m_IsDaylightCycleEnabled ) ;
2015-03-21 10:20:31 -04:00
IniFile . SetValueI ( " General " , " Weather " , static_cast < int > ( m_Weather ) ) ;
2015-01-11 16:12:26 -05:00
IniFile . SetValueI ( " General " , " TimeInTicks " , GetTimeOfDay ( ) ) ;
2017-01-19 16:45:23 -05:00
IniFile . SetValueI ( " General " , " WorldAgeMS " , static_cast < Int64 > ( m_WorldAge . count ( ) ) ) ;
2014-06-04 15:00:55 -04:00
IniFile . WriteFile ( m_IniFileName ) ;
2016-01-12 08:04:59 -05:00
2013-08-11 14:16:41 -04:00
m_TickThread . Stop ( ) ;
m_Lighting . Stop ( ) ;
2012-07-15 16:07:38 -04:00
m_Generator . Stop ( ) ;
m_ChunkSender . Stop ( ) ;
2020-07-14 12:57:30 -04:00
m_Storage . Stop ( ) ; // Waits for thread to finish
2017-01-17 16:38:04 -05:00
a_DeadlockDetect . UntrackCriticalSection ( m_CSClients ) ;
a_DeadlockDetect . UntrackCriticalSection ( m_CSTasks ) ;
m_ChunkMap - > UntrackInDeadlockDetect ( a_DeadlockDetect ) ;
2020-07-14 12:57:30 -04:00
if ( IsSavingEnabled ( ) )
{
// Unload the scoreboard
cScoreboardSerializer Serializer ( m_DataPath , & m_Scoreboard ) ;
Serializer . Save ( ) ;
m_MapManager . SaveMapData ( ) ;
}
// 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.
// TODO: destructors should only be used for releasing resources, not doing extra work
m_ChunkMap . reset ( ) ;
2012-07-15 16:07:38 -04:00
}
2015-01-11 16:12:26 -05:00
void cWorld : : Tick ( std : : chrono : : milliseconds a_Dt , std : : chrono : : milliseconds a_LastTickDurationMSec )
2012-06-14 09:06:06 -04:00
{
2013-08-19 03:28:22 -04:00
// Call the plugins
2013-11-30 08:22:26 -05:00
cPluginManager : : Get ( ) - > CallHookWorldTick ( * this , a_Dt , a_LastTickDurationMSec ) ;
2016-01-12 08:04:59 -05:00
2014-07-24 12:32:05 -04: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-06 19:07:32 -04:00
2015-01-11 16:12:26 -05:00
m_WorldAge + = a_Dt ;
2012-06-14 09:06:06 -04:00
2014-08-10 10:46:03 -04:00
if ( m_IsDaylightCycleEnabled )
2012-06-14 09:06:06 -04:00
{
2015-01-17 17:24:25 -05: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 16:12:26 -05:00
m_TimeOfDay + = a_Dt ;
2012-11-01 17:38:20 -04:00
2014-08-06 19:07:32 -04:00
// Wrap time of day each 20 minutes (1200 seconds)
2015-01-11 16:12:26 -05:00
if ( m_TimeOfDay > std : : chrono : : minutes ( 20 ) )
2014-08-06 19:07:32 -04:00
{
2015-01-11 16:12:26 -05:00
m_TimeOfDay - = std : : chrono : : minutes ( 20 ) ;
2014-08-06 19:07:32 -04:00
}
2012-11-01 17:38:20 -04:00
2014-08-06 19:07:32 -04:00
// Updates the sky darkness based on current time of day
UpdateSkyDarkness ( ) ;
// Broadcast time update every 40 ticks (2 seconds)
2015-01-11 16:12:26 -05:00
if ( m_LastTimeUpdate < m_WorldAge - cTickTime ( 40 ) )
2014-08-06 19:07:32 -04:00
{
BroadcastTimeUpdate ( ) ;
2015-01-11 16:12:26 -05:00
m_LastTimeUpdate = std : : chrono : : duration_cast < cTickTimeLong > ( m_WorldAge ) ;
2014-08-06 19:07:32 -04:00
}
2012-06-14 09:06:06 -04:00
}
2014-06-06 16:31:16 -04:00
// Add entities waiting in the queue to be added:
2017-08-29 08:08:18 -04:00
cEntityList EntitiesToAdd ;
2014-06-06 16:31:16 -04:00
{
2017-08-29 08:08:18 -04:00
// Don't access chunkmap while holding lock
2014-06-06 16:31:16 -04:00
cCSLock Lock ( m_CSEntitiesToAdd ) ;
2017-08-29 08:08:18 -04:00
std : : swap ( EntitiesToAdd , m_EntitiesToAdd ) ;
}
for ( auto & Entity : EntitiesToAdd )
{
auto EntityPtr = Entity . get ( ) ;
2020-05-02 19:58:53 -04:00
ASSERT ( EntityPtr - > GetWorld ( ) = = this ) ;
2017-08-29 08:08:18 -04:00
m_ChunkMap - > AddEntity ( std : : move ( Entity ) ) ;
2020-03-05 05:52:34 -05:00
EntityPtr - > OnAddToWorld ( * this ) ;
2017-08-29 08:08:18 -04:00
ASSERT ( ! EntityPtr - > IsTicking ( ) ) ;
EntityPtr - > SetIsTicking ( true ) ;
2020-04-09 16:26:06 -04:00
cPluginManager : : Get ( ) - > CallHookSpawnedEntity ( * this , * Entity ) ;
2014-06-06 16:31:16 -04:00
}
2017-08-29 08:08:18 -04:00
EntitiesToAdd . clear ( ) ;
2014-06-06 16:31:16 -04:00
2014-06-08 15:58:08 -04:00
// Add players waiting in the queue to be added:
AddQueuedPlayers ( ) ;
2013-04-13 17:02:10 -04:00
m_ChunkMap - > Tick ( a_Dt ) ;
2016-02-07 12:07:14 -05:00
TickMobs ( a_Dt ) ;
2015-06-30 10:50:15 -04:00
m_MapManager . TickMaps ( ) ;
2012-07-15 16:36:34 -04:00
2015-01-18 05:25:16 -05:00
TickClients ( static_cast < float > ( a_Dt . count ( ) ) ) ;
2013-09-16 03:25:23 -04:00
TickQueuedBlocks ( ) ;
2013-08-11 15:05:44 -04:00
TickQueuedTasks ( ) ;
2016-01-12 08:04:59 -05:00
2015-01-18 05:25:16 -05:00
GetSimulatorManager ( ) - > Simulate ( static_cast < float > ( a_Dt . count ( ) ) ) ;
2012-06-14 09:06:06 -04:00
2015-01-18 05:25:16 -05:00
TickWeather ( static_cast < float > ( a_Dt . count ( ) ) ) ;
2012-06-14 09:06:06 -04:00
2016-09-03 11:38:29 -04:00
if ( m_WorldAge - m_LastChunkCheck > std : : chrono : : seconds ( 10 ) )
2012-06-14 09:06:06 -04:00
{
2016-09-03 11:38:29 -04:00
// Unload every 10 seconds
2012-06-14 09:06:06 -04:00
UnloadUnusedChunks ( ) ;
2016-09-03 11:38:29 -04:00
if ( m_WorldAge - m_LastSave > std : : chrono : : minutes ( 5 ) )
{
// Save every 5 minutes
SaveAllChunks ( ) ;
}
else if ( GetNumUnusedDirtyChunks ( ) > m_UnusedDirtyChunksCap )
{
// Save if we have too many dirty unused chunks
SaveAllChunks ( ) ;
}
}
2012-06-14 09:06:06 -04:00
}
2013-02-13 14:22:08 -05:00
void cWorld : : TickWeather ( float a_Dt )
2012-06-14 09:06:06 -04:00
{
2013-12-22 09:55:24 -05:00
UNUSED ( a_Dt ) ;
2013-06-24 12:50:32 -04:00
// There are no weather changes anywhere but in the Overworld:
if ( GetDimension ( ) ! = dimOverworld )
{
return ;
}
2013-02-13 14:22:08 -05:00
if ( m_WeatherInterval > 0 )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
// Not yet, wait for the weather period to end
m_WeatherInterval - - ;
2012-06-14 09:06:06 -04:00
}
2013-02-13 14:22:08 -05:00
else
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
// Change weather:
2014-04-25 19:55:38 -04:00
SetWeather ( ChooseNewWeather ( ) ) ;
}
2012-06-14 09:06:06 -04:00
2013-02-13 14:22:08 -05:00
if ( m_Weather = = eWeather_ThunderStorm )
2012-06-14 09:06:06 -04:00
{
2013-02-13 14:22:08 -05:00
// 0.5% chance per tick of thunderbolt
2017-06-13 15:35:30 -04:00
if ( GetRandomProvider ( ) . RandBool ( 0.005 ) )
2012-06-14 09:06:06 -04:00
{
2017-09-19 10:12:54 -04:00
CastThunderbolt ( { 0 , 0 , 0 } ) ; // TODO: find random positions near players to cast thunderbolts.
2012-06-14 09:06:06 -04:00
}
}
}
2015-01-11 16:12:26 -05:00
void cWorld : : TickMobs ( std : : chrono : : milliseconds a_Dt )
2012-06-14 09:06:06 -04:00
{
2013-10-22 12:30:26 -04: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 08:16:21 -04:00
// before every Mob action, we have to count them depending on the distance to players, on their family ...
2013-09-07 16:19:56 -04:00
cMobCensus MobCensus ;
m_ChunkMap - > CollectMobCensus ( MobCensus ) ;
2013-10-20 08:00:45 -04:00
if ( m_bAnimals )
2012-06-14 09:06:06 -04:00
{
2013-10-20 08:16:21 -04:00
// Spawning is enabled, spawn now:
static const cMonster : : eFamily AllFamilies [ ] =
{
cMonster : : mfHostile ,
cMonster : : mfPassive ,
cMonster : : mfAmbient ,
cMonster : : mfWater ,
} ;
2013-12-22 09:55:24 -05:00
for ( size_t i = 0 ; i < ARRAYCOUNT ( AllFamilies ) ; i + + )
2013-06-25 02:36:59 -04:00
{
2013-10-20 08:16:21 -04:00
cMonster : : eFamily Family = AllFamilies [ i ] ;
2015-01-11 16:12:26 -05:00
cTickTime SpawnDelay = cTickTime ( cMonster : : GetSpawnDelay ( Family ) ) ;
2013-10-20 08:00:45 -04:00
if (
2013-10-24 10:45:13 -04:00
( m_LastSpawnMonster [ Family ] > m_WorldAge - SpawnDelay ) | | // Not reached the needed ticks before the next round
2013-10-20 08:16:21 -04:00
MobCensus . IsCapped ( Family )
2013-10-20 08:00:45 -04:00
)
{
continue ;
}
2015-01-11 16:12:26 -05:00
m_LastSpawnMonster [ Family ] = std : : chrono : : duration_cast < cTickTimeLong > ( m_WorldAge ) ;
2013-10-20 08:16:21 -04:00
cMobSpawner Spawner ( Family , m_AllowedMobs ) ;
2013-10-20 08:00:45 -04:00
if ( Spawner . CanSpawnAnything ( ) )
2013-08-16 04:48:19 -04:00
{
2013-10-20 08:00:45 -04:00
m_ChunkMap - > SpawnMobs ( Spawner ) ;
// do the spawn
2017-09-11 17:20:49 -04:00
for ( auto & Mob : Spawner . getSpawned ( ) )
2013-08-16 04:48:19 -04:00
{
2017-09-11 17:20:49 -04:00
SpawnMobFinalize ( std : : move ( Mob ) ) ;
2013-08-16 04:48:19 -04:00
}
2013-10-20 08:00:45 -04:00
}
2014-07-17 13:13:23 -04:00
} // for i - AllFamilies[]
} // if (Spawning enabled)
2016-04-06 05:16:14 -04:00
2017-09-11 17:20:49 -04:00
ForEachEntity ( [ = ] ( cEntity & a_Entity )
2016-04-06 05:16:14 -04:00
{
2017-09-11 17:20:49 -04:00
if ( ! a_Entity . IsMob ( ) )
2016-04-06 05:16:14 -04:00
{
return false ;
}
2017-09-11 17:20:49 -04:00
if ( ! a_Entity . IsTicking ( ) )
2016-04-06 05:16:14 -04:00
{
return false ;
}
2017-09-11 17:20:49 -04:00
auto & Monster = static_cast < cMonster & > ( a_Entity ) ;
ASSERT ( Monster . GetParentChunk ( ) ! = nullptr ) ; // A ticking entity must have a valid parent chunk
2016-04-06 05:16:14 -04:00
// Tick close mobs
2017-09-11 17:20:49 -04:00
if ( Monster . GetParentChunk ( ) - > HasAnyClients ( ) )
2016-04-06 05:16:14 -04:00
{
2017-09-11 17:20:49 -04:00
Monster . Tick ( a_Dt , * ( a_Entity . GetParentChunk ( ) ) ) ;
2016-04-06 05:16:14 -04:00
}
2017-05-21 04:29:06 -04:00
// Destroy far hostile mobs except if last target was a player
2017-09-11 17:20:49 -04:00
else if ( ( Monster . GetMobFamily ( ) = = cMonster : : eFamily : : mfHostile ) & & ! Monster . WasLastTargetAPlayer ( ) )
2016-04-06 05:16:14 -04:00
{
2017-09-11 17:20:49 -04:00
if ( Monster . GetMobType ( ) ! = eMonsterType : : mtWolf )
2016-04-06 05:16:14 -04:00
{
2020-03-05 05:52:34 -05:00
Monster . Destroy ( ) ;
2016-04-06 05:16:14 -04:00
}
else
{
2017-09-11 17:20:49 -04:00
auto & Wolf = static_cast < cWolf & > ( Monster ) ;
2018-04-11 07:17:30 -04:00
if ( ! Wolf . IsAngry ( ) & & ! Wolf . IsTame ( ) )
2016-04-06 05:16:14 -04:00
{
2020-03-05 05:52:34 -05:00
Monster . Destroy ( ) ;
2016-04-06 05:16:14 -04:00
}
}
}
return false ;
}
2017-09-11 17:20:49 -04:00
) ;
2012-06-14 09:06:06 -04:00
}
2013-08-11 15:05:44 -04:00
void cWorld : : TickQueuedTasks ( void )
2014-01-14 15:17:03 -05:00
{
2014-10-12 07:18:52 -04:00
// Move the tasks to be executed to a seperate vector to avoid deadlocks on accessing m_Tasks
2015-09-25 13:56:49 -04:00
decltype ( m_Tasks ) Tasks ;
2014-01-14 15:17:03 -05:00
{
2015-09-25 13:56:49 -04:00
cCSLock Lock ( m_CSTasks ) ;
if ( m_Tasks . empty ( ) )
2014-10-13 08:49:18 -04:00
{
2015-09-25 13:56:49 -04: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 08:49:18 -04:00
{
2020-05-14 18:15:35 -04:00
const auto WorldAgeTicks = std : : chrono : : duration_cast < cTickTimeLong > ( m_WorldAge ) . count ( ) ;
return ( a_Task . first > = WorldAgeTicks ) ;
2014-10-13 08:49:18 -04:00
}
2015-09-25 13:56:49 -04: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 15:17:03 -05:00
}
2015-09-25 13:56:49 -04:00
// Execute each task:
for ( const auto & Task : Tasks )
2014-01-14 15:17:03 -05:00
{
2015-09-25 13:56:49 -04:00
Task . second ( * this ) ;
2014-01-14 15:17:03 -05:00
} // for itr - m_Tasks[]
}
2013-08-11 15:05:44 -04:00
2018-07-26 17:24:36 -04:00
2013-08-13 16:45:29 -04:00
void cWorld : : TickClients ( float a_Dt )
{
2015-01-24 14:17:00 -05:00
cClientHandlePtrs RemoveClients ;
2013-08-13 16:45:29 -04:00
{
cCSLock Lock ( m_CSClients ) ;
2016-01-12 08:04:59 -05:00
2013-08-14 07:43:55 -04:00
// Remove clients scheduled for removal:
2015-01-24 14:17:00 -05:00
for ( auto itr = m_ClientsToRemove . begin ( ) , end = m_ClientsToRemove . end ( ) ; itr ! = end ; + + itr )
2013-08-14 07:43:55 -04:00
{
2015-01-24 14:17:00 -05: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 07:43:55 -04:00
} // for itr - m_ClientsToRemove[]
m_ClientsToRemove . clear ( ) ;
2016-01-12 08:04:59 -05:00
2013-08-14 07:43:55 -04:00
// Add clients scheduled for adding:
2015-01-24 14:17:00 -05:00
for ( auto itr = m_ClientsToAdd . begin ( ) , end = m_ClientsToAdd . end ( ) ; itr ! = end ; + + itr )
2013-08-14 07:43:55 -04:00
{
2014-07-22 12:26:48 -04:00
ASSERT ( std : : find ( m_Clients . begin ( ) , m_Clients . end ( ) , * itr ) = = m_Clients . end ( ) ) ;
2013-08-14 07:43:55 -04:00
m_Clients . push_back ( * itr ) ;
} // for itr - m_ClientsToRemove[]
m_ClientsToAdd . clear ( ) ;
2016-01-12 08:04:59 -05:00
2013-08-13 16:45:29 -04:00
// Tick the clients, take out those that have been destroyed into RemoveClients
2015-01-24 14:17:00 -05:00
for ( auto itr = m_Clients . begin ( ) ; itr ! = m_Clients . end ( ) ; )
2013-08-13 16:45:29 -04: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 14:17:00 -05:00
// Delete the clients queued for removal:
RemoveClients . clear ( ) ;
2013-08-13 16:45:29 -04:00
}
2013-10-30 18:33:42 -04:00
void cWorld : : UpdateSkyDarkness ( void )
{
2015-01-11 16:12:26 -05:00
int TempTime = std : : chrono : : duration_cast < cTickTime > ( m_TimeOfDay ) . count ( ) ;
2013-10-30 18:33:42 -04:00
if ( TempTime < = TIME_SUNSET )
{
m_SkyDarkness = 0 ;
}
else if ( TempTime < = TIME_NIGHT_START )
{
2014-09-08 14:15:29 -04:00
m_SkyDarkness = static_cast < NIBBLETYPE > ( ( TIME_NIGHT_START - TempTime ) / TIME_SPAWN_DIVISOR ) ;
2013-10-30 18:33:42 -04:00
}
else if ( TempTime < = TIME_NIGHT_END )
{
m_SkyDarkness = 8 ;
}
else
{
2014-09-08 14:15:29 -04:00
m_SkyDarkness = static_cast < NIBBLETYPE > ( ( TIME_SUNRISE - TempTime ) / TIME_SPAWN_DIVISOR ) ;
2013-10-30 18:33:42 -04:00
}
}
2017-08-17 09:48:38 -04:00
void cWorld : : WakeUpSimulators ( Vector3i a_Block )
2013-02-28 02:42:45 -05:00
{
2017-08-17 09:48:38 -04:00
return m_ChunkMap - > WakeUpSimulators ( a_Block ) ;
2013-02-28 02:42:45 -05:00
}
2013-06-21 16:47:58 -04:00
void cWorld : : WakeUpSimulatorsInArea ( int a_MinBlockX , int a_MaxBlockX , int a_MinBlockY , int a_MaxBlockY , int a_MinBlockZ , int a_MaxBlockZ )
{
2017-08-17 09:48:38 -04:00
LOGWARNING ( " cWorld::WakeUpSimulatorsInArea(int, int, int) is deprecated, use cWorld::WakeUpSimulatorsInArea(Vector3i) instead. " ) ;
WakeUpSimulatorsInArea ( cCuboid ( { a_MinBlockX , a_MinBlockY , a_MinBlockZ } , { a_MaxBlockX , a_MaxBlockY , a_MaxBlockZ } ) ) ;
}
void cWorld : : WakeUpSimulatorsInArea ( const cCuboid & a_Area )
{
m_SimulatorManager - > WakeUpArea ( a_Area ) ;
2013-06-21 16:47:58 -04:00
}
2017-09-11 17:20:49 -04:00
bool cWorld : : ForEachBlockEntityInChunk ( int a_ChunkX , int a_ChunkZ , cBlockEntityCallback a_Callback )
2013-11-20 15:53:29 -05:00
{
return m_ChunkMap - > ForEachBlockEntityInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : ForEachBrewingstandInChunk ( int a_ChunkX , int a_ChunkZ , cBrewingstandCallback a_Callback )
2015-09-24 04:48:33 -04:00
{
return m_ChunkMap - > ForEachBrewingstandInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : ForEachChestInChunk ( int a_ChunkX , int a_ChunkZ , cChestCallback a_Callback )
2012-06-17 15:58:39 -04:00
{
return m_ChunkMap - > ForEachChestInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : ForEachDispenserInChunk ( int a_ChunkX , int a_ChunkZ , cDispenserCallback a_Callback )
2012-12-26 12:16:33 -05:00
{
return m_ChunkMap - > ForEachDispenserInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : ForEachDropperInChunk ( int a_ChunkX , int a_ChunkZ , cDropperCallback a_Callback )
2013-05-26 10:39:04 -04:00
{
return m_ChunkMap - > ForEachDropperInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : ForEachDropSpenserInChunk ( int a_ChunkX , int a_ChunkZ , cDropSpenserCallback a_Callback )
2013-05-26 10:39:04 -04:00
{
return m_ChunkMap - > ForEachDropSpenserInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : ForEachFurnaceInChunk ( int a_ChunkX , int a_ChunkZ , cFurnaceCallback a_Callback )
2012-06-17 15:58:39 -04:00
{
return m_ChunkMap - > ForEachFurnaceInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2013-09-07 12:12:22 -04:00
void cWorld : : DoExplosionAt ( double a_ExplosionSize , double a_BlockX , double a_BlockY , double a_BlockZ , bool a_CanCauseFire , eExplosionSource a_Source , void * a_SourceData )
2013-04-17 22:42:45 -04:00
{
2020-04-12 18:04:30 -04:00
cLock Lock ( * this ) ;
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
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 08:04:59 -05:00
2014-08-29 08:44:10 -04:00
// TODO: Implement block hardiness
2013-06-18 15:09:51 -04:00
cVector3iArray BlocksAffected ;
2013-09-07 12:12:22 -04:00
m_ChunkMap - > DoExplosionAt ( a_ExplosionSize , a_BlockX , a_BlockY , a_BlockZ , BlocksAffected ) ;
2020-03-20 19:42:16 -04:00
auto & Random = GetRandomProvider ( ) ;
auto SoundPitchMultiplier = 1.0f + ( Random . RandReal ( 1.0f ) - Random . RandReal ( 1.0f ) ) * 0.2f ;
BroadcastSoundEffect ( " entity.generic.explode " , Vector3d ( a_BlockX , a_BlockY , a_BlockZ ) , 4.0f , SoundPitchMultiplier * 0.7f ) ;
2014-08-29 08:44:10 -04:00
2020-04-02 03:29:42 -04:00
Vector3d ExplosionPos ( a_BlockX , a_BlockY , a_BlockZ ) ;
2020-04-12 18:04:30 -04:00
for ( auto Player : m_Players )
2013-04-17 22:42:45 -04:00
{
2020-04-12 18:04:30 -04:00
cClientHandle * ch = Player - > GetClientHandle ( ) ;
if ( ch = = nullptr )
2013-04-17 22:42:45 -04:00
{
2020-04-12 18:04:30 -04:00
continue ;
2013-04-17 22:42:45 -04:00
}
2020-04-12 18:04:30 -04:00
bool InRange = ( Player - > GetExplosionExposureRate ( ExplosionPos , static_cast < float > ( a_ExplosionSize ) ) > 0 ) ;
auto Speed = InRange ? Player - > GetSpeed ( ) : Vector3d { } ;
2020-04-21 16:19:22 -04:00
ch - > SendExplosion ( { a_BlockX , a_BlockY , a_BlockZ } , static_cast < float > ( a_ExplosionSize ) , BlocksAffected , Speed ) ;
2013-04-17 22:42:45 -04:00
}
2014-08-29 08:44:10 -04:00
2020-03-20 19:42:16 -04:00
auto Position = Vector3d ( a_BlockX , a_BlockY - 0.5f , a_BlockZ ) ;
auto ParticleFormula = a_ExplosionSize * 0.33f ;
auto Spread = ParticleFormula * 0.5f ;
auto ParticleCount = std : : min ( ( ParticleFormula * 125 ) , 600.0 ) ;
BroadcastParticleEffect ( " largesmoke " , Position , Vector3f { } , static_cast < float > ( Spread ) , static_cast < int > ( ParticleCount ) ) ;
Spread = ParticleFormula * 0.35f ;
ParticleCount = std : : min ( ( ParticleFormula * 550 ) , 1800.0 ) ;
BroadcastParticleEffect ( " explode " , Position , Vector3f { } , static_cast < float > ( Spread ) , static_cast < int > ( ParticleCount ) ) ;
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
cPluginManager : : Get ( ) - > CallHookExploded ( * this , a_ExplosionSize , a_CanCauseFire , a_BlockX , a_BlockY , a_BlockZ , a_Source , a_SourceData ) ;
2013-04-17 22:42:45 -04:00
}
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithBlockEntityAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cBlockEntityCallback a_Callback )
2013-11-20 15:53:29 -05:00
{
return m_ChunkMap - > DoWithBlockEntityAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithBeaconAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cBeaconCallback a_Callback )
2014-07-30 16:19:51 -04:00
{
return m_ChunkMap - > DoWithBeaconAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithBedAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cBedCallback a_Callback )
2017-07-07 03:31:45 -04:00
{
return m_ChunkMap - > DoWithBedAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithBrewingstandAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cBrewingstandCallback a_Callback )
2015-09-24 04:48:33 -04:00
{
return m_ChunkMap - > DoWithBrewingstandAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithChestAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cChestCallback a_Callback )
2012-06-17 15:58:39 -04:00
{
return m_ChunkMap - > DoWithChestAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithDispenserAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cDispenserCallback a_Callback )
2012-12-26 12:16:33 -05:00
{
return m_ChunkMap - > DoWithDispenserAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2013-05-26 10:39:04 -04:00
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithDropperAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cDropperCallback a_Callback )
2013-05-26 10:39:04 -04:00
{
return m_ChunkMap - > DoWithDropperAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithDropSpenserAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cDropSpenserCallback a_Callback )
2013-05-26 10:39:04 -04:00
{
return m_ChunkMap - > DoWithDropSpenserAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2012-12-26 12:16:33 -05:00
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithFurnaceAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cFurnaceCallback a_Callback )
2012-06-17 15:58:39 -04:00
{
return m_ChunkMap - > DoWithFurnaceAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2020-03-27 08:03:28 -04:00
bool cWorld : : DoWithHopperAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cHopperCallback a_Callback )
{
return m_ChunkMap - > DoWithHopperAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithNoteBlockAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cNoteBlockCallback a_Callback )
2013-12-14 11:52:22 -05:00
{
return m_ChunkMap - > DoWithNoteBlockAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithCommandBlockAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cCommandBlockCallback a_Callback )
2014-01-18 08:16:47 -05:00
{
return m_ChunkMap - > DoWithCommandBlockAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithMobHeadAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cMobHeadCallback a_Callback )
2014-02-18 15:40:02 -05:00
{
2014-03-07 05:44:16 -05:00
return m_ChunkMap - > DoWithMobHeadAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
2014-02-18 15:40:02 -05:00
}
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithFlowerPotAt ( int a_BlockX , int a_BlockY , int a_BlockZ , cFlowerPotCallback a_Callback )
2014-02-18 15:40:02 -05:00
{
2014-03-06 19:30:34 -05:00
return m_ChunkMap - > DoWithFlowerPotAt ( a_BlockX , a_BlockY , a_BlockZ , a_Callback ) ;
2014-02-18 15:40:02 -05:00
}
2012-06-17 15:58:39 -04: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 ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithChunk ( int a_ChunkX , int a_ChunkZ , cChunkCallback a_Callback )
2013-08-03 14:26:50 -04:00
{
return m_ChunkMap - > DoWithChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithChunkAt ( Vector3i a_BlockPos , cChunkCallback a_Callback )
2015-03-21 13:17:26 -04:00
{
return m_ChunkMap - > DoWithChunkAt ( a_BlockPos , a_Callback ) ;
}
2019-10-11 05:02:53 -04:00
bool cWorld : : GrowTree ( int a_X , int a_Y , int a_Z )
2012-06-14 09:06:06 -04:00
{
if ( GetBlock ( a_X , a_Y , a_Z ) = = E_BLOCK_SAPLING )
{
// There is a sapling here, grow a tree according to its type:
2019-10-11 05:02:53 -04:00
return GrowTreeFromSapling ( a_X , a_Y , a_Z , GetBlockMeta ( a_X , a_Y , a_Z ) ) ;
2012-06-14 09:06:06 -04:00
}
else
{
// There is nothing here, grow a tree based on the current biome here:
2019-10-11 05:02:53 -04:00
return GrowTreeByBiome ( a_X , a_Y , a_Z ) ;
2012-06-14 09:06:06 -04:00
}
}
2019-10-11 05:02:53 -04:00
bool cWorld : : GrowTreeFromSapling ( int a_X , int a_Y , int a_Z , NIBBLETYPE a_SaplingMeta )
2012-06-14 09:06:06 -04:00
{
cNoise Noise ( m_Generator . GetSeed ( ) ) ;
2012-07-15 09:33:43 -04:00
sSetBlockVector Logs , Other ;
2015-07-29 11:04:03 -04:00
auto WorldAge = static_cast < int > ( std : : chrono : : duration_cast < cTickTimeLong > ( m_WorldAge ) . count ( ) & 0xffffffff ) ;
2012-06-14 09:06:06 -04:00
switch ( a_SaplingMeta & 0x07 )
{
2019-12-22 17:38:11 -05: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 13:02:00 -04:00
case E_META_SAPLING_JUNGLE :
{
2019-09-01 03:30:00 -04:00
bool IsLarge = GetLargeTreeAdjustment ( a_X , a_Y , a_Z , a_SaplingMeta ) ;
2019-12-22 17:38:11 -05:00
GetJungleTreeImage ( { a_X , a_Y , a_Z } , Noise , WorldAge , Logs , Other , IsLarge ) ;
2015-07-13 13:02:00 -04:00
break ;
}
case E_META_SAPLING_DARK_OAK :
{
2019-09-01 03:30:00 -04:00
if ( ! GetLargeTreeAdjustment ( a_X , a_Y , a_Z , a_SaplingMeta ) )
2015-07-13 13:02:00 -04:00
{
2019-10-11 05:02:53 -04:00
return false ;
2015-07-13 13:02:00 -04:00
}
2019-12-22 17:38:11 -05:00
GetDarkoakTreeImage ( { a_X , a_Y , a_Z } , Noise , WorldAge , Logs , Other ) ;
2015-07-13 13:02:00 -04:00
break ;
}
2012-06-14 09:06:06 -04:00
}
2012-07-15 09:33:43 -04:00
Other . insert ( Other . begin ( ) , Logs . begin ( ) , Logs . end ( ) ) ;
Logs . clear ( ) ;
2019-10-11 05:02:53 -04:00
return GrowTreeImage ( Other ) ;
2012-06-14 09:06:06 -04:00
}
2019-09-01 03:30:00 -04:00
bool cWorld : : GetLargeTreeAdjustment ( int & a_X , int & a_Y , int & a_Z , NIBBLETYPE a_Meta )
{
bool IsLarge = true ;
a_Meta = a_Meta & 0x07 ;
// Check to see if we are the northwest corner
for ( int x = 0 ; x < 2 ; + + x )
{
for ( int z = 0 ; z < 2 ; + + z )
{
NIBBLETYPE meta ;
BLOCKTYPE type ;
GetBlockTypeMeta ( a_X + x , a_Y , a_Z + z , type , meta ) ;
IsLarge = IsLarge & & ( type = = E_BLOCK_SAPLING ) & & ( ( a_Meta & meta ) = = a_Meta ) ;
}
}
if ( IsLarge )
{
return true ;
}
IsLarge = true ;
// Check to see if we are the southwest corner
for ( int x = 0 ; x < 2 ; + + x )
{
for ( int z = 0 ; z > - 2 ; - - z )
{
NIBBLETYPE meta ;
BLOCKTYPE type ;
GetBlockTypeMeta ( a_X + x , a_Y , a_Z + z , type , meta ) ;
IsLarge = IsLarge & & ( type = = E_BLOCK_SAPLING ) & & ( ( a_Meta & meta ) = = a_Meta ) ;
}
}
if ( IsLarge )
{
- - a_Z ;
return true ;
}
IsLarge = true ;
// Check to see if we are the southeast corner
for ( int x = 0 ; x > - 2 ; - - x )
{
for ( int z = 0 ; z > - 2 ; - - z )
{
NIBBLETYPE meta ;
BLOCKTYPE type ;
GetBlockTypeMeta ( a_X + x , a_Y , a_Z + z , type , meta ) ;
IsLarge = IsLarge & & ( type = = E_BLOCK_SAPLING ) & & ( ( a_Meta & meta ) = = a_Meta ) ;
}
}
if ( IsLarge )
{
- - a_Z ;
- - a_X ;
return true ;
}
IsLarge = true ;
// Check to see if we are the northeast corner
for ( int x = 0 ; x > - 2 ; - - x )
{
for ( int z = 0 ; z < 2 ; + + z )
{
NIBBLETYPE meta ;
BLOCKTYPE type ;
GetBlockTypeMeta ( a_X + x , a_Y , a_Z + z , type , meta ) ;
IsLarge = IsLarge & & ( type = = E_BLOCK_SAPLING ) & & ( ( a_Meta & meta ) = = a_Meta ) ;
}
}
if ( IsLarge )
{
- - a_X ;
}
return IsLarge ;
}
2019-10-11 05:02:53 -04:00
bool cWorld : : GrowTreeByBiome ( int a_X , int a_Y , int a_Z )
2012-06-14 09:06:06 -04:00
{
cNoise Noise ( m_Generator . GetSeed ( ) ) ;
2012-07-15 09:33:43 -04:00
sSetBlockVector Logs , Other ;
2019-12-22 17:38:11 -05: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 09:33:43 -04:00
Other . insert ( Other . begin ( ) , Logs . begin ( ) , Logs . end ( ) ) ;
Logs . clear ( ) ;
2019-10-11 05:02:53 -04:00
return GrowTreeImage ( Other ) ;
2012-06-14 09:06:06 -04:00
}
2019-10-11 05:02:53 -04:00
bool cWorld : : GrowTreeImage ( const sSetBlockVector & a_Blocks )
2012-06-14 09:06:06 -04:00
{
// Check that the tree has place to grow
2016-01-12 08:04:59 -05:00
2012-06-14 09:06:06 -04: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 01:20:17 -05:00
if ( itr - > m_BlockType = = E_BLOCK_LOG )
2012-06-14 09:06:06 -04:00
{
b2 . push_back ( * itr ) ;
}
} // for itr - a_Blocks[]
2016-01-12 08:04:59 -05:00
2012-06-14 09:06:06 -04:00
// Query blocktypes and metas at those log blocks:
if ( ! GetBlocks ( b2 , false ) )
{
2019-10-11 05:02:53 -04:00
return false ;
2012-06-14 09:06:06 -04:00
}
2016-01-12 08:04:59 -05:00
2012-06-14 09:06:06 -04: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 01:20:17 -05:00
switch ( itr - > m_BlockType )
2012-06-14 09:06:06 -04:00
{
CASE_TREE_ALLOWED_BLOCKS :
{
break ;
}
default :
{
2019-10-11 05:02:53 -04:00
return false ;
2012-06-14 09:06:06 -04:00
}
}
} // for itr - b2[]
2016-01-12 08:04:59 -05:00
2012-06-14 09:06:06 -04:00
// All ok, replace blocks with the tree image:
m_ChunkMap - > ReplaceTreeBlocks ( a_Blocks ) ;
2019-10-11 05:02:53 -04:00
return true ;
2012-06-14 09:06:06 -04:00
}
2019-10-11 05:02:53 -04:00
int cWorld : : GrowPlantAt ( Vector3i a_BlockPos , int a_NumStages )
2012-06-14 09:06:06 -04:00
{
2019-10-11 05:02:53 -04:00
return m_ChunkMap - > GrowPlantAt ( a_BlockPos , a_NumStages ) ;
}
2016-01-12 08:04:59 -05:00
2016-05-29 16:10:35 -04:00
2019-10-11 05:02:53 -04:00
bool cWorld : : GrowRipePlant ( Vector3i a_BlockPos )
{
return ( GrowPlantAt ( a_BlockPos , 16 ) > 0 ) ;
2012-06-14 09:06:06 -04:00
}
2016-05-29 04:30:47 -04:00
int cWorld : : GrowCactus ( int a_BlockX , int a_BlockY , int a_BlockZ , int a_NumBlocksToGrow )
2012-10-03 04:52:11 -04:00
{
2019-10-11 05:02:53 -04:00
LOGWARNING ( " cWorld::GrowCactus is obsolete, use cWorld::GrowPlantAt instead " ) ;
return m_ChunkMap - > GrowPlantAt ( { a_BlockX , a_BlockY , a_BlockZ } ) ;
2012-10-03 04:52:11 -04:00
}
2016-05-29 04:30:47 -04:00
bool cWorld : : GrowMelonPumpkin ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType )
2012-06-14 09:06:06 -04:00
{
2019-10-11 05:02:53 -04:00
LOGWARNING ( " cWorld::GrowMelonPumpkin is obsolete, use cWorld::GrowPlantAt instead " ) ;
return ( m_ChunkMap - > GrowPlantAt ( { a_BlockX , a_BlockY , a_BlockZ } , 16 ) > 0 ) ; // 8 stages for the stem, 8 attempts for the produce
2012-06-14 09:06:06 -04:00
}
2016-05-29 04:30:47 -04:00
int cWorld : : GrowSugarcane ( int a_BlockX , int a_BlockY , int a_BlockZ , int a_NumBlocksToGrow )
2012-10-03 04:52:11 -04:00
{
2019-10-11 05:02:53 -04:00
LOGWARNING ( " cWorld::GrowSugarcane is obsolete, use cWorld::GrowPlantAt instead " ) ;
return m_ChunkMap - > GrowPlantAt ( { a_BlockX , a_BlockY , a_BlockZ } , a_NumBlocksToGrow ) ;
2012-10-03 04:52:11 -04:00
}
2014-02-03 15:26:17 -05:00
EMCSBiome cWorld : : GetBiomeAt ( int a_BlockX , int a_BlockZ )
2012-06-14 09:06:06 -04:00
{
return m_ChunkMap - > GetBiomeAt ( a_BlockX , a_BlockZ ) ;
}
2014-02-18 07:06:18 -05: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 07:44:40 -05: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 07:06:18 -05:00
}
2016-11-18 14:00:04 -05:00
void cWorld : : SetMaxViewDistance ( int a_MaxViewDistance )
{
m_MaxViewDistance = Clamp ( a_MaxViewDistance , cClientHandle : : MIN_VIEW_DISTANCE , cClientHandle : : MAX_VIEW_DISTANCE ) ;
}
2019-10-11 05:02:53 -04:00
void cWorld : : SetBlock ( Vector3i a_BlockPos , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta )
2012-06-14 09:06:06 -04:00
{
2019-10-11 05:02:53 -04:00
m_ChunkMap - > SetBlock ( a_BlockPos , a_BlockType , a_BlockMeta ) ;
2012-06-14 09:06:06 -04:00
}
2016-04-06 09:16:16 -04:00
2019-10-11 05:02:53 -04:00
void cWorld : : SetBlockMeta ( Vector3i a_BlockPos , NIBBLETYPE a_MetaData , bool a_ShouldMarkDirty , bool a_ShouldInformClients )
2012-06-14 09:06:06 -04:00
{
2019-10-11 05:02:53 -04:00
m_ChunkMap - > SetBlockMeta ( a_BlockPos , a_MetaData , a_ShouldMarkDirty , a_ShouldInformClients ) ;
2012-06-14 09:06:06 -04:00
}
2019-10-11 05:02:53 -04:00
NIBBLETYPE cWorld : : GetBlockSkyLight ( Vector3i a_BlockPos )
2012-06-14 09:06:06 -04:00
{
2019-10-11 05:02:53 -04:00
return m_ChunkMap - > GetBlockSkyLight ( a_BlockPos ) ;
2012-06-14 09:06:06 -04:00
}
2019-10-11 05:02:53 -04:00
NIBBLETYPE cWorld : : GetBlockBlockLight ( Vector3i a_BlockPos )
2012-10-20 07:40:34 -04:00
{
2019-10-11 05:02:53 -04:00
return m_ChunkMap - > GetBlockBlockLight ( a_BlockPos ) ;
2012-10-20 07:40:34 -04:00
}
2019-10-16 04:06:34 -04:00
bool cWorld : : GetBlockTypeMeta ( Vector3i a_BlockPos , BLOCKTYPE & a_BlockType , NIBBLETYPE & a_BlockMeta )
2012-06-14 09:06:06 -04:00
{
2019-10-16 04:06:34 -04:00
return m_ChunkMap - > GetBlockTypeMeta ( a_BlockPos , a_BlockType , a_BlockMeta ) ;
2012-06-14 09:06:06 -04:00
}
2019-10-11 05:02:53 -04:00
bool cWorld : : GetBlockInfo ( Vector3i a_BlockPos , BLOCKTYPE & a_BlockType , NIBBLETYPE & a_Meta , NIBBLETYPE & a_SkyLight , NIBBLETYPE & a_BlockLight )
2012-10-20 07:40:34 -04:00
{
2019-10-11 05:02:53 -04:00
return m_ChunkMap - > GetBlockInfo ( a_BlockPos , a_BlockType , a_Meta , a_SkyLight , a_BlockLight ) ;
2012-10-20 07:40:34 -04:00
}
2012-10-06 12:58:31 -04:00
bool cWorld : : WriteBlockArea ( cBlockArea & a_Area , int a_MinBlockX , int a_MinBlockY , int a_MinBlockZ , int a_DataTypes )
{
return m_ChunkMap - > WriteBlockArea ( a_Area , a_MinBlockX , a_MinBlockY , a_MinBlockZ , a_DataTypes ) ;
}
2019-10-16 04:06:34 -04:00
void cWorld : : SpawnItemPickups ( const cItems & a_Pickups , Vector3i a_BlockPos , double a_FlyAwaySpeed , bool a_IsPlayerCreated )
{
auto & random = GetRandomProvider ( ) ;
auto microX = random . RandReal < double > ( 0 , 1 ) ;
auto microZ = random . RandReal < double > ( 0 , 1 ) ;
return SpawnItemPickups ( a_Pickups , Vector3d ( microX , 0 , microZ ) + a_BlockPos , a_FlyAwaySpeed , a_IsPlayerCreated ) ;
}
void cWorld : : SpawnItemPickups ( const cItems & a_Pickups , Vector3d a_Pos , double a_FlyAwaySpeed , bool a_IsPlayerCreated )
2012-06-14 09:06:06 -04:00
{
2017-06-13 15:35:30 -04:00
auto & Random = GetRandomProvider ( ) ;
2013-12-09 18:43:06 -05:00
a_FlyAwaySpeed / = 100 ; // Pre-divide, so that we don't have to divide each time inside the loop
2012-06-14 09:06:06 -04:00
for ( cItems : : const_iterator itr = a_Pickups . begin ( ) ; itr ! = a_Pickups . end ( ) ; + + itr )
{
2014-12-05 06:56:53 -05:00
if ( ! IsValidItem ( itr - > m_ItemType ) | | ( itr - > m_ItemType = = E_BLOCK_AIR ) )
2013-12-06 15:39:42 -05:00
{
// Don't spawn pickup if item isn't even valid; should prevent client crashing too
continue ;
}
2020-03-22 20:05:47 -04:00
float SpeedX = static_cast < float > ( a_FlyAwaySpeed * Random . RandInt ( - 10 , 10 ) ) ;
float SpeedY = static_cast < float > ( a_FlyAwaySpeed * Random . RandInt ( 40 , 50 ) ) ;
float SpeedZ = static_cast < float > ( a_FlyAwaySpeed * Random . RandInt ( - 10 , 10 ) ) ;
2016-01-12 08:04:59 -05:00
2019-10-16 04:06:34 -04:00
auto Pickup = cpp14 : : make_unique < cPickup > ( a_Pos , * itr , a_IsPlayerCreated , Vector3f { SpeedX , SpeedY , SpeedZ } ) ;
2016-12-19 15:12:23 -05:00
auto PickupPtr = Pickup . get ( ) ;
PickupPtr - > Initialize ( std : : move ( Pickup ) , * this ) ;
2012-06-14 09:06:06 -04:00
}
}
2019-10-16 04:06:34 -04:00
void cWorld : : SpawnItemPickups ( const cItems & a_Pickups , Vector3d a_Pos , Vector3d a_Speed , bool a_IsPlayerCreated )
2012-06-14 09:06:06 -04:00
{
for ( cItems : : const_iterator itr = a_Pickups . begin ( ) ; itr ! = a_Pickups . end ( ) ; + + itr )
{
2014-12-05 06:56:53 -05:00
if ( ! IsValidItem ( itr - > m_ItemType ) | | ( itr - > m_ItemType = = E_BLOCK_AIR ) )
2013-12-06 15:39:42 -05:00
{
continue ;
}
2019-10-16 04:06:34 -04:00
auto pickup = cpp14 : : make_unique < cPickup > ( a_Pos , * itr , a_IsPlayerCreated , a_Speed ) ;
2019-09-29 08:59:24 -04:00
auto pickupPtr = pickup . get ( ) ;
pickupPtr - > Initialize ( std : : move ( pickup ) , * this ) ;
2012-06-14 09:06:06 -04:00
}
}
2019-09-29 08:59:24 -04:00
UInt32 cWorld : : SpawnItemPickup ( Vector3d a_Pos , const cItem & a_Item , Vector3f a_Speed , int a_LifetimeTicks , bool a_CanCombine )
2017-07-12 06:13:27 -04:00
{
2019-09-29 08:59:24 -04:00
auto pickup = cpp14 : : make_unique < cPickup > ( a_Pos , a_Item , false , a_Speed , a_LifetimeTicks , a_CanCombine ) ;
auto pickupPtr = pickup . get ( ) ;
if ( ! pickupPtr - > Initialize ( std : : move ( pickup ) , * this ) )
2017-07-12 06:13:27 -04:00
{
return cEntity : : INVALID_ID ;
}
2019-09-29 08:59:24 -04:00
return pickupPtr - > GetUniqueID ( ) ;
2017-07-12 06:13:27 -04:00
}
2020-04-09 16:25:20 -04:00
UInt32 cWorld : : SpawnFallingBlock ( Vector3d a_Pos , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta )
2013-12-07 08:26:52 -05:00
{
2019-09-29 08:59:24 -04:00
auto fallingBlock = cpp14 : : make_unique < cFallingBlock > ( a_Pos , a_BlockType , a_BlockMeta ) ;
auto fallingBlockPtr = fallingBlock . get ( ) ;
auto ID = fallingBlock - > GetUniqueID ( ) ;
if ( ! fallingBlockPtr - > Initialize ( std : : move ( fallingBlock ) , * this ) )
2017-05-02 07:16:59 -04:00
{
return cEntity : : INVALID_ID ;
}
2016-12-19 15:12:23 -05:00
return ID ;
2013-12-07 08:26:52 -05:00
}
2019-09-29 08:59:24 -04:00
UInt32 cWorld : : SpawnExperienceOrb ( Vector3d a_Pos , int a_Reward )
2013-11-25 15:03:26 -05:00
{
2014-04-18 07:54:17 -04:00
if ( a_Reward < 1 )
{
2015-03-21 10:18:17 -04:00
LOGWARNING ( " %s: Attempting to create an experience orb with non-positive reward! " , __FUNCTION__ ) ;
return cEntity : : INVALID_ID ;
2014-04-18 07:54:17 -04:00
}
2019-09-29 08:59:24 -04:00
auto expOrb = cpp14 : : make_unique < cExpOrb > ( a_Pos , a_Reward ) ;
auto expOrbPtr = expOrb . get ( ) ;
if ( ! expOrbPtr - > Initialize ( std : : move ( expOrb ) , * this ) )
2017-05-02 07:16:59 -04:00
{
return cEntity : : INVALID_ID ;
}
2019-09-29 08:59:24 -04:00
return expOrbPtr - > GetUniqueID ( ) ;
2013-11-25 15:03:26 -05:00
}
2019-09-29 08:59:24 -04:00
std : : vector < UInt32 > cWorld : : SpawnSplitExperienceOrbs ( Vector3d a_Pos , int a_Reward )
2018-08-02 10:59:10 -04:00
{
std : : vector < UInt32 > OrbsID ;
if ( a_Reward < 1 )
{
LOGWARNING ( " %s: Attempting to create an experience orb with non-positive reward! " , __FUNCTION__ ) ;
return OrbsID ;
}
std : : vector < int > Rewards = cExpOrb : : Split ( a_Reward ) ;
// Check generate number to decide speed limit (distribute range)
2019-09-27 11:51:44 -04:00
float SpeedLimit = static_cast < float > ( ( Rewards . size ( ) / 2 ) + 5 ) ;
2018-08-02 10:59:10 -04:00
if ( SpeedLimit > 10 )
{
SpeedLimit = 10 ;
}
auto & Random = GetRandomProvider ( ) ;
for ( auto Reward : Rewards )
{
2019-09-29 08:59:24 -04:00
auto ExpOrb = cpp14 : : make_unique < cExpOrb > ( a_Pos , Reward ) ;
2018-08-02 10:59:10 -04:00
auto ExpOrbPtr = ExpOrb . get ( ) ;
double SpeedX = Random . RandReal ( - SpeedLimit , SpeedLimit ) ;
double SpeedY = Random . RandReal ( 0.5 ) ;
double SpeedZ = Random . RandReal ( - SpeedLimit , SpeedLimit ) ;
ExpOrbPtr - > SetSpeed ( SpeedX , SpeedY , SpeedZ ) ;
UInt32 Id = ExpOrbPtr - > GetUniqueID ( ) ;
if ( ExpOrbPtr - > Initialize ( std : : move ( ExpOrb ) , * this ) )
{
OrbsID . push_back ( Id ) ;
}
}
return OrbsID ;
}
2019-09-29 08:59:24 -04:00
UInt32 cWorld : : SpawnMinecart ( Vector3d a_Pos , int a_MinecartType , const cItem & a_Content , int a_BlockHeight )
2014-01-12 08:33:32 -05:00
{
2016-12-19 15:12:23 -05:00
std : : unique_ptr < cMinecart > Minecart ;
2014-01-12 08:33:32 -05:00
switch ( a_MinecartType )
{
2019-09-29 08:59:24 -04:00
case E_ITEM_MINECART : Minecart = cpp14 : : make_unique < cRideableMinecart > ( a_Pos , a_Content , a_BlockHeight ) ; break ;
case E_ITEM_CHEST_MINECART : Minecart = cpp14 : : make_unique < cMinecartWithChest > ( a_Pos ) ; break ;
case E_ITEM_FURNACE_MINECART : Minecart = cpp14 : : make_unique < cMinecartWithFurnace > ( a_Pos ) ; break ;
case E_ITEM_MINECART_WITH_TNT : Minecart = cpp14 : : make_unique < cMinecartWithTNT > ( a_Pos ) ; break ;
case E_ITEM_MINECART_WITH_HOPPER : Minecart = cpp14 : : make_unique < cMinecartWithHopper > ( a_Pos ) ; break ;
2014-01-12 08:33:32 -05:00
default :
{
2015-03-21 10:18:17 -04:00
return cEntity : : INVALID_ID ;
2014-01-12 08:33:32 -05:00
}
} // switch (a_MinecartType)
2016-12-19 15:12:23 -05:00
auto MinecartPtr = Minecart . get ( ) ;
if ( ! MinecartPtr - > Initialize ( std : : move ( Minecart ) , * this ) )
2017-05-02 07:16:59 -04:00
{
return cEntity : : INVALID_ID ;
}
2016-12-19 15:12:23 -05:00
return MinecartPtr - > GetUniqueID ( ) ;
2014-01-12 08:33:32 -05:00
}
2017-09-07 04:25:34 -04:00
UInt32 cWorld : : SpawnBoat ( Vector3d a_Pos , cBoat : : eMaterial a_Material )
2016-05-29 04:30:47 -04:00
{
2017-09-07 04:25:34 -04:00
auto Boat = cpp14 : : make_unique < cBoat > ( a_Pos , a_Material ) ;
2016-12-19 15:12:23 -05:00
auto BoatPtr = Boat . get ( ) ;
if ( ! BoatPtr - > Initialize ( std : : move ( Boat ) , * this ) )
2016-05-29 04:30:47 -04:00
{
return cEntity : : INVALID_ID ;
}
2016-12-19 15:12:23 -05:00
return BoatPtr - > GetUniqueID ( ) ;
2016-05-29 04:30:47 -04:00
}
2018-07-26 17:24:36 -04:00
2020-03-22 11:33:36 -04:00
UInt32 cWorld : : SpawnPrimedTNT ( Vector3d a_Pos , int a_FuseTicks , double a_InitialVelocityCoeff , bool a_ShouldPlayFuseSound )
2013-06-18 15:09:51 -04:00
{
2017-09-07 04:25:34 -04:00
auto TNT = cpp14 : : make_unique < cTNTEntity > ( a_Pos , a_FuseTicks ) ;
2016-12-19 15:12:23 -05:00
auto TNTPtr = TNT . get ( ) ;
if ( ! TNTPtr - > Initialize ( std : : move ( TNT ) , * this ) )
2017-05-02 07:16:59 -04:00
{
return cEntity : : INVALID_ID ;
}
2016-12-19 15:12:23 -05:00
2020-03-22 11:33:36 -04:00
if ( a_ShouldPlayFuseSound )
{
BroadcastSoundEffect ( " entity.tnt.primed " , a_Pos , 1.0f , 1.0f ) ;
}
2017-06-13 15:35:30 -04:00
auto & Random = GetRandomProvider ( ) ;
2016-12-19 15:12:23 -05:00
TNTPtr - > SetSpeed (
2020-03-22 11:33:36 -04:00
a_InitialVelocityCoeff * Random . RandReal ( - 0.5f , 0.5f ) ,
2014-03-05 17:12:48 -05:00
a_InitialVelocityCoeff * 2 ,
2020-03-22 11:33:36 -04:00
a_InitialVelocityCoeff * Random . RandReal ( - 0.5f , 0.5f )
2014-03-20 04:28:29 -04:00
) ;
2016-12-19 15:12:23 -05:00
return TNTPtr - > GetUniqueID ( ) ;
2013-06-18 15:09:51 -04:00
}
2014-12-24 01:20:17 -05:00
void cWorld : : SetBlocks ( const sSetBlockVector & a_Blocks )
{
m_ChunkMap - > SetBlocks ( a_Blocks ) ;
}
2012-06-14 09:06:06 -04:00
void cWorld : : ReplaceBlocks ( const sSetBlockVector & a_Blocks , BLOCKTYPE a_FilterBlockType )
{
m_ChunkMap - > ReplaceBlocks ( a_Blocks , a_FilterBlockType ) ;
}
bool cWorld : : GetBlocks ( sSetBlockVector & a_Blocks , bool a_ContinueOnFailure )
{
return m_ChunkMap - > GetBlocks ( a_Blocks , a_ContinueOnFailure ) ;
}
2019-10-16 04:06:34 -04:00
bool cWorld : : DigBlock ( Vector3i a_BlockPos )
{
BLOCKTYPE blockType ;
NIBBLETYPE blockMeta ;
GetBlockTypeMeta ( a_BlockPos , blockType , blockMeta ) ;
cChunkInterface chunkInterface ( GetChunkMap ( ) ) ;
auto blockHandler = cBlockInfo : : GetHandler ( blockType ) ;
blockHandler - > OnBreaking ( chunkInterface , * this , a_BlockPos ) ;
if ( ! m_ChunkMap - > DigBlock ( a_BlockPos ) )
{
return false ;
}
blockHandler - > OnBroken ( chunkInterface , * this , a_BlockPos , blockType , blockMeta ) ;
return true ;
}
bool cWorld : : DropBlockAsPickups ( Vector3i a_BlockPos , const cEntity * a_Digger , const cItem * a_Tool )
{
auto pickups = PickupsFromBlock ( a_BlockPos , a_Digger , a_Tool ) ;
if ( ! DigBlock ( a_BlockPos ) )
{
return false ;
}
2020-03-22 20:05:47 -04:00
SpawnItemPickups ( pickups , Vector3d ( 0.5 , 0.5 , 0.5 ) + a_BlockPos , 10 ) ;
2019-10-16 04:06:34 -04:00
return true ;
}
cItems cWorld : : PickupsFromBlock ( Vector3i a_BlockPos , const cEntity * a_Digger , const cItem * a_Tool )
2012-06-14 09:06:06 -04:00
{
2019-10-16 04:06:34 -04:00
return m_ChunkMap - > PickupsFromBlock ( a_BlockPos , a_Digger , a_Tool ) ;
2012-06-14 09:06:06 -04:00
}
2017-07-31 15:50:40 -04:00
void cWorld : : SendBlockTo ( int a_X , int a_Y , int a_Z , cPlayer & a_Player )
2012-06-14 09:06:06 -04:00
{
m_ChunkMap - > SendBlockTo ( a_X , a_Y , a_Z , a_Player ) ;
}
2013-03-03 14:05:11 -05:00
int cWorld : : GetHeight ( int a_X , int a_Z )
2012-06-14 09:06:06 -04:00
{
return m_ChunkMap - > GetHeight ( a_X , a_Z ) ;
}
2013-04-13 17:02:10 -04:00
bool cWorld : : TryGetHeight ( int a_BlockX , int a_BlockZ , int & a_Height )
{
return m_ChunkMap - > TryGetHeight ( a_BlockX , a_BlockZ , a_Height ) ;
}
2012-08-24 03:58:26 -04:00
void cWorld : : SendBlockEntity ( int a_BlockX , int a_BlockY , int a_BlockZ , cClientHandle & a_Client )
{
m_ChunkMap - > SendBlockEntity ( a_BlockX , a_BlockY , a_BlockZ , a_Client ) ;
}
2016-04-18 06:30:23 -04:00
void cWorld : : MarkChunkDirty ( int a_ChunkX , int a_ChunkZ )
2012-06-14 09:06:06 -04:00
{
2016-04-18 06:30:23 -04:00
m_ChunkMap - > MarkChunkDirty ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
void cWorld : : MarkChunkSaving ( int a_ChunkX , int a_ChunkZ )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkMap - > MarkChunkSaving ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
void cWorld : : MarkChunkSaved ( int a_ChunkX , int a_ChunkZ )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkMap - > MarkChunkSaved ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2017-07-20 07:19:18 -04:00
void cWorld : : QueueSetChunkData ( cSetChunkDataPtr a_SetChunkData )
2012-06-14 09:06:06 -04:00
{
// Validate biomes, if needed:
2014-07-24 12:32:05 -04:00
if ( ! a_SetChunkData - > AreBiomesValid ( ) )
2012-06-14 09:06:06 -04:00
{
// The biomes are not assigned, get them from the generator:
2019-09-01 03:30:00 -04:00
m_Generator . GenerateBiomes ( { a_SetChunkData - > GetChunkX ( ) , a_SetChunkData - > GetChunkZ ( ) } , a_SetChunkData - > GetBiomes ( ) ) ;
2014-07-24 12:32:05 -04:00
a_SetChunkData - > MarkBiomesValid ( ) ;
2012-06-14 09:06:06 -04:00
}
2016-01-12 08:04:59 -05:00
2014-07-24 12:32:05 -04:00
// Validate heightmap, if needed:
if ( ! a_SetChunkData - > IsHeightMapValid ( ) )
{
a_SetChunkData - > CalculateHeightMap ( ) ;
}
2016-01-12 08:04:59 -05:00
2014-07-24 12:32:05 -04: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 ) ;
2016-12-19 15:12:23 -05:00
m_SetChunkDataQueue . emplace_back ( std : : move ( a_SetChunkData ) ) ;
2014-07-24 12:32:05 -04:00
}
void cWorld : : SetChunkData ( cSetChunkData & a_SetChunkData )
{
ASSERT ( a_SetChunkData . AreBiomesValid ( ) ) ;
ASSERT ( a_SetChunkData . IsHeightMapValid ( ) ) ;
2016-01-12 08:04:59 -05:00
2014-07-24 12:32:05 -04:00
m_ChunkMap - > SetChunkData ( a_SetChunkData ) ;
2016-01-12 08:04:59 -05:00
2013-04-01 16:56:25 -04:00
// Initialize the entities (outside the m_ChunkMap's CS, to fix FS #347):
2016-12-19 15:12:23 -05:00
for ( auto & Entity : a_SetChunkData . GetEntities ( ) )
2013-04-01 14:24:05 -04:00
{
2016-12-19 15:12:23 -05:00
auto EntityPtr = Entity . get ( ) ;
EntityPtr - > Initialize ( std : : move ( Entity ) , * this ) ;
2013-04-01 14:24:05 -04:00
}
2016-01-12 08:04:59 -05:00
2012-06-14 09:06:06 -04:00
// If a client is requesting this chunk, send it to them:
2014-07-24 12:32:05 -04:00
int ChunkX = a_SetChunkData . GetChunkX ( ) ;
int ChunkZ = a_SetChunkData . GetChunkZ ( ) ;
2015-06-10 10:16:05 -04: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 16:27:13 -04:00
cChunkSender : : E_CHUNK_PRIORITY_MEDIUM ,
2015-06-10 10:16:05 -04:00
a_Chunk . GetAllClients ( )
) ;
}
return true ;
}
) ;
2014-07-24 12:32:05 -04:00
// Save the chunk right after generating, so that we don't have to generate it again on next run
2017-09-07 08:41:16 -04:00
// If saving is disabled, then the chunk was marked dirty so it will get
// saved if saving is later enabled.
if ( a_SetChunkData . ShouldMarkDirty ( ) & & IsSavingEnabled ( ) )
2012-06-14 09:06:06 -04:00
{
2014-08-28 05:36:35 -04:00
m_Storage . QueueSaveChunk ( ChunkX , ChunkZ ) ;
2012-06-14 09:06:06 -04: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 ) ;
}
2019-09-24 08:20:50 -04:00
bool cWorld : : GetChunkData ( cChunkCoords a_Coords , cChunkDataCallback & a_Callback ) const
2012-06-14 09:06:06 -04:00
{
2019-09-24 08:20:50 -04:00
return m_ChunkMap - > GetChunkData ( a_Coords , a_Callback ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
bool cWorld : : GetChunkBlockTypes ( int a_ChunkX , int a_ChunkZ , BLOCKTYPE * a_BlockTypes )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > GetChunkBlockTypes ( a_ChunkX , a_ChunkZ , a_BlockTypes ) ;
2012-06-14 09:06:06 -04:00
}
2014-09-05 16:16:48 -04:00
bool cWorld : : IsChunkQueued ( int a_ChunkX , int a_ChunkZ ) const
{
return m_ChunkMap - > IsChunkQueued ( a_ChunkX , a_ChunkZ ) ;
}
2013-04-13 17:02:10 -04:00
bool cWorld : : IsChunkValid ( int a_ChunkX , int a_ChunkZ ) const
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > IsChunkValid ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
bool cWorld : : HasChunkAnyClients ( int a_ChunkX , int a_ChunkZ ) const
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > HasChunkAnyClients ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2013-03-03 14:05:11 -05:00
void cWorld : : UnloadUnusedChunks ( void )
2012-06-14 09:06:06 -04:00
{
2016-09-03 11:38:29 -04:00
m_LastChunkCheck = std : : chrono : : duration_cast < cTickTimeLong > ( m_WorldAge ) ;
2012-06-14 09:06:06 -04:00
m_ChunkMap - > UnloadUnusedChunks ( ) ;
}
2014-02-10 20:00:07 -05:00
void cWorld : : QueueUnloadUnusedChunks ( void )
{
2015-09-25 13:56:49 -04:00
QueueTask ( [ ] ( cWorld & a_World ) { a_World . UnloadUnusedChunks ( ) ; } ) ;
2014-02-10 20:00:07 -05:00
}
2012-06-14 09:06:06 -04:00
2014-02-11 08:01:25 -05:00
2014-10-15 13:01:55 -04:00
void cWorld : : CollectPickupsByPlayer ( cPlayer & a_Player )
2012-06-14 09:06:06 -04:00
{
m_ChunkMap - > CollectPickupsByPlayer ( a_Player ) ;
}
2016-12-19 15:12:23 -05:00
void cWorld : : AddPlayer ( std : : unique_ptr < cPlayer > a_Player , cWorld * a_OldWorld )
2012-06-14 09:06:06 -04:00
{
2014-06-08 15:58:08 -04:00
cCSLock Lock ( m_CSPlayersToAdd ) ;
2016-12-19 15:12:23 -05:00
m_PlayersToAdd . emplace_back ( std : : move ( a_Player ) , a_OldWorld ) ;
2012-06-14 09:06:06 -04:00
}
2020-03-05 05:52:34 -05:00
std : : unique_ptr < cPlayer > cWorld : : RemovePlayer ( cPlayer & a_Player )
2012-06-14 09:06:06 -04:00
{
2020-03-05 05:52:34 -05:00
// Check the chunkmap
std : : unique_ptr < cPlayer > PlayerPtr ( static_cast < cPlayer * > ( m_ChunkMap - > RemoveEntity ( a_Player ) . release ( ) ) ) ;
2016-12-19 15:12:23 -05:00
2020-03-05 05:52:34 -05:00
if ( PlayerPtr ! = nullptr )
2014-06-21 17:07:38 -04:00
{
2020-03-05 05:52:34 -05:00
// Player found in the world, tell it it's being removed
PlayerPtr - > OnRemoveFromWorld ( * this ) ;
2014-06-21 17:07:38 -04:00
}
2020-03-05 05:52:34 -05:00
else // Check the awaiting players list
2014-06-08 15:58:08 -04:00
{
cCSLock Lock ( m_CSPlayersToAdd ) ;
2020-03-05 05:52:34 -05:00
auto itr = std : : find_if ( m_PlayersToAdd . begin ( ) , m_PlayersToAdd . end ( ) ,
[ & ] ( const decltype ( m_PlayersToAdd ) : : value_type & value )
{
return ( value . first . get ( ) = = & a_Player ) ;
}
) ;
if ( itr ! = m_PlayersToAdd . end ( ) )
2016-08-22 13:43:43 -04:00
{
2020-03-05 05:52:34 -05:00
PlayerPtr = std : : move ( itr - > first ) ;
m_PlayersToAdd . erase ( itr ) ;
}
2014-06-08 15:58:08 -04:00
}
2020-03-05 05:52:34 -05:00
// Remove from the player list
2013-08-13 16:45:29 -04:00
{
2020-04-12 18:04:30 -04:00
cLock Lock ( * this ) ;
2016-12-19 15:12:23 -05:00
LOGD ( " Removing player %s from world \" %s \" " , a_Player . GetName ( ) . c_str ( ) , m_WorldName . c_str ( ) ) ;
m_Players . remove ( & a_Player ) ;
2013-08-13 16:45:29 -04:00
}
2016-01-12 08:04:59 -05:00
2013-08-13 16:45:29 -04:00
// Remove the player's client from the list of clients to be ticked:
2016-12-19 15:12:23 -05:00
cClientHandle * Client = a_Player . GetClientHandle ( ) ;
2014-10-20 16:55:07 -04:00
if ( Client ! = nullptr )
2013-08-13 16:45:29 -04:00
{
2014-06-08 15:58:08 -04:00
Client - > RemoveFromWorld ( ) ;
m_ChunkMap - > RemoveClientFromChunks ( Client ) ;
2013-08-13 16:45:29 -04:00
cCSLock Lock ( m_CSClients ) ;
2014-06-08 15:58:08 -04:00
m_ClientsToRemove . push_back ( Client ) ;
2013-08-13 16:45:29 -04:00
}
2016-12-19 15:12:23 -05:00
return PlayerPtr ;
}
# ifdef _DEBUG
bool cWorld : : IsPlayerReferencedInWorldOrChunk ( cPlayer & a_Player )
{
{
2020-03-29 16:58:19 -04:00
cLock lock ( * this ) ;
auto * Chunk = a_Player . GetParentChunk ( ) ;
if ( Chunk & & Chunk - > HasEntity ( a_Player . GetUniqueID ( ) ) )
{
return true ;
}
2016-12-19 15:12:23 -05:00
}
{
cCSLock Lock ( m_CSPlayersToAdd ) ;
if ( std : : find_if (
m_PlayersToAdd . begin ( ) , m_PlayersToAdd . end ( ) ,
[ & a_Player ] ( const cAwaitingPlayerList : : value_type & Item ) { return Item . first . get ( ) = = & a_Player ; } ) ! = m_PlayersToAdd . end ( )
)
{
return true ;
}
}
{
2020-04-12 18:04:30 -04:00
cLock Lock ( * this ) ;
2016-12-19 15:12:23 -05:00
if ( std : : find ( m_Players . begin ( ) , m_Players . end ( ) , & a_Player ) ! = m_Players . end ( ) )
{
return true ;
}
}
{
cCSLock Lock ( m_CSEntitiesToAdd ) ;
if ( std : : find ( m_ClientsToAdd . begin ( ) , m_ClientsToAdd . end ( ) , a_Player . GetClientHandlePtr ( ) ) ! = m_ClientsToAdd . end ( ) )
{
return true ;
}
}
{
cCSLock Lock ( m_CSClients ) ;
if ( std : : find ( m_Clients . begin ( ) , m_Clients . end ( ) , a_Player . GetClientHandlePtr ( ) ) ! = m_Clients . end ( ) )
{
return true ;
}
}
// Assume OK if in ClientsToRemove or PlayersToRemove
return false ;
2012-06-14 09:06:06 -04:00
}
2016-12-19 15:12:23 -05:00
# endif
2012-06-14 09:06:06 -04:00
2017-09-11 17:20:49 -04:00
bool cWorld : : ForEachPlayer ( cPlayerListCallback a_Callback )
2012-06-14 09:06:06 -04:00
{
// Calls the callback for each player in the list
2020-04-12 18:04:30 -04:00
cLock Lock ( * this ) ;
2017-09-11 17:20:49 -04:00
for ( auto & Player : m_Players )
2012-06-14 09:06:06 -04:00
{
2017-09-11 17:20:49 -04:00
if ( Player - > IsTicking ( ) & & a_Callback ( * Player ) )
2012-06-14 09:06:06 -04:00
{
return false ;
}
} // for itr - m_Players[]
return true ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithPlayer ( const AString & a_PlayerName , cPlayerListCallback a_Callback )
2012-07-02 07:21:21 -04:00
{
2014-11-30 05:11:47 -05:00
// Calls the callback for the specified player in the list
2020-04-12 18:04:30 -04:00
cLock Lock ( * this ) ;
2017-09-11 17:20:49 -04:00
for ( auto & Player : m_Players )
2012-06-14 09:06:06 -04:00
{
2017-09-11 17:20:49 -04:00
if ( Player - > IsTicking ( ) & & ( NoCaseCompare ( Player - > GetName ( ) , a_PlayerName ) = = 0 ) )
2012-06-14 09:06:06 -04:00
{
2017-09-11 17:20:49 -04:00
a_Callback ( * Player ) ;
2012-07-02 07:21:21 -04:00
return true ;
2012-06-14 09:06:06 -04:00
}
2012-07-02 07:21:21 -04:00
} // for itr - m_Players[]
return false ;
2012-06-14 09:06:06 -04:00
}
2017-09-11 17:20:49 -04:00
bool cWorld : : FindAndDoWithPlayer ( const AString & a_PlayerNameHint , cPlayerListCallback a_Callback )
2012-08-22 19:05:12 -04:00
{
2014-10-20 16:55:07 -04:00
cPlayer * BestMatch = nullptr ;
2014-05-08 14:16:35 -04:00
size_t BestRating = 0 ;
size_t NameLength = a_PlayerNameHint . length ( ) ;
2012-08-22 19:05:12 -04:00
2020-04-12 18:04:30 -04:00
cLock Lock ( * this ) ;
2012-08-22 19:05:12 -04:00
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
2016-02-07 12:07:14 -05:00
if ( ! ( * itr ) - > IsTicking ( ) )
2016-02-01 15:49:34 -05:00
{
continue ;
}
2014-05-08 14:16:35 -04:00
size_t Rating = RateCompareString ( a_PlayerNameHint , ( * itr ) - > GetName ( ) ) ;
2013-02-01 14:59:58 -05:00
if ( Rating > = BestRating )
2012-08-22 19:05:12 -04:00
{
BestMatch = * itr ;
BestRating = Rating ;
}
2013-02-01 14:59:58 -05:00
if ( Rating = = NameLength ) // Perfect match
2012-08-22 19:05:12 -04:00
{
break ;
}
} // for itr - m_Players[]
2014-10-20 16:55:07 -04:00
if ( BestMatch ! = nullptr )
2012-08-22 19:05:12 -04:00
{
2017-09-11 17:20:49 -04:00
return a_Callback ( * BestMatch ) ;
2012-08-22 19:05:12 -04:00
}
return false ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithPlayerByUUID ( const cUUID & a_PlayerUUID , cPlayerListCallback a_Callback )
2014-11-02 15:01:23 -05:00
{
2020-04-12 18:04:30 -04:00
cLock Lock ( * this ) ;
2017-09-11 17:20:49 -04:00
for ( auto & Player : m_Players )
2014-11-02 15:01:23 -05:00
{
2017-09-11 17:20:49 -04:00
if ( Player - > IsTicking ( ) & & ( Player - > GetUUID ( ) = = a_PlayerUUID ) )
2016-02-01 15:49:34 -05:00
{
2017-09-11 17:20:49 -04:00
return a_Callback ( * Player ) ;
2014-11-02 15:01:23 -05:00
}
}
return false ;
}
2018-08-02 10:59:10 -04:00
bool cWorld : : DoWithNearestPlayer ( Vector3d a_Pos , double a_RangeLimit , cPlayerListCallback a_Callback , bool a_CheckLineOfSight , bool a_IgnoreSpectator )
2012-06-14 09:06:06 -04:00
{
2018-08-02 10:59:10 -04:00
double ClosestDistance = a_RangeLimit ;
2014-10-20 16:55:07 -04:00
cPlayer * ClosestPlayer = nullptr ;
2012-06-14 09:06:06 -04:00
2020-04-12 18:04:30 -04:00
cLock Lock ( * this ) ;
2012-06-14 09:06:06 -04:00
for ( cPlayerList : : const_iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
{
2016-02-07 12:07:14 -05:00
if ( ! ( * itr ) - > IsTicking ( ) )
2016-02-01 15:49:34 -05:00
{
continue ;
}
2018-08-02 10:59:10 -04:00
if ( a_IgnoreSpectator & & ( * itr ) - > IsGameModeSpectator ( ) )
{
continue ;
}
2012-06-14 09:06:06 -04:00
Vector3f Pos = ( * itr ) - > GetPosition ( ) ;
2014-03-14 09:36:44 -04:00
double Distance = ( Pos - a_Pos ) . Length ( ) ;
2012-06-14 09:06:06 -04:00
2017-05-11 08:34:36 -04:00
// If the player is too far, skip them:
if ( Distance > ClosestDistance )
2012-06-14 09:06:06 -04:00
{
2017-05-11 08:34:36 -04:00
continue ;
2012-06-14 09:06:06 -04:00
}
2017-05-11 08:34:36 -04:00
// Check LineOfSight, if requested:
if (
a_CheckLineOfSight & &
! cLineBlockTracer : : LineOfSightTrace ( * this , a_Pos , Pos , cLineBlockTracer : : losAirWater )
)
{
continue ;
}
ClosestDistance = Distance ;
ClosestPlayer = * itr ;
2012-06-14 09:06:06 -04:00
}
2018-08-02 10:59:10 -04:00
if ( ClosestPlayer )
{
return a_Callback ( * ClosestPlayer ) ;
}
else
{
return false ;
}
2012-06-14 09:06:06 -04:00
}
void cWorld : : SendPlayerList ( cPlayer * a_DestPlayer )
{
// Sends the playerlist to a_DestPlayer
2020-04-12 18:04:30 -04:00
cLock Lock ( * this ) ;
2012-08-19 15:42:32 -04:00
for ( cPlayerList : : iterator itr = m_Players . begin ( ) ; itr ! = m_Players . end ( ) ; + + itr )
2012-06-14 09:06:06 -04:00
{
cClientHandle * ch = ( * itr ) - > GetClientHandle ( ) ;
2014-10-20 16:55:07 -04:00
if ( ( ch ! = nullptr ) & & ! ch - > IsDestroyed ( ) )
2012-06-14 09:06:06 -04:00
{
2014-09-18 12:50:17 -04:00
a_DestPlayer - > GetClientHandle ( ) - > SendPlayerListAddPlayer ( * ( * itr ) ) ;
2012-06-14 09:06:06 -04:00
}
}
}
2017-09-11 17:20:49 -04:00
bool cWorld : : ForEachEntity ( cEntityCallback a_Callback )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > ForEachEntity ( a_Callback ) ;
2012-06-14 09:06:06 -04:00
}
2017-09-11 17:20:49 -04:00
bool cWorld : : ForEachEntityInChunk ( int a_ChunkX , int a_ChunkZ , cEntityCallback a_Callback )
2012-06-14 09:06:06 -04:00
{
2012-06-16 04:35:07 -04:00
return m_ChunkMap - > ForEachEntityInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
2012-06-14 09:06:06 -04:00
}
2017-09-11 17:20:49 -04:00
bool cWorld : : ForEachEntityInBox ( const cBoundingBox & a_Box , cEntityCallback a_Callback )
2014-09-03 11:00:26 -04:00
{
return m_ChunkMap - > ForEachEntityInBox ( a_Box , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : DoWithEntityByID ( UInt32 a_UniqueID , cEntityCallback a_Callback )
2012-06-14 09:06:06 -04:00
{
2015-03-18 10:35:19 -04:00
// First check the entities-to-add:
{
cCSLock Lock ( m_CSEntitiesToAdd ) ;
2016-12-19 15:12:23 -05:00
for ( const auto & ent : m_EntitiesToAdd )
2015-03-18 10:35:19 -04:00
{
if ( ent - > GetUniqueID ( ) = = a_UniqueID )
{
2017-09-11 17:20:49 -04:00
a_Callback ( * ent ) ;
2015-03-18 10:35:19 -04:00
return true ;
}
} // for ent - m_EntitiesToAdd[]
}
// Then check the chunkmap:
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > DoWithEntityByID ( a_UniqueID , a_Callback ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
void cWorld : : CompareChunkClients ( int a_ChunkX1 , int a_ChunkZ1 , int a_ChunkX2 , int a_ChunkZ2 , cClientDiffCallback & a_Callback )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkMap - > CompareChunkClients ( a_ChunkX1 , a_ChunkZ1 , a_ChunkX2 , a_ChunkZ2 , a_Callback ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
bool cWorld : : AddChunkClient ( int a_ChunkX , int a_ChunkZ , cClientHandle * a_Client )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > AddChunkClient ( a_ChunkX , a_ChunkZ , a_Client ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
void cWorld : : RemoveChunkClient ( int a_ChunkX , int a_ChunkZ , cClientHandle * a_Client )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
m_ChunkMap - > RemoveChunkClient ( a_ChunkX , a_ChunkZ , a_Client ) ;
2012-06-14 09:06:06 -04:00
}
void cWorld : : RemoveClientFromChunks ( cClientHandle * a_Client )
{
m_ChunkMap - > RemoveClientFromChunks ( a_Client ) ;
}
2014-10-06 15:27:53 -04:00
void cWorld : : SendChunkTo ( int a_ChunkX , int a_ChunkZ , cChunkSender : : eChunkPriority a_Priority , cClientHandle * a_Client )
2012-06-14 09:06:06 -04:00
{
2014-10-06 15:27:53 -04:00
m_ChunkSender . QueueSendChunkTo ( a_ChunkX , a_ChunkZ , a_Priority , a_Client ) ;
2012-06-14 09:06:06 -04:00
}
2014-10-06 15:27:53 -04:00
void cWorld : : ForceSendChunkTo ( int a_ChunkX , int a_ChunkZ , cChunkSender : : eChunkPriority a_Priority , cClientHandle * a_Client )
2014-02-18 07:06:18 -05:00
{
a_Client - > AddWantedChunk ( a_ChunkX , a_ChunkZ ) ;
2014-10-06 15:27:53 -04:00
m_ChunkSender . QueueSendChunkTo ( a_ChunkX , a_ChunkZ , a_Priority , a_Client ) ;
2014-02-18 07:06:18 -05:00
}
2012-06-14 09:06:06 -04:00
void cWorld : : RemoveClientFromChunkSender ( cClientHandle * a_Client )
{
m_ChunkSender . RemoveClient ( a_Client ) ;
}
2014-08-28 05:36:35 -04:00
void cWorld : : TouchChunk ( int a_ChunkX , int a_ChunkZ )
2012-06-14 09:06:06 -04:00
{
2014-08-28 05:36:35 -04:00
m_ChunkMap - > TouchChunk ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2015-05-30 06:11:17 -04:00
void cWorld : : PrepareChunk ( int a_ChunkX , int a_ChunkZ , std : : unique_ptr < cChunkCoordCallback > a_CallAfter )
2014-12-10 16:35:16 -05:00
{
2015-05-30 06:11:17 -04:00
m_ChunkMap - > PrepareChunk ( a_ChunkX , a_ChunkZ , std : : move ( a_CallAfter ) ) ;
2014-12-10 16:35:16 -05:00
}
2014-08-28 05:36:35 -04:00
void cWorld : : ChunkLoadFailed ( int a_ChunkX , int a_ChunkZ )
2012-06-14 09:06:06 -04:00
{
2014-08-28 05:36:35 -04:00
m_ChunkMap - > ChunkLoadFailed ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2013-06-12 03:14:06 -04:00
bool cWorld : : SetSignLines ( int a_BlockX , int a_BlockY , int a_BlockZ , const AString & a_Line1 , const AString & a_Line2 , const AString & a_Line3 , const AString & a_Line4 , cPlayer * a_Player )
2012-06-14 09:06:06 -04:00
{
2012-06-16 11:06:14 -04:00
AString Line1 ( a_Line1 ) ;
AString Line2 ( a_Line2 ) ;
AString Line3 ( a_Line3 ) ;
AString Line4 ( a_Line4 ) ;
2014-11-15 09:16:52 -05:00
2014-10-15 13:01:55 -04:00
if ( cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookUpdatingSign ( * this , a_BlockX , a_BlockY , a_BlockZ , Line1 , Line2 , Line3 , Line4 , a_Player ) )
2012-06-16 11:06:14 -04:00
{
2013-06-12 03:14:06 -04:00
return false ;
}
2014-11-15 09:16:52 -05:00
2013-06-12 03:14:06 -04:00
if ( m_ChunkMap - > SetSignLines ( a_BlockX , a_BlockY , a_BlockZ , Line1 , Line2 , Line3 , Line4 ) )
{
2014-10-15 13:01:55 -04:00
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookUpdatedSign ( * this , a_BlockX , a_BlockY , a_BlockZ , Line1 , Line2 , Line3 , Line4 , a_Player ) ;
2013-06-12 03:14:06 -04:00
return true ;
2012-06-16 11:06:14 -04:00
}
2013-06-12 03:14:06 -04:00
2014-11-15 09:16:52 -05:00
return false ;
2012-06-14 09:06:06 -04:00
}
2014-01-23 07:57:04 -05:00
bool cWorld : : SetCommandBlockCommand ( int a_BlockX , int a_BlockY , int a_BlockZ , const AString & a_Command )
{
2017-09-11 17:20:49 -04:00
return DoWithCommandBlockAt ( a_BlockX , a_BlockY , a_BlockZ , [ & ] ( cCommandBlockEntity & a_CommandBlock )
2014-01-23 07:57:04 -05:00
{
2017-09-11 17:20:49 -04:00
a_CommandBlock . SetCommand ( a_Command ) ;
2014-01-23 07:57:04 -05:00
return false ;
}
2017-09-11 17:20:49 -04:00
) ;
2014-01-23 07:57:04 -05:00
}
2014-03-02 10:01:37 -05:00
bool cWorld : : IsTrapdoorOpen ( int a_BlockX , int a_BlockY , int a_BlockZ )
{
2014-03-02 10:16:22 -05:00
BLOCKTYPE Block ;
NIBBLETYPE Meta ;
GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , Block , Meta ) ;
2014-09-30 14:04:49 -04:00
if ( ( Block ! = E_BLOCK_TRAPDOOR ) & & ( Block ! = E_BLOCK_IRON_TRAPDOOR ) )
2014-03-02 10:01:37 -05:00
{
return false ;
}
2016-01-12 08:04:59 -05:00
2014-03-02 10:01:37 -05:00
return ( Meta & 0x4 ) > 0 ;
}
bool cWorld : : SetTrapdoorOpen ( int a_BlockX , int a_BlockY , int a_BlockZ , bool a_Open )
{
2014-03-02 10:16:22 -05:00
BLOCKTYPE Block ;
NIBBLETYPE Meta ;
GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , Block , Meta ) ;
2014-09-30 14:04:49 -04:00
if ( ( Block ! = E_BLOCK_TRAPDOOR ) & & ( Block ! = E_BLOCK_IRON_TRAPDOOR ) )
2014-03-02 10:01:37 -05:00
{
return false ;
}
2016-01-12 08:04:59 -05:00
2015-04-25 19:38:41 -04:00
bool IsOpen = ( Meta & 0x4 ) ! = 0 ;
2014-03-02 10:01:37 -05:00
if ( a_Open ! = IsOpen )
{
SetBlockMeta ( a_BlockX , a_BlockY , a_BlockZ , Meta ^ 0x4 ) ;
2018-07-26 19:12:41 -04:00
BroadcastSoundParticleEffect ( EffectID : : SFX_RANDOM_WOODEN_TRAPDOOR_OPEN , { a_BlockX , a_BlockY , a_BlockZ } , 0 ) ;
2014-03-02 10:01:37 -05:00
return true ;
}
return false ;
}
2012-06-14 09:06:06 -04:00
void cWorld : : RegenerateChunk ( int a_ChunkX , int a_ChunkZ )
{
m_ChunkMap - > MarkChunkRegenerating ( a_ChunkX , a_ChunkZ ) ;
2016-01-12 08:04:59 -05:00
2019-09-01 03:30:00 -04:00
m_Generator . QueueGenerateChunk ( { a_ChunkX , a_ChunkZ } , true ) ;
2012-06-14 09:06:06 -04:00
}
void cWorld : : GenerateChunk ( int a_ChunkX , int a_ChunkZ )
{
2014-12-10 16:35:16 -05:00
m_ChunkMap - > GenerateChunk ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2015-05-30 06:11:17 -04:00
void cWorld : : QueueLightChunk ( int a_ChunkX , int a_ChunkZ , std : : unique_ptr < cChunkCoordCallback > a_Callback )
2012-06-14 09:06:06 -04:00
{
2015-05-30 06:11:17 -04:00
m_Lighting . QueueChunk ( a_ChunkX , a_ChunkZ , std : : move ( a_Callback ) ) ;
2012-06-14 09:06:06 -04:00
}
bool cWorld : : IsChunkLighted ( int a_ChunkX , int a_ChunkZ )
{
return m_ChunkMap - > IsChunkLighted ( a_ChunkX , a_ChunkZ ) ;
}
2012-07-02 12:30:17 -04:00
bool cWorld : : ForEachChunkInRect ( int a_MinChunkX , int a_MaxChunkX , int a_MinChunkZ , int a_MaxChunkZ , cChunkDataCallback & a_Callback )
{
return m_ChunkMap - > ForEachChunkInRect ( a_MinChunkX , a_MaxChunkX , a_MinChunkZ , a_MaxChunkZ , a_Callback ) ;
}
2017-09-11 17:20:49 -04:00
bool cWorld : : ForEachLoadedChunk ( cFunctionRef < bool ( int , int ) > a_Callback )
2015-09-24 10:43:31 -04:00
{
return m_ChunkMap - > ForEachLoadedChunk ( a_Callback ) ;
}
2012-06-14 09:06:06 -04:00
void cWorld : : SaveAllChunks ( void )
{
2017-09-07 08:41:16 -04:00
if ( IsSavingEnabled ( ) )
{
m_LastSave = std : : chrono : : duration_cast < cTickTimeLong > ( m_WorldAge ) ;
m_ChunkMap - > SaveAllChunks ( ) ;
}
2012-06-14 09:06:06 -04:00
}
2013-08-11 15:05:44 -04:00
void cWorld : : QueueSaveAllChunks ( void )
{
2015-09-25 13:56:49 -04:00
QueueTask ( [ ] ( cWorld & a_World ) { a_World . SaveAllChunks ( ) ; } ) ;
2013-08-11 15:05:44 -04:00
}
2015-09-25 13:56:49 -04:00
void cWorld : : QueueTask ( std : : function < void ( cWorld & ) > a_Task )
2013-08-11 15:05:44 -04:00
{
cCSLock Lock ( m_CSTasks ) ;
2020-05-14 18:15:35 -04:00
m_Tasks . emplace_back ( 0 , std : : move ( a_Task ) ) ;
2013-08-11 15:05:44 -04:00
}
2014-01-19 17:49:19 -05:00
2015-09-25 13:56:49 -04:00
void cWorld : : ScheduleTask ( int a_DelayTicks , std : : function < void ( cWorld & ) > a_Task )
2014-01-14 15:17:03 -05:00
{
2015-01-11 16:12:26 -05:00
Int64 TargetTick = a_DelayTicks + std : : chrono : : duration_cast < cTickTimeLong > ( m_WorldAge ) . count ( ) ;
2015-09-25 13:56:49 -04:00
// Insert the task into the list of scheduled tasks
2014-01-14 15:17:03 -05:00
{
2015-09-25 13:56:49 -04:00
cCSLock Lock ( m_CSTasks ) ;
2020-05-14 18:15:35 -04:00
m_Tasks . emplace_back ( TargetTick , std : : move ( a_Task ) ) ;
2014-01-14 15:17:03 -05:00
}
}
2013-08-11 15:05:44 -04:00
2014-06-06 16:31:16 -04:00
2016-12-19 15:12:23 -05:00
void cWorld : : AddEntity ( OwnedEntity a_Entity )
2012-06-14 09:06:06 -04:00
{
2014-06-06 16:31:16 -04:00
cCSLock Lock ( m_CSEntitiesToAdd ) ;
2016-12-19 15:12:23 -05:00
m_EntitiesToAdd . emplace_back ( std : : move ( a_Entity ) ) ;
2013-04-13 17:02:10 -04:00
}
2015-03-21 10:18:17 -04:00
bool cWorld : : HasEntity ( UInt32 a_UniqueID )
2013-04-13 17:02:10 -04:00
{
2014-06-06 16:31:16 -04: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 10:14:27 -05:00
if ( m_ChunkMap . get ( ) = = nullptr )
{
// Chunkmap has already been destroyed, there are no entities anymore.
return false ;
}
2013-04-13 17:02:10 -04:00
return m_ChunkMap - > HasEntity ( a_UniqueID ) ;
2012-06-14 09:06:06 -04:00
}
2017-09-05 10:11:35 -04:00
OwnedEntity cWorld : : RemoveEntity ( cEntity & a_Entity )
{
// Check if the entity is in the chunkmap:
auto Entity = m_ChunkMap - > RemoveEntity ( a_Entity ) ;
if ( Entity ! = nullptr )
{
2020-03-05 05:52:34 -05:00
Entity - > OnRemoveFromWorld ( * this ) ;
2017-09-05 10:11:35 -04:00
return Entity ;
}
// Check if the entity is in the queue to be added to the world:
cCSLock Lock ( m_CSEntitiesToAdd ) ;
auto itr = std : : find_if ( m_EntitiesToAdd . begin ( ) , m_EntitiesToAdd . end ( ) ,
[ & a_Entity ] ( const OwnedEntity & a_OwnedEntity )
{
return ( a_OwnedEntity . get ( ) = = & a_Entity ) ;
}
) ;
if ( itr ! = m_EntitiesToAdd . end ( ) )
{
Entity = std : : move ( * itr ) ;
m_EntitiesToAdd . erase ( itr ) ;
}
return Entity ;
}
2016-09-03 11:38:29 -04:00
size_t cWorld : : GetNumChunks ( void ) const
2012-06-14 09:06:06 -04:00
{
return m_ChunkMap - > GetNumChunks ( ) ;
}
2016-09-03 11:38:29 -04:00
size_t cWorld : : GetNumUnusedDirtyChunks ( void ) const
{
return m_ChunkMap - > GetNumUnusedDirtyChunks ( ) ;
}
2012-06-14 09:06:06 -04:00
void cWorld : : GetChunkStats ( int & a_NumValid , int & a_NumDirty , int & a_NumInLightingQueue )
{
m_ChunkMap - > GetChunkStats ( a_NumValid , a_NumDirty ) ;
2015-07-29 11:04:03 -04:00
a_NumInLightingQueue = static_cast < int > ( m_Lighting . GetQueueLength ( ) ) ;
2012-06-14 09:06:06 -04:00
}
2012-10-13 04:56:12 -04:00
2013-09-16 03:25:23 -04:00
void cWorld : : TickQueuedBlocks ( void )
2012-07-15 16:36:34 -04:00
{
2012-10-13 04:56:12 -04:00
if ( m_BlockTickQueue . empty ( ) )
{
2012-07-15 16:36:34 -04:00
return ;
2012-10-13 04:56:12 -04:00
}
2012-07-15 16:36:34 -04:00
m_BlockTickQueueCopy . clear ( ) ;
m_BlockTickQueue . swap ( m_BlockTickQueueCopy ) ;
2014-04-18 15:09:44 -04:00
for ( std : : vector < BlockTickQueueItem * > : : iterator itr = m_BlockTickQueueCopy . begin ( ) ; itr ! = m_BlockTickQueueCopy . end ( ) ; + + itr )
2012-07-15 16:36:34 -04:00
{
2013-11-30 09:58:27 -05:00
BlockTickQueueItem * Block = ( * itr ) ;
2013-09-16 03:25:23 -04:00
Block - > TicksToWait - = 1 ;
if ( Block - > TicksToWait < = 0 )
2012-07-15 16:36:34 -04:00
{
2013-09-16 03:25:23 -04:00
// TODO: Handle the case when the chunk is already unloaded
2020-04-17 05:36:37 -04:00
m_ChunkMap - > TickBlock ( { Block - > X , Block - > Y , Block - > Z } ) ;
2014-07-17 16:15:34 -04:00
delete Block ; // We don't have to remove it from the vector, this will happen automatically on the next tick
2012-10-13 04:56:12 -04:00
}
else
{
2014-07-17 10:33:09 -04:00
m_BlockTickQueue . push_back ( Block ) ; // Keep the block in the queue
2012-07-15 16:36:34 -04:00
}
2012-10-13 04:56:12 -04:00
} // for itr - m_BlockTickQueueCopy[]
2012-07-15 16:36:34 -04:00
}
2012-10-13 04:56:12 -04:00
2013-09-16 03:25:23 -04:00
void cWorld : : QueueBlockForTick ( int a_BlockX , int a_BlockY , int a_BlockZ , int a_TicksToWait )
2012-07-15 16:36:34 -04:00
{
2012-10-13 04:56:12 -04:00
BlockTickQueueItem * Block = new BlockTickQueueItem ;
Block - > X = a_BlockX ;
Block - > Y = a_BlockY ;
Block - > Z = a_BlockZ ;
2013-09-16 03:25:23 -04:00
Block - > TicksToWait = a_TicksToWait ;
2016-01-12 08:04:59 -05:00
2012-07-15 16:36:34 -04:00
m_BlockTickQueue . push_back ( Block ) ;
}
2012-10-13 04:56:12 -04:00
bool cWorld : : IsBlockDirectlyWatered ( int a_BlockX , int a_BlockY , int a_BlockZ )
2012-07-15 16:36:34 -04:00
{
2012-10-13 04:56:12 -04:00
return (
IsBlockWater ( GetBlock ( a_BlockX - 1 , a_BlockY , a_BlockZ ) ) | |
IsBlockWater ( GetBlock ( a_BlockX + 1 , a_BlockY , a_BlockZ ) ) | |
IsBlockWater ( GetBlock ( a_BlockX , a_BlockY , a_BlockZ - 1 ) ) | |
IsBlockWater ( GetBlock ( a_BlockX , a_BlockY , a_BlockZ + 1 ) )
) ;
}
2015-07-16 09:06:54 -04:00
UInt32 cWorld : : SpawnMob ( double a_PosX , double a_PosY , double a_PosZ , eMonsterType a_MonsterType , bool a_Baby )
2012-10-28 10:57:35 -04:00
{
2016-12-19 15:12:23 -05:00
auto Monster = cMonster : : NewMonsterFromType ( a_MonsterType ) ;
2015-03-21 10:18:17 -04:00
if ( Monster = = nullptr )
2013-10-20 07:25:56 -04:00
{
2015-03-21 10:18:17 -04:00
return cEntity : : INVALID_ID ;
2013-10-20 07:25:56 -04:00
}
2015-03-21 10:18:17 -04:00
Monster - > SetPosition ( a_PosX , a_PosY , a_PosZ ) ;
2016-01-12 08:04:59 -05:00
2015-07-16 09:06:54 -04:00
if ( a_Baby )
{
Monster - > SetAge ( - 1 ) ;
}
2016-12-19 15:12:23 -05:00
return SpawnMobFinalize ( std : : move ( Monster ) ) ;
2013-09-07 20:47:02 -04:00
}
2018-07-26 17:24:36 -04:00
2016-12-19 15:12:23 -05:00
UInt32 cWorld : : SpawnMobFinalize ( std : : unique_ptr < cMonster > a_Monster )
2013-09-07 20:47:02 -04:00
{
2015-03-21 10:18:17 -04:00
ASSERT ( a_Monster ! = nullptr ) ;
2014-07-24 06:15:48 -04:00
// Give the mob full health.
2013-09-07 20:47:02 -04:00
a_Monster - > SetHealth ( a_Monster - > GetMaxHealth ( ) ) ;
2014-07-24 06:15:48 -04:00
// A plugin doesn't agree with the spawn. bail out.
2013-09-07 20:47:02 -04:00
if ( cPluginManager : : Get ( ) - > CallHookSpawningMonster ( * this , * a_Monster ) )
2013-08-08 03:13:13 -04:00
{
2015-03-21 10:18:17 -04:00
return cEntity : : INVALID_ID ;
2013-08-08 03:13:13 -04:00
}
2014-07-24 06:15:48 -04:00
2016-12-19 15:12:23 -05:00
auto & Monster = * a_Monster ;
2014-07-24 06:15:48 -04:00
// Initialize the monster into the current world.
2016-12-19 15:12:23 -05:00
if ( ! Monster . Initialize ( std : : move ( a_Monster ) , * this ) )
2013-08-08 03:13:13 -04:00
{
2015-03-21 10:18:17 -04:00
return cEntity : : INVALID_ID ;
2013-08-08 03:13:13 -04:00
}
2014-07-24 06:15:48 -04:00
2016-12-19 15:12:23 -05:00
cPluginManager : : Get ( ) - > CallHookSpawnedMonster ( * this , Monster ) ;
2013-10-08 14:20:49 -04:00
2016-12-19 15:12:23 -05:00
return Monster . GetUniqueID ( ) ;
2012-10-28 10:57:35 -04:00
}
2019-09-29 08:59:24 -04:00
UInt32 cWorld : : CreateProjectile ( Vector3d a_Pos , cProjectileEntity : : eKind a_Kind , cEntity * a_Creator , const cItem * a_Item , const Vector3d * a_Speed )
2013-08-22 02:55:58 -04:00
{
2019-09-29 08:59:24 -04:00
auto Projectile = cProjectileEntity : : Create ( a_Kind , a_Creator , a_Pos , a_Item , a_Speed ) ;
2014-10-20 16:55:07 -04:00
if ( Projectile = = nullptr )
2013-08-22 02:55:58 -04:00
{
2015-03-21 10:18:17 -04:00
return cEntity : : INVALID_ID ;
2013-08-22 02:55:58 -04:00
}
2016-12-19 15:12:23 -05:00
auto ProjectilePtr = Projectile . get ( ) ;
if ( ! ProjectilePtr - > Initialize ( std : : move ( Projectile ) , * this ) )
2013-08-22 02:55:58 -04:00
{
2015-03-21 10:18:17 -04:00
return cEntity : : INVALID_ID ;
2013-08-22 02:55:58 -04:00
}
2016-12-19 15:12:23 -05:00
return ProjectilePtr - > GetUniqueID ( ) ;
2013-08-22 02:55:58 -04:00
}
2019-09-29 08:59:24 -04: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 )
{
return CreateProjectile ( { a_PosX , a_PosY , a_PosZ } , a_Kind , a_Creator , a_Item , a_Speed ) ;
}
2017-06-13 15:35:30 -04:00
int cWorld : : GetTickRandomNumber ( int a_Range )
{
return GetRandomProvider ( ) . RandInt ( a_Range ) ;
}
2013-07-30 16:48:59 -04:00
void cWorld : : TabCompleteUserName ( const AString & a_Text , AStringVector & a_Results )
{
2014-08-01 17:15:14 -04: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 ;
2020-04-12 18:04:30 -04:00
cLock Lock ( * this ) ;
2013-07-31 05:16:11 -04:00
for ( cPlayerList : : iterator itr = m_Players . begin ( ) , end = m_Players . end ( ) ; itr ! = end ; + + itr )
{
2014-03-01 16:25:27 -05:00
AString PlayerName ( ( * itr ) - > GetName ( ) ) ;
2014-09-02 13:12:35 -04:00
if ( ( * itr ) - > HasCustomName ( ) )
{
PlayerName = ( * itr ) - > GetCustomName ( ) ;
}
2016-02-05 15:27:31 -05:00
AString : : size_type Found = StrToLower ( PlayerName ) . find ( StrToLower ( LastWord ) ) ; // Try to find last word in playername
2014-03-01 16:25:27 -05:00
if ( Found = = AString : : npos )
2013-07-31 05:16:11 -04:00
{
2014-07-17 13:13:23 -04:00
continue ; // No match
2013-07-31 05:16:11 -04:00
}
2014-09-02 13:12:35 -04:00
2014-08-01 17:15:14 -04: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 05:16:11 -04:00
}
2013-07-30 16:48:59 -04:00
}
2014-07-10 12:18:32 -04: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 16:54:00 -04:00
cRedstoneSimulator * cWorld : : InitializeRedstoneSimulator ( cIniFile & a_IniFile )
2014-02-07 16:13:55 -05:00
{
2014-07-31 17:11:51 -04:00
AString SimulatorName = a_IniFile . GetValueSet ( " Physics " , " RedstoneSimulator " , " Incremental " ) ;
2014-02-07 16:13:55 -05:00
if ( SimulatorName . empty ( ) )
{
2014-07-31 17:11:51 -04:00
LOGWARNING ( " [Physics] RedstoneSimulator not present or empty in %s, using the default of \" Incremental \" . " , GetIniFileName ( ) . c_str ( ) ) ;
SimulatorName = " Incremental " ;
2014-02-07 16:13:55 -05:00
}
2016-01-12 08:04:59 -05:00
2014-10-25 16:54:00 -04:00
cRedstoneSimulator * res = nullptr ;
2014-02-07 16:13:55 -05:00
2014-07-31 17:11:51 -04:00
if ( NoCaseCompare ( SimulatorName , " Incremental " ) = = 0 )
2014-02-07 16:13:55 -05:00
{
2014-10-25 16:54:00 -04:00
res = new cIncrementalRedstoneSimulator ( * this ) ;
2014-02-07 16:13:55 -05:00
}
2014-02-07 16:59:08 -05:00
else if ( NoCaseCompare ( SimulatorName , " noop " ) = = 0 )
2014-02-07 16:13:55 -05:00
{
res = new cRedstoneNoopSimulator ( * this ) ;
}
2016-12-28 18:41:20 -05:00
else
{
LOGWARNING ( " [Physics] Unknown RedstoneSimulator \" %s \" in %s, using the default of \" Incremental \" . " , SimulatorName . c_str ( ) , GetIniFileName ( ) . c_str ( ) ) ;
res = new cIncrementalRedstoneSimulator ( * this ) ;
}
2016-01-12 08:04:59 -05:00
2015-06-26 18:24:51 -04:00
m_SimulatorManager - > RegisterSimulator ( res , 2 /* Two game ticks is a redstone tick */ ) ;
2016-01-12 08:04:59 -05:00
2014-02-07 16:13:55 -05:00
return res ;
}
2012-10-13 05:53:28 -04:00
cFluidSimulator * cWorld : : InitializeFluidSimulator ( cIniFile & a_IniFile , const char * a_FluidName , BLOCKTYPE a_SimulateBlock , BLOCKTYPE a_StationaryBlock )
{
AString SimulatorNameKey ;
Printf ( SimulatorNameKey , " %sSimulator " , a_FluidName ) ;
AString SimulatorSectionName ;
Printf ( SimulatorSectionName , " %sSimulator " , a_FluidName ) ;
2017-06-19 16:28:01 -04:00
bool IsWater = ( strcmp ( a_FluidName , " Water " ) = = 0 ) ; // Used for defaults
AString DefaultSimulatorName = ( ( GetDimension ( ) = = dimNether ) & & IsWater ) ? " Vaporise " : " Vanilla " ;
AString SimulatorName = a_IniFile . GetValueSet ( " Physics " , SimulatorNameKey , DefaultSimulatorName ) ;
2012-10-13 05:53:28 -04:00
if ( SimulatorName . empty ( ) )
{
2017-06-19 16:28:01 -04:00
LOGWARNING ( " [Physics] %s not present or empty in %s, using the default of \" %s \" . " , SimulatorNameKey . c_str ( ) , GetIniFileName ( ) . c_str ( ) , DefaultSimulatorName . c_str ( ) ) ;
SimulatorName = DefaultSimulatorName ;
2012-10-13 05:53:28 -04:00
}
2014-10-20 16:55:07 -04:00
cFluidSimulator * res = nullptr ;
2012-10-15 16:16:43 -04:00
int Rate = 1 ;
2013-03-14 16:46:40 -04:00
if (
2013-03-14 15:44:27 -04:00
( NoCaseCompare ( SimulatorName , " vaporize " ) = = 0 ) | |
( NoCaseCompare ( SimulatorName , " vaporise " ) = = 0 )
)
{
res = new cVaporizeFluidSimulator ( * this , a_SimulateBlock , a_StationaryBlock ) ;
}
2013-03-14 16:03:42 -04:00
else if (
( NoCaseCompare ( SimulatorName , " noop " ) = = 0 ) | |
( NoCaseCompare ( SimulatorName , " nop " ) = = 0 ) | |
( NoCaseCompare ( SimulatorName , " null " ) = = 0 ) | |
( NoCaseCompare ( SimulatorName , " nil " ) = = 0 )
)
{
res = new cNoopFluidSimulator ( * this , a_SimulateBlock , a_StationaryBlock ) ;
}
2012-10-13 05:53:28 -04:00
else
{
2013-03-14 16:46:40 -04:00
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 08:04:59 -05:00
2014-09-08 13:56:27 -04: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 07:18:20 -04:00
Falloff = IsWater ? 1 : 2 ;
2014-09-08 13:56:27 -04:00
}
2014-03-05 08:54:38 -05:00
if ( NoCaseCompare ( SimulatorName , " floody " ) = = 0 )
{
2014-09-08 13:56:27 -04:00
res = new cFloodyFluidSimulator ( * this , a_SimulateBlock , a_StationaryBlock , static_cast < NIBBLETYPE > ( Falloff ) , TickDelay , NumNeighborsForSource ) ;
2014-03-05 08:54:38 -05:00
}
else if ( NoCaseCompare ( SimulatorName , " vanilla " ) = = 0 )
{
2014-09-08 13:56:27 -04:00
res = new cVanillaFluidSimulator ( * this , a_SimulateBlock , a_StationaryBlock , static_cast < NIBBLETYPE > ( Falloff ) , TickDelay , NumNeighborsForSource ) ;
2014-03-05 08:54:38 -05: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 13:56:27 -04:00
res = new cVanillaFluidSimulator ( * this , a_SimulateBlock , a_StationaryBlock , static_cast < NIBBLETYPE > ( Falloff ) , TickDelay , NumNeighborsForSource ) ;
2014-03-05 08:54:38 -05:00
}
2012-10-13 05:53:28 -04:00
}
2016-01-12 08:04:59 -05:00
2012-10-15 16:16:43 -04:00
m_SimulatorManager - > RegisterSimulator ( res , Rate ) ;
2012-10-13 05:53:28 -04:00
return res ;
}
2013-08-11 15:05:44 -04:00
2014-02-11 14:38:28 -05:00
2014-06-08 15:58:08 -04:00
void cWorld : : AddQueuedPlayers ( void )
{
ASSERT ( m_TickThread . IsCurrentThread ( ) ) ;
// Grab the list of players to add, it has to be locked to access it:
2016-08-22 13:43:43 -04:00
cAwaitingPlayerList PlayersToAdd ;
2014-06-08 15:58:08 -04:00
{
cCSLock Lock ( m_CSPlayersToAdd ) ;
std : : swap ( PlayersToAdd , m_PlayersToAdd ) ;
}
2016-01-12 08:04:59 -05:00
2016-12-19 15:12:23 -05:00
// Temporary (#3115-will-fix): store pointers to player objects after ownership transferral
std : : vector < std : : pair < cPlayer * , cWorld * > > AddedPlayerPtrs ;
AddedPlayerPtrs . reserve ( PlayersToAdd . size ( ) ) ;
2014-06-08 15:58:08 -04:00
// Add all the players in the grabbed list:
{
2020-04-12 18:04:30 -04:00
cLock Lock ( * this ) ;
2016-08-22 13:43:43 -04:00
for ( auto & AwaitingPlayer : PlayersToAdd )
2014-06-08 15:58:08 -04:00
{
2016-08-22 13:43:43 -04:00
auto & Player = AwaitingPlayer . first ;
2016-12-19 15:12:23 -05:00
ASSERT ( std : : find ( m_Players . begin ( ) , m_Players . end ( ) , Player . get ( ) ) = = m_Players . end ( ) ) ; // Is it already in the list? HOW?
2016-03-29 14:23:53 -04:00
LOGD ( " Adding player %s to world \" %s \" . " , Player - > GetName ( ) . c_str ( ) , m_WorldName . c_str ( ) ) ;
2014-07-22 12:26:48 -04:00
2016-12-19 15:12:23 -05:00
m_Players . push_back ( Player . get ( ) ) ;
2016-03-29 14:23:53 -04:00
Player - > SetWorld ( this ) ;
2014-06-08 15:58:08 -04:00
// Add to chunkmap, if not already there (Spawn vs MoveToWorld):
2016-12-19 15:12:23 -05:00
auto PlayerPtr = Player . get ( ) ;
m_ChunkMap - > AddEntityIfNotPresent ( std : : move ( Player ) ) ;
2020-03-05 05:52:34 -05:00
PlayerPtr - > OnAddToWorld ( * this ) ;
2016-12-19 15:12:23 -05:00
ASSERT ( ! PlayerPtr - > IsTicking ( ) ) ;
PlayerPtr - > SetIsTicking ( true ) ;
AddedPlayerPtrs . emplace_back ( PlayerPtr , AwaitingPlayer . second ) ;
2014-06-08 15:58:08 -04:00
} // for itr - PlayersToAdd[]
2020-04-12 18:04:30 -04:00
} // cLock(*this)
2014-06-08 15:58:08 -04:00
// Add all the players' clienthandles:
{
cCSLock Lock ( m_CSClients ) ;
2016-12-19 15:12:23 -05:00
for ( auto & AwaitingPlayer : AddedPlayerPtrs )
2014-06-08 15:58:08 -04:00
{
2016-08-22 13:43:43 -04:00
auto & Player = AwaitingPlayer . first ;
cClientHandlePtr Client = Player - > GetClientHandlePtr ( ) ;
2014-10-20 16:55:07 -04:00
if ( Client ! = nullptr )
2014-06-08 15:58:08 -04:00
{
m_Clients . push_back ( Client ) ;
}
} // for itr - PlayersToAdd[]
} // Lock(m_CSClients)
// Stream chunks to all eligible clients:
2016-12-19 15:12:23 -05:00
for ( auto & AwaitingPlayer : AddedPlayerPtrs )
2014-06-08 15:58:08 -04:00
{
2016-08-22 13:43:43 -04:00
auto & Player = AwaitingPlayer . first ;
cClientHandle * Client = Player - > GetClientHandle ( ) ;
2014-10-20 16:55:07 -04:00
if ( Client ! = nullptr )
2014-06-08 15:58:08 -04:00
{
2014-06-12 10:21:07 -04:00
Client - > SendPlayerMoveLook ( ) ;
Client - > SendHealth ( ) ;
2016-08-22 13:43:43 -04:00
Client - > SendWholeInventory ( * Player - > GetWindow ( ) ) ;
2020-04-07 17:23:54 -04:00
// Send resource pack
auto ResourcePackUrl = cRoot : : Get ( ) - > GetServer ( ) - > GetResourcePackUrl ( ) ;
if ( ! ResourcePackUrl . empty ( ) )
{
Client - > SendResourcePack ( ResourcePackUrl ) ;
}
2014-06-08 15:58:08 -04:00
}
} // for itr - PlayersToAdd[]
2016-08-22 13:43:43 -04:00
// Call EntityChangedWorld callback on all eligible clients
2016-12-19 15:12:23 -05:00
for ( auto & AwaitingPlayer : AddedPlayerPtrs )
2016-08-22 13:43:43 -04:00
{
if ( AwaitingPlayer . second ! = nullptr )
{
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookEntityChangedWorld ( * ( static_cast < cEntity * > ( AwaitingPlayer . first ) ) , * AwaitingPlayer . second ) ;
}
}
2014-06-08 15:58:08 -04:00
}
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2014-01-10 16:22:54 -05: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 ) ;
2016-12-19 15:12:23 -05:00
auto SetChunkData = cpp14 : : make_unique < cSetChunkData > (
2014-01-10 16:22:54 -05:00
a_ChunkDesc . GetChunkX ( ) , a_ChunkDesc . GetChunkZ ( ) ,
a_ChunkDesc . GetBlockTypes ( ) , BlockMetas ,
2014-10-20 16:55:07 -04:00
nullptr , nullptr , // We don't have lighting, chunk will be lighted when needed
2014-01-10 16:22:54 -05:00
& a_ChunkDesc . GetHeightMap ( ) , & a_ChunkDesc . GetBiomeMap ( ) ,
2015-04-27 15:18:21 -04:00
std : : move ( a_ChunkDesc . GetEntities ( ) ) , std : : move ( a_ChunkDesc . GetBlockEntities ( ) ) ,
2014-01-10 16:22:54 -05:00
true
2016-12-19 15:12:23 -05:00
) ;
2014-08-29 12:19:27 -04:00
SetChunkData - > RemoveInvalidBlockEntities ( ) ;
2017-07-20 07:19:18 -04:00
m_World - > QueueSetChunkData ( std : : move ( SetChunkData ) ) ;
2014-01-10 16:22:54 -05:00
}
2019-09-01 03:30:00 -04:00
bool cWorld : : cChunkGeneratorCallbacks : : IsChunkValid ( cChunkCoords a_Coords )
2014-01-10 16:22:54 -05:00
{
2019-09-01 03:30:00 -04:00
return m_World - > IsChunkValid ( a_Coords . m_ChunkX , a_Coords . m_ChunkZ ) ;
2014-01-10 16:22:54 -05:00
}
2019-09-01 03:30:00 -04:00
bool cWorld : : cChunkGeneratorCallbacks : : IsChunkQueued ( cChunkCoords a_Coords )
2014-09-05 16:16:48 -04:00
{
2019-09-01 03:30:00 -04:00
return m_World - > IsChunkQueued ( a_Coords . m_ChunkX , a_Coords . m_ChunkZ ) ;
2014-09-05 16:16:48 -04:00
}
2019-09-01 03:30:00 -04:00
bool cWorld : : cChunkGeneratorCallbacks : : HasChunkAnyClients ( cChunkCoords a_Coords )
2014-01-10 16:22:54 -05:00
{
2019-09-01 03:30:00 -04:00
return m_World - > HasChunkAnyClients ( a_Coords . m_ChunkX , a_Coords . m_ChunkZ ) ;
2014-01-10 16:22:54 -05:00
}
void cWorld : : cChunkGeneratorCallbacks : : CallHookChunkGenerating ( cChunkDesc & a_ChunkDesc )
{
cPluginManager : : Get ( ) - > CallHookChunkGenerating (
2014-10-15 13:01:55 -04:00
* m_World , a_ChunkDesc . GetChunkX ( ) , a_ChunkDesc . GetChunkZ ( ) , & a_ChunkDesc
2014-01-10 16:22:54 -05:00
) ;
}
void cWorld : : cChunkGeneratorCallbacks : : CallHookChunkGenerated ( cChunkDesc & a_ChunkDesc )
{
cPluginManager : : Get ( ) - > CallHookChunkGenerated (
2014-10-15 13:01:55 -04:00
* m_World , a_ChunkDesc . GetChunkX ( ) , a_ChunkDesc . GetChunkZ ( ) , & a_ChunkDesc
2014-01-10 16:22:54 -05:00
) ;
}