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"
2020-09-12 14:57:44 -04:00
# include "Physics/Explodinator.h"
2012-09-23 18:09:57 -04:00
# 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):
2020-10-29 16:47:20 -04:00
# include "Entities/EnderCrystal.h"
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"
2021-03-28 09:40:57 -04:00
# include "BlockEntities/SignEntity.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
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:
2021-03-17 19:18:02 -04:00
cWorld : : cLock : : cLock ( const cWorld & a_World ) :
2020-12-21 09:31:44 -05: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 ) :
2021-03-28 08:34:57 -04:00
Super ( Printf ( " World Ticker (%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 ( ) ;
2021-04-04 20:38:43 -04:00
auto TickTime = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( 1 _tick ) ;
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
2021-04-04 20:38:43 -04:00
if ( TickTime < 1 _tick )
2013-03-11 13:15:34 -04:00
{
2021-04-04 20:38:43 -04:00
// Stretch tick time until it's at least 1 tick:
std : : this_thread : : sleep_for ( 1 _tick - 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 ) ,
2021-03-20 16:58:19 -04:00
m_WorldDate ( 0 ) ,
2021-03-17 19:18:02 -04:00
m_WorldTickAge ( 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 ) ,
2020-12-21 09:31:44 -05:00
m_ChunkMap ( this ) ,
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-12-21 09:31:44 -05:00
m_ChunkMap . TrackInDeadlockDetect ( a_DeadlockDetect , m_WorldName ) ;
2020-04-12 18:04:30 -04:00
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:
2021-01-05 19:35:42 -05:00
a_DeadlockDetect . TrackCriticalSection ( m_CSTasks , Printf ( " World %s tasks " , m_WorldName . c_str ( ) ) ) ;
2017-09-22 11:55:42 -04:00
// 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 ) ;
2020-12-23 17:39:53 -05:00
SetMaxViewDistance ( IniFile . GetValueSetI ( " SpawnPosition " , " MaxViewDistance " , cClientHandle : : DEFAULT_VIEW_DISTANCE ) ) ;
2017-09-22 11:55:42 -04:00
// 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 ) ;
2021-04-04 20:38:43 -04:00
m_WorldDate = cTickTime ( IniFile . GetValueSetI ( " General " , " TimeInTicks " , GetWorldDate ( ) . count ( ) ) ) ;
2017-09-22 11:55:42 -04:00
// preallocate some memory for ticking blocks so we don't need to allocate that often
m_BlockTickQueue . reserve ( 1000 ) ;
m_BlockTickQueueCopy . reserve ( 1000 ) ;
// Simulators:
2020-08-01 14:18:03 -04:00
m_SimulatorManager = std : : make_unique < cSimulatorManager > ( * this ) ;
2017-09-22 11:55:42 -04:00
m_WaterSimulator = InitializeFluidSimulator ( IniFile , " Water " , E_BLOCK_WATER , E_BLOCK_STATIONARY_WATER ) ;
m_LavaSimulator = InitializeFluidSimulator ( IniFile , " Lava " , E_BLOCK_LAVA , E_BLOCK_STATIONARY_LAVA ) ;
2020-08-01 14:18:03 -04:00
m_SandSimulator = std : : make_unique < cSandSimulator > ( * this , IniFile ) ;
m_FireSimulator = std : : make_unique < cFireSimulator > ( * this , IniFile ) ;
2017-09-22 11:55:42 -04:00
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)
2021-04-04 20:38:43 -04:00
m_LastSpawnMonster . emplace ( cMonster : : mfHostile , 0 _tick ) ;
m_LastSpawnMonster . emplace ( cMonster : : mfPassive , 0 _tick ) ;
m_LastSpawnMonster . emplace ( cMonster : : mfAmbient , 0 _tick ) ;
m_LastSpawnMonster . emplace ( cMonster : : mfWater , 0 _tick ) ;
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
}
2021-04-04 20:38:43 -04:00
cTickTime cWorld : : GetTimeOfDay ( void ) const
2021-03-20 16:58:19 -04:00
{
using namespace std : : chrono_literals ;
2021-04-04 20:38:43 -04:00
return std : : chrono : : duration_cast < cTickTime > ( m_WorldDate % 20 min ) ;
2021-03-20 16:58:19 -04:00
}
2021-04-04 20:38:43 -04:00
cTickTimeLong cWorld : : GetWorldAge ( void ) const
2021-03-20 16:58:19 -04:00
{
2021-04-04 20:38:43 -04:00
return std : : chrono : : duration_cast < cTickTimeLong > ( m_WorldAge ) ;
2021-03-20 16:58:19 -04:00
}
2021-04-04 20:38:43 -04:00
cTickTimeLong cWorld : : GetWorldDate ( ) const
{
return std : : chrono : : duration_cast < cTickTimeLong > ( m_WorldDate ) ;
}
cTickTimeLong cWorld : : GetWorldTickAge ( ) const
{
return m_WorldTickAge ;
}
void cWorld : : SetTimeOfDay ( const cTickTime a_TimeOfDay )
2021-03-20 16:58:19 -04:00
{
using namespace std : : chrono_literals ;
2021-04-04 20:38:43 -04:00
m_WorldDate = ( m_WorldDate / 20 min ) * 20 min + a_TimeOfDay ;
2021-03-20 16:58:19 -04:00
UpdateSkyDarkness ( ) ;
BroadcastTimeUpdate ( ) ;
}
2020-11-13 16:50:46 -05:00
int cWorld : : GetDefaultWeatherInterval ( eWeather a_Weather ) const
2014-03-02 15:04:01 -05:00
{
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
2021-01-04 21:13:02 -05:00
bool cWorld : : IsWeatherSunnyAt ( int a_BlockX , int a_BlockZ ) const
2017-12-26 16:25:57 -05:00
{
2021-01-04 21:13:02 -05:00
return m_ChunkMap . IsWeatherSunnyAt ( a_BlockX , a_BlockZ ) ;
}
2017-12-26 16:25:57 -05:00
2021-01-04 21:13:02 -05:00
bool cWorld : : IsWeatherWetAt ( int a_BlockX , int a_BlockZ )
{
return m_ChunkMap . IsWeatherWetAt ( a_BlockX , a_BlockZ ) ;
}
bool cWorld : : IsWeatherWetAtXYZ ( const Vector3i a_Position )
{
return m_ChunkMap . IsWeatherWetAt ( a_Position ) ;
2017-12-26 16:25:57 -05:00
}
2020-04-17 05:36:37 -04:00
void cWorld : : SetNextBlockToTick ( const Vector3i a_BlockPos )
2012-06-14 09:06:06 -04:00
{
2020-12-21 09:31:44 -05: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:
2021-01-26 04:41:55 -05:00
# if !defined(NDEBUG) || defined(ANDROID)
2016-11-07 17:15:07 -05:00
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
2021-01-26 04:41:55 -05:00
# endif // !NDEBUG
2015-11-13 10:08:16 -05:00
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 )
{
2021-03-14 22:28:18 -04:00
BLOCKTYPE HeadBlock = GetBlock ( { static_cast < int > ( a_X ) , PotentialY , static_cast < int > ( a_Z ) } ) ;
2015-11-13 10:08:16 -05:00
// Is this block safe for spawning
if ( HeadBlock ! = E_BLOCK_AIR )
{
continue ;
}
2021-03-14 22:28:18 -04:00
BLOCKTYPE BodyBlock = GetBlock ( { static_cast < int > ( a_X ) , PotentialY - 1 , static_cast < int > ( a_Z ) } ) ;
2015-11-13 10:08:16 -05:00
// Is this block safe for spawning
if ( BodyBlock ! = E_BLOCK_AIR )
{
continue ;
}
2021-03-14 22:28:18 -04:00
BLOCKTYPE FloorBlock = GetBlock ( { static_cast < int > ( a_X ) , PotentialY - 2 , static_cast < int > ( a_Z ) } ) ;
2015-11-13 10:08:16 -05:00
// 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 ;
2021-03-14 22:28:18 -04:00
const BLOCKTYPE BlockType = GetBlock ( { XPos , a_PosY , ZPos } ) ;
2015-11-13 10:08:16 -05:00
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
{
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 ) ) ;
2021-04-04 20:38:43 -04:00
IniFile . SetValueI ( " General " , " TimeInTicks " , GetWorldDate ( ) . count ( ) ) ;
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_CSTasks ) ;
2020-12-21 09:31:44 -05:00
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 ( ) ;
}
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
{
2021-03-17 19:18:02 -04:00
// Notify 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
2015-01-11 16:12:26 -05:00
m_WorldAge + = a_Dt ;
2021-04-04 20:38:43 -04:00
m_WorldTickAge + + ;
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
{
2021-03-20 16:58:19 -04:00
m_WorldDate + = a_Dt ;
2012-11-01 17:38:20 -04:00
2021-03-17 19:18:02 -04:00
// Updates the sky darkness based on current time of day:
2014-08-06 19:07:32 -04:00
UpdateSkyDarkness ( ) ;
2021-03-17 19:18:02 -04:00
// Broadcast time update every 64 ticks (3.2 seconds):
2021-04-04 20:38:43 -04:00
if ( ( m_WorldTickAge % 64 _tick ) = = 0 _tick )
2014-08-06 19:07:32 -04:00
{
BroadcastTimeUpdate ( ) ;
}
2012-06-14 09:06:06 -04:00
}
2021-03-17 19:18:02 -04:00
// Broadcast player list pings every 256 ticks (12.8 seconds):
2021-04-04 20:38:43 -04:00
if ( ( m_WorldTickAge % 256 _tick ) = = 0 _tick )
2021-03-17 19:18:02 -04:00
{
BroadcastPlayerListUpdatePing ( ) ;
}
2021-03-05 08:03:55 -05:00
TickQueuedChunkDataSets ( ) ;
2013-09-16 03:25:23 -04:00
TickQueuedBlocks ( ) ;
2021-01-05 19:35:42 -05:00
m_ChunkMap . Tick ( a_Dt ) ;
2020-08-01 06:25:06 -04:00
TickMobs ( a_Dt ) ;
2021-01-05 19:35:42 -05:00
TickQueuedEntityAdditions ( ) ;
2020-08-01 06:25:06 -04:00
m_MapManager . TickMaps ( ) ;
2013-08-11 15:05:44 -04:00
TickQueuedTasks ( ) ;
2021-03-17 19:18:02 -04:00
TickWeather ( static_cast < float > ( a_Dt . count ( ) ) ) ;
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
2021-06-22 14:31:08 -04:00
// Flush out all clients' buffered data:
for ( const auto Player : m_Players )
{
Player - > GetClientHandle ( ) - > ProcessProtocolOut ( ) ;
}
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 ;
2020-12-21 09:31:44 -05:00
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 ] ;
2013-10-20 08:00:45 -04:00
if (
2021-04-04 20:38:43 -04:00
( m_LastSpawnMonster [ Family ] > ( m_WorldTickAge - cMonster : : GetSpawnDelay ( Family ) ) ) | | // 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 ;
}
2021-04-04 20:38:43 -04:00
m_LastSpawnMonster [ Family ] = m_WorldTickAge ;
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
{
2020-12-21 09:31:44 -05:00
m_ChunkMap . SpawnMobs ( Spawner ) ;
2013-10-20 08:00:45 -04:00
// 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
}
2021-03-05 08:03:55 -05:00
void cWorld : : TickQueuedChunkDataSets ( )
{
decltype ( m_SetChunkDataQueue ) SetChunkDataQueue ;
{
cCSLock Lock ( m_CSSetChunkDataQueue ) ;
SetChunkDataQueue = std : : move ( m_SetChunkDataQueue ) ;
}
// Set any chunk data that has been queued for setting:
for ( auto & Item : SetChunkDataQueue )
{
// A copy of the chunk coordinates since we're moving Item.
const auto Chunk = Item . Chunk ;
// Set the data:
m_ChunkMap . SetChunkData ( std : : move ( Item ) ) ;
// If a client is requesting this chunk, send it to them:
cChunkSender & ChunkSender = m_ChunkSender ;
DoWithChunk (
Chunk . m_ChunkX , Chunk . m_ChunkZ ,
[ & ChunkSender ] ( cChunk & a_Chunk )
{
if ( a_Chunk . HasAnyClients ( ) )
{
ChunkSender . QueueSendChunkTo (
a_Chunk . GetPosX ( ) ,
a_Chunk . GetPosZ ( ) ,
cChunkSender : : Priority : : Medium ,
a_Chunk . GetAllClients ( )
) ;
}
return true ;
}
) ;
} // for itr - SetChunkDataQueue[]
}
2021-01-05 19:35:42 -05:00
void cWorld : : TickQueuedEntityAdditions ( void )
{
decltype ( m_EntitiesToAdd ) EntitiesToAdd ;
{
cCSLock Lock ( m_CSEntitiesToAdd ) ;
EntitiesToAdd = std : : move ( m_EntitiesToAdd ) ;
}
// Ensures m_Players manipulation happens under the chunkmap lock.
cLock Lock ( * this ) ;
// Add entities waiting in the queue to be added:
for ( auto & Item : EntitiesToAdd )
{
const auto Entity = Item . first . get ( ) ;
if ( Entity - > IsPlayer ( ) )
{
const auto Player = static_cast < cPlayer * > ( Entity ) ;
LOGD ( " Adding player %s to world \" %s \" . " , Player - > GetName ( ) . c_str ( ) , m_WorldName . c_str ( ) ) ;
ASSERT ( std : : find ( m_Players . begin ( ) , m_Players . end ( ) , Player ) = = m_Players . end ( ) ) ; // Is it already in the list? HOW?
m_Players . push_back ( Player ) ;
}
m_ChunkMap . AddEntity ( std : : move ( Item . first ) ) ;
if ( const auto OldWorld = Item . second ; OldWorld ! = nullptr )
{
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookEntityChangedWorld ( * Entity , * OldWorld ) ;
}
}
}
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 )
2021-04-04 20:38:43 -04:00
{
return a_Task . first > = m_WorldAge ;
} ) ;
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-10-30 18:33:42 -04:00
void cWorld : : UpdateSkyDarkness ( void )
{
2021-04-04 20:38:43 -04:00
const auto TIME_SUNSET = 12000 _tick ;
const auto TIME_NIGHT_START = 13187 _tick ;
const auto TIME_NIGHT_END = 22812 _tick ;
const auto TIME_SUNRISE = 23999 _tick ;
const auto TIME_SPAWN_DIVISOR = 148 _tick ;
2021-03-20 16:58:19 -04:00
const auto TempTime = GetTimeOfDay ( ) ;
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
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . WakeUpSimulators ( a_Block ) ;
2013-02-28 02:42:45 -05:00
}
2017-08-17 09:48:38 -04:00
void cWorld : : WakeUpSimulatorsInArea ( const cCuboid & a_Area )
{
2020-07-28 19:12:45 -04:00
m_SimulatorManager - > WakeUp ( 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
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . ForEachBlockEntityInChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
2013-11-20 15:53:29 -05:00
}
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 ) ;
2020-09-12 14:57:44 -04:00
if ( ! cPluginManager : : Get ( ) - > CallHookExploding ( * this , a_ExplosionSize , a_CanCauseFire , a_BlockX , a_BlockY , a_BlockZ , a_Source , a_SourceData ) & & ( a_ExplosionSize > 0 ) )
2013-04-17 22:42:45 -04:00
{
2020-10-24 13:48:48 -04:00
// TODO: CanCauseFire gets reset to false for some reason, (plugin has ability to change it, might be related)
2020-10-08 15:13:44 -04:00
const cEntity * Entity ;
switch ( a_Source )
{
case eExplosionSource : : esEnderCrystal :
case eExplosionSource : : esGhastFireball :
case eExplosionSource : : esMonster :
case eExplosionSource : : esPrimedTNT :
case eExplosionSource : : esWitherBirth :
case eExplosionSource : : esWitherSkull :
{
Entity = static_cast < const cEntity * > ( a_SourceData ) ;
break ;
}
default :
{
Entity = nullptr ;
}
}
2021-02-06 13:37:50 -05:00
Explodinator : : Kaboom ( * this , Vector3d ( a_BlockX , a_BlockY , a_BlockZ ) , FloorC ( a_ExplosionSize ) , a_CanCauseFire , Entity ) ;
2020-09-12 14:57:44 -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
}
}
2021-03-28 09:40:57 -04:00
bool cWorld : : DoWithBlockEntityAt ( const Vector3i a_Position , cBlockEntityCallback a_Callback )
2014-01-18 08:16:47 -05:00
{
2021-03-28 09:40:57 -04:00
return m_ChunkMap . DoWithBlockEntityAt ( a_Position , 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 )
{
2021-03-28 09:40:57 -04:00
return DoWithBlockEntityAt ( { a_BlockX , a_BlockY , a_BlockZ } , [ & a_Line1 , & a_Line2 , & a_Line3 , & a_Line4 ] ( cBlockEntity & a_BlockEntity )
{
if ( ( a_BlockEntity . GetBlockType ( ) ! = E_BLOCK_WALLSIGN ) & & ( a_BlockEntity . GetBlockType ( ) ! = E_BLOCK_SIGN_POST ) )
{
return false ; // Not a sign
}
const auto & Sign = static_cast < cSignEntity & > ( a_BlockEntity ) ;
a_Line1 = Sign . GetLine ( 0 ) ;
a_Line2 = Sign . GetLine ( 1 ) ;
a_Line3 = Sign . GetLine ( 2 ) ;
a_Line4 = Sign . GetLine ( 3 ) ;
return true ;
} ) ;
2012-06-17 15:58:39 -04:00
}
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
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . DoWithChunk ( a_ChunkX , a_ChunkZ , a_Callback ) ;
2013-08-03 14:26:50 -04:00
}
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
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . DoWithChunkAt ( a_BlockPos , a_Callback ) ;
2015-03-21 13:17:26 -04:00
}
2020-08-26 08:08:37 -04:00
bool cWorld : : GrowTree ( const Vector3i a_BlockPos )
2012-06-14 09:06:06 -04:00
{
2020-08-26 08:08:37 -04:00
if ( GetBlock ( a_BlockPos ) = = E_BLOCK_SAPLING )
2012-06-14 09:06:06 -04:00
{
// There is a sapling here, grow a tree according to its type:
2020-08-26 08:08:37 -04:00
return GrowTreeFromSapling ( a_BlockPos ) ;
2012-06-14 09:06:06 -04:00
}
else
{
// There is nothing here, grow a tree based on the current biome here:
2020-08-26 08:08:37 -04:00
return GrowTreeByBiome ( a_BlockPos ) ;
2012-06-14 09:06:06 -04:00
}
}
2020-08-26 08:08:37 -04:00
bool cWorld : : GrowTreeFromSapling ( Vector3i a_BlockPos )
2012-06-14 09:06:06 -04:00
{
cNoise Noise ( m_Generator . GetSeed ( ) ) ;
2012-07-15 09:33:43 -04:00
sSetBlockVector Logs , Other ;
2021-04-04 20:38:43 -04:00
auto WorldAge = static_cast < int > ( m_WorldTickAge . count ( ) & 0xffffffff ) ;
2020-08-26 08:08:37 -04:00
auto SaplingMeta = GetBlockMeta ( a_BlockPos ) ;
switch ( SaplingMeta & 0x07 )
2012-06-14 09:06:06 -04:00
{
2020-08-26 08:08:37 -04:00
case E_META_SAPLING_APPLE : GetAppleTreeImage ( a_BlockPos , Noise , WorldAge , Logs , Other ) ; break ;
case E_META_SAPLING_BIRCH : GetBirchTreeImage ( a_BlockPos , Noise , WorldAge , Logs , Other ) ; break ;
2020-05-07 17:15:40 -04:00
case E_META_SAPLING_CONIFER :
{
2020-08-26 08:08:37 -04:00
bool IsLarge = GetLargeTreeAdjustment ( a_BlockPos , SaplingMeta ) ;
GetConiferTreeImage ( a_BlockPos , Noise , WorldAge , Logs , Other , IsLarge ) ;
2020-05-07 17:15:40 -04:00
break ;
}
2020-08-26 08:08:37 -04:00
case E_META_SAPLING_ACACIA : GetAcaciaTreeImage ( a_BlockPos , Noise , WorldAge , Logs , Other ) ; break ;
2015-07-13 13:02:00 -04:00
case E_META_SAPLING_JUNGLE :
{
2020-08-26 08:08:37 -04:00
bool IsLarge = GetLargeTreeAdjustment ( a_BlockPos , SaplingMeta ) ;
GetJungleTreeImage ( a_BlockPos , Noise , WorldAge , Logs , Other , IsLarge ) ;
2015-07-13 13:02:00 -04:00
break ;
}
case E_META_SAPLING_DARK_OAK :
{
2020-08-26 08:08:37 -04:00
if ( ! GetLargeTreeAdjustment ( a_BlockPos , 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
}
2020-08-26 08:08:37 -04:00
GetDarkoakTreeImage ( a_BlockPos , 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
}
2020-08-26 08:08:37 -04:00
bool cWorld : : GetLargeTreeAdjustment ( Vector3i & a_BlockPos , NIBBLETYPE a_Meta )
2019-09-01 03:30:00 -04:00
{
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 ;
2020-08-26 08:08:37 -04:00
GetBlockTypeMeta ( a_BlockPos . addedXZ ( x , z ) , type , meta ) ;
IsLarge = IsLarge & & ( type = = E_BLOCK_SAPLING ) & & ( ( meta & 0x07 ) = = a_Meta ) ;
2019-09-01 03:30:00 -04:00
}
}
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 ;
2020-08-26 08:08:37 -04:00
GetBlockTypeMeta ( a_BlockPos . addedXZ ( x , z ) , type , meta ) ;
IsLarge = IsLarge & & ( type = = E_BLOCK_SAPLING ) & & ( ( meta & 0x07 ) = = a_Meta ) ;
2019-09-01 03:30:00 -04:00
}
}
if ( IsLarge )
{
2020-08-26 08:08:37 -04:00
- - a_BlockPos . z ;
2019-09-01 03:30:00 -04:00
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 ;
2020-08-26 08:08:37 -04:00
GetBlockTypeMeta ( a_BlockPos . addedXZ ( x , z ) , type , meta ) ;
IsLarge = IsLarge & & ( type = = E_BLOCK_SAPLING ) & & ( ( meta & 0x07 ) = = a_Meta ) ;
2019-09-01 03:30:00 -04:00
}
}
if ( IsLarge )
{
2020-08-26 08:08:37 -04:00
- - a_BlockPos . x ;
- - a_BlockPos . z ;
2019-09-01 03:30:00 -04:00
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 ;
2020-08-26 08:08:37 -04:00
GetBlockTypeMeta ( a_BlockPos . addedXZ ( x , z ) , type , meta ) ;
IsLarge = IsLarge & & ( type = = E_BLOCK_SAPLING ) & & ( ( meta & 0x07 ) = = a_Meta ) ;
2019-09-01 03:30:00 -04:00
}
}
if ( IsLarge )
{
2020-08-26 08:08:37 -04:00
- - a_BlockPos . x ;
2019-09-01 03:30:00 -04:00
}
return IsLarge ;
}
2020-08-26 08:08:37 -04:00
bool cWorld : : GrowTreeByBiome ( const Vector3i a_BlockPos )
2012-06-14 09:06:06 -04:00
{
cNoise Noise ( m_Generator . GetSeed ( ) ) ;
2012-07-15 09:33:43 -04:00
sSetBlockVector Logs , Other ;
2021-04-04 20:38:43 -04:00
auto seq = static_cast < int > ( m_WorldTickAge . count ( ) & 0xffffffff ) ;
2020-08-26 08:08:37 -04:00
GetTreeImageByBiome ( a_BlockPos , Noise , seq , GetBiomeAt ( a_BlockPos . x , a_BlockPos . 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:
2020-12-21 09:31:44 -05:00
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
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . GrowPlantAt ( a_BlockPos , a_NumStages ) ;
2019-10-11 05:02:53 -04:00
}
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
}
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
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . GetBiomeAt ( a_BlockX , a_BlockZ ) ;
2012-06-14 09:06:06 -04:00
}
2014-02-18 07:06:18 -05:00
bool cWorld : : SetBiomeAt ( int a_BlockX , int a_BlockZ , EMCSBiome a_Biome )
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . SetBiomeAt ( a_BlockX , a_BlockZ , a_Biome ) ;
2014-02-18 07:06:18 -05:00
}
bool cWorld : : SetAreaBiome ( int a_MinX , int a_MaxX , int a_MinZ , int a_MaxZ , EMCSBiome a_Biome )
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . SetAreaBiome ( a_MinX , a_MaxX , a_MinZ , a_MaxZ , a_Biome ) ;
2014-02-18 07:06:18 -05:00
}
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
}
2020-12-23 17:39:53 -05:00
void cWorld : : SetMaxViewDistance ( int a_MaxViewDistance )
2016-11-18 14:00:04 -05:00
{
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
{
2020-12-21 09:31:44 -05: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
2020-08-26 16:45:13 -04:00
void cWorld : : SetBlockMeta ( Vector3i a_BlockPos , NIBBLETYPE a_MetaData )
2012-06-14 09:06:06 -04:00
{
2020-12-21 09:31:44 -05:00
m_ChunkMap . SetBlockMeta ( a_BlockPos , a_MetaData ) ;
2012-06-14 09:06:06 -04:00
}
2021-05-05 09:25:10 -04:00
NIBBLETYPE cWorld : : GetBlockSkyLight ( Vector3i a_BlockPos ) const
2012-06-14 09:06:06 -04:00
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . GetBlockSkyLight ( a_BlockPos ) ;
2012-06-14 09:06:06 -04:00
}
2021-05-05 09:25:10 -04:00
NIBBLETYPE cWorld : : GetBlockBlockLight ( Vector3i a_BlockPos ) const
2012-10-20 07:40:34 -04:00
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . GetBlockBlockLight ( a_BlockPos ) ;
2012-10-20 07:40:34 -04:00
}
2021-05-05 09:25:10 -04:00
bool cWorld : : GetBlockTypeMeta ( Vector3i a_BlockPos , BLOCKTYPE & a_BlockType , NIBBLETYPE & a_BlockMeta ) const
2012-06-14 09:06:06 -04:00
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . GetBlockTypeMeta ( a_BlockPos , a_BlockType , a_BlockMeta ) ;
2012-06-14 09:06:06 -04:00
}
2021-05-05 09:25:10 -04:00
bool cWorld : : GetBlockInfo ( Vector3i a_BlockPos , BLOCKTYPE & a_BlockType , NIBBLETYPE & a_Meta , NIBBLETYPE & a_SkyLight , NIBBLETYPE & a_BlockLight ) const
2012-10-20 07:40:34 -04:00
{
2020-12-21 09:31:44 -05: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 )
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . WriteBlockArea ( a_Area , a_MinBlockX , a_MinBlockY , a_MinBlockZ , a_DataTypes ) ;
2012-10-06 12:58:31 -04:00
}
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
2020-08-01 14:18:03 -04:00
auto Pickup = std : : 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 ;
}
2020-08-01 14:18:03 -04:00
auto pickup = std : : 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
{
2020-08-01 14:18:03 -04:00
auto pickup = std : : make_unique < cPickup > ( a_Pos , a_Item , false , a_Speed , a_LifetimeTicks , a_CanCombine ) ;
2019-09-29 08:59:24 -04:00
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
{
2020-08-01 14:18:03 -04:00
auto fallingBlock = std : : make_unique < cFallingBlock > ( a_Pos , a_BlockType , a_BlockMeta ) ;
2019-09-29 08:59:24 -04:00
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
}
2020-08-01 14:18:03 -04:00
auto expOrb = std : : make_unique < cExpOrb > ( a_Pos , a_Reward ) ;
2019-09-29 08:59:24 -04:00
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 )
{
2020-08-01 14:18:03 -04:00
auto ExpOrb = std : : 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 )
{
2020-08-01 14:18:03 -04:00
case E_ITEM_MINECART : Minecart = std : : make_unique < cRideableMinecart > ( a_Pos , a_Content , a_BlockHeight ) ; break ;
case E_ITEM_CHEST_MINECART : Minecart = std : : make_unique < cMinecartWithChest > ( a_Pos ) ; break ;
case E_ITEM_FURNACE_MINECART : Minecart = std : : make_unique < cMinecartWithFurnace > ( a_Pos ) ; break ;
case E_ITEM_MINECART_WITH_TNT : Minecart = std : : make_unique < cMinecartWithTNT > ( a_Pos ) ; break ;
case E_ITEM_MINECART_WITH_HOPPER : Minecart = std : : 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
{
2020-08-01 14:18:03 -04:00
auto Boat = std : : 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
{
2020-08-01 14:18:03 -04:00
auto TNT = std : : 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
}
2020-10-29 16:47:20 -04:00
UInt32 cWorld : : SpawnEnderCrystal ( Vector3d a_Pos , bool a_ShowBottom )
{
auto EnderCrystal = std : : make_unique < cEnderCrystal > ( a_Pos , a_ShowBottom ) ;
auto EnderCrystalPtr = EnderCrystal . get ( ) ;
if ( ! EnderCrystalPtr - > Initialize ( std : : move ( EnderCrystal ) , * this ) )
{
return cEntity : : INVALID_ID ;
}
return EnderCrystalPtr - > GetUniqueID ( ) ;
}
2020-07-29 14:30:38 -04:00
void cWorld : : PlaceBlock ( const Vector3i a_Position , const BLOCKTYPE a_BlockType , const NIBBLETYPE a_BlockMeta )
2014-12-24 01:20:17 -05:00
{
2021-05-05 09:25:10 -04:00
BLOCKTYPE BlockType ;
NIBBLETYPE BlockMeta ;
GetBlockTypeMeta ( a_Position , BlockType , BlockMeta ) ;
2020-07-29 14:30:38 -04:00
SetBlock ( a_Position , a_BlockType , a_BlockMeta ) ;
2014-12-24 01:20:17 -05:00
2020-07-29 14:30:38 -04:00
cChunkInterface ChunkInterface ( GetChunkMap ( ) ) ;
2021-05-05 09:25:10 -04:00
cBlockHandler : : For ( BlockType ) . OnBroken ( ChunkInterface , * this , a_Position , BlockType , BlockMeta , nullptr ) ;
2020-09-20 09:50:52 -04:00
cBlockHandler : : For ( a_BlockType ) . OnPlaced ( ChunkInterface , * this , a_Position , a_BlockType , a_BlockMeta ) ;
2012-06-14 09:06:06 -04:00
}
bool cWorld : : GetBlocks ( sSetBlockVector & a_Blocks , bool a_ContinueOnFailure )
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . GetBlocks ( a_Blocks , a_ContinueOnFailure ) ;
2012-06-14 09:06:06 -04:00
}
2020-10-08 15:13:44 -04:00
bool cWorld : : DigBlock ( Vector3i a_BlockPos , const cEntity * a_Digger )
2019-10-16 04:06:34 -04:00
{
2020-07-29 14:30:38 -04:00
BLOCKTYPE BlockType ;
NIBBLETYPE BlockMeta ;
GetBlockTypeMeta ( a_BlockPos , BlockType , BlockMeta ) ;
2020-12-21 09:31:44 -05:00
if ( ! m_ChunkMap . DigBlock ( a_BlockPos ) )
2019-10-16 04:06:34 -04:00
{
return false ;
}
2020-07-29 14:30:38 -04:00
cChunkInterface ChunkInterface ( GetChunkMap ( ) ) ;
2020-10-08 15:13:44 -04:00
cBlockHandler : : For ( BlockType ) . OnBroken ( ChunkInterface , * this , a_BlockPos , BlockType , BlockMeta , a_Digger ) ;
2020-07-29 14:30:38 -04:00
2019-10-16 04:06:34 -04:00
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 ) ;
2020-10-08 15:13:44 -04:00
if ( ! DigBlock ( a_BlockPos , a_Digger ) )
2019-10-16 04:06:34 -04:00
{
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
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . PickupsFromBlock ( a_BlockPos , a_Digger , a_Tool ) ;
2012-06-14 09:06:06 -04:00
}
2021-05-05 09:25:10 -04:00
void cWorld : : SendBlockTo ( int a_X , int a_Y , int a_Z , const cPlayer & a_Player )
2012-06-14 09:06:06 -04:00
{
2020-12-21 09:31:44 -05:00
m_ChunkMap . SendBlockTo ( a_X , a_Y , a_Z , a_Player ) ;
2012-06-14 09:06:06 -04:00
}
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
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . GetHeight ( a_X , a_Z ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
bool cWorld : : TryGetHeight ( int a_BlockX , int a_BlockZ , int & a_Height )
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . TryGetHeight ( a_BlockX , a_BlockZ , a_Height ) ;
2013-04-13 17:02:10 -04:00
}
2012-08-24 03:58:26 -04:00
void cWorld : : SendBlockEntity ( int a_BlockX , int a_BlockY , int a_BlockZ , cClientHandle & a_Client )
{
2020-12-21 09:31:44 -05:00
m_ChunkMap . SendBlockEntity ( a_BlockX , a_BlockY , a_BlockZ , a_Client ) ;
2012-08-24 03:58:26 -04:00
}
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
{
2020-12-21 09:31:44 -05: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
{
2020-12-21 09:31:44 -05: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
{
2020-12-21 09:31:44 -05:00
m_ChunkMap . MarkChunkSaved ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
2021-03-05 08:03:55 -05:00
void cWorld : : QueueSetChunkData ( struct SetChunkData & & a_SetChunkData )
2012-06-14 09:06:06 -04: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
}
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
)
{
2020-12-21 09:31:44 -05:00
m_ChunkMap . ChunkLighted ( a_ChunkX , a_ChunkZ , a_BlockLight , a_SkyLight ) ;
2012-06-14 09:06:06 -04:00
}
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
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . GetChunkData ( a_Coords , a_Callback ) ;
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
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . IsChunkQueued ( a_ChunkX , a_ChunkZ ) ;
2014-09-05 16:16:48 -04:00
}
2013-04-13 17:02:10 -04:00
bool cWorld : : IsChunkValid ( int a_ChunkX , int a_ChunkZ ) const
2012-06-14 09:06:06 -04:00
{
2020-12-21 09:31:44 -05: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
{
2020-12-21 09:31:44 -05: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
{
2021-04-04 20:38:43 -04:00
m_LastChunkCheck = m_WorldAge ;
2020-12-21 09:31:44 -05:00
m_ChunkMap . UnloadUnusedChunks ( ) ;
2012-06-14 09:06:06 -04:00
}
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
{
2020-12-21 09:31:44 -05:00
m_ChunkMap . CollectPickupsByPlayer ( a_Player ) ;
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 ) ;
2021-06-22 14:31:08 -04:00
for ( const auto Player : m_Players )
2012-08-22 19:05:12 -04:00
{
2021-06-22 14:31:08 -04:00
if ( ! Player - > IsTicking ( ) )
2016-02-01 15:49:34 -05:00
{
continue ;
}
2021-06-22 14:31:08 -04:00
size_t Rating = RateCompareString ( a_PlayerNameHint , Player - > GetName ( ) ) ;
2013-02-01 14:59:58 -05:00
if ( Rating > = BestRating )
2012-08-22 19:05:12 -04:00
{
2021-06-22 14:31:08 -04:00
BestMatch = Player ;
2012-08-22 19:05:12 -04:00
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 ) ;
2021-06-22 14:31:08 -04:00
for ( const auto Player : m_Players )
2012-06-14 09:06:06 -04:00
{
2021-06-22 14:31:08 -04:00
if ( ! Player - > IsTicking ( ) )
2016-02-01 15:49:34 -05:00
{
continue ;
}
2018-08-02 10:59:10 -04:00
2021-06-22 14:31:08 -04:00
if ( a_IgnoreSpectator & & Player - > IsGameModeSpectator ( ) )
2018-08-02 10:59:10 -04:00
{
continue ;
}
2021-06-22 14:31:08 -04:00
Vector3f Pos = Player - > 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 ;
2021-06-22 14:31:08 -04:00
ClosestPlayer = Player ;
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 ) ;
2021-01-05 19:35:42 -05:00
for ( const auto & Player : m_Players )
2012-06-14 09:06:06 -04:00
{
2021-01-05 19:35:42 -05:00
if ( ! Player - > GetClientHandle ( ) - > IsDestroyed ( ) )
2012-06-14 09:06:06 -04:00
{
2021-01-05 19:35:42 -05:00
a_DestPlayer - > GetClientHandle ( ) - > SendPlayerListAddPlayer ( * Player ) ;
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
{
2020-12-21 09:31:44 -05: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
{
2020-12-21 09:31:44 -05: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
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . ForEachEntityInBox ( a_Box , a_Callback ) ;
2014-09-03 11:00:26 -04:00
}
2021-03-17 19:18:02 -04:00
size_t cWorld : : GetPlayerCount ( ) const
{
cLock Lock ( * this ) ;
return m_Players . size ( ) ;
}
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 ) ;
2021-01-05 19:35:42 -05:00
for ( const auto & Item : m_EntitiesToAdd )
2015-03-18 10:35:19 -04:00
{
2021-01-05 19:35:42 -05:00
if ( Item . first - > GetUniqueID ( ) = = a_UniqueID )
2015-03-18 10:35:19 -04:00
{
2021-01-05 19:35:42 -05:00
a_Callback ( * Item . first ) ;
2015-03-18 10:35:19 -04:00
return true ;
}
} // for ent - m_EntitiesToAdd[]
}
// Then check the chunkmap:
2020-12-21 09:31:44 -05: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
{
2020-12-21 09:31:44 -05: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
{
2020-12-21 09:31:44 -05: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
{
2020-12-21 09:31:44 -05:00
m_ChunkMap . RemoveChunkClient ( a_ChunkX , a_ChunkZ , a_Client ) ;
2012-06-14 09:06:06 -04:00
}
void cWorld : : RemoveClientFromChunks ( cClientHandle * a_Client )
{
2020-12-21 09:31:44 -05:00
m_ChunkMap . RemoveClientFromChunks ( a_Client ) ;
2012-06-14 09:06:06 -04:00
}
2020-09-12 15:43:18 -04:00
void cWorld : : SendChunkTo ( int a_ChunkX , int a_ChunkZ , cChunkSender : : Priority 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
}
2020-09-12 15:43:18 -04:00
void cWorld : : ForceSendChunkTo ( int a_ChunkX , int a_ChunkZ , cChunkSender : : Priority 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
}
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
{
2020-12-21 09:31:44 -05: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
{
2020-12-21 09:31:44 -05: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
{
2021-03-28 09:40:57 -04:00
// TODO: rvalue these strings
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
2021-03-28 09:40:57 -04:00
if (
DoWithBlockEntityAt ( { a_BlockX , a_BlockY , a_BlockZ } , [ & Line1 , & Line2 , & Line3 , & Line4 ] ( cBlockEntity & a_BlockEntity )
{
if ( ( a_BlockEntity . GetBlockType ( ) ! = E_BLOCK_WALLSIGN ) & & ( a_BlockEntity . GetBlockType ( ) ! = E_BLOCK_SIGN_POST ) )
{
return false ; // Not a sign
}
static_cast < cSignEntity & > ( a_BlockEntity ) . SetLines ( Line1 , Line2 , Line3 , Line4 ) ;
return true ;
} )
)
2013-06-12 03:14:06 -04:00
{
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 )
{
2021-03-28 09:40:57 -04:00
return DoWithBlockEntityAt ( { a_BlockX , a_BlockY , a_BlockZ } , [ & ] ( cBlockEntity & a_BlockEntity )
{
if ( a_BlockEntity . GetBlockType ( ) ! = E_BLOCK_COMMAND_BLOCK )
2014-01-23 07:57:04 -05:00
{
return false ;
}
2021-03-28 09:40:57 -04:00
static_cast < cCommandBlockEntity & > ( a_BlockEntity ) . SetCommand ( a_Command ) ;
return true ;
} ) ;
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 ;
2021-03-14 22:28:18 -04:00
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 ;
2021-03-14 22:28:18 -04:00
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 )
{
2021-03-14 22:28:18 -04:00
SetBlockMeta ( { a_BlockX , a_BlockY , a_BlockZ } , Meta ^ 0x4 ) ;
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 )
{
2020-12-21 09:31:44 -05:00
m_ChunkMap . MarkChunkRegenerating ( a_ChunkX , a_ChunkZ ) ;
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 )
{
2020-12-21 09:31:44 -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 )
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . IsChunkLighted ( a_ChunkX , a_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
}
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 )
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . ForEachChunkInRect ( a_MinChunkX , a_MaxChunkX , a_MinChunkZ , a_MaxChunkZ , a_Callback ) ;
2012-07-02 12:30:17 -04:00
}
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
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . ForEachLoadedChunk ( a_Callback ) ;
2015-09-24 10:43:31 -04:00
}
2012-06-14 09:06:06 -04:00
void cWorld : : SaveAllChunks ( void )
{
2017-09-07 08:41:16 -04:00
if ( IsSavingEnabled ( ) )
{
2021-04-04 20:38:43 -04:00
m_LastSave = m_WorldAge ;
2020-12-21 09:31:44 -05:00
m_ChunkMap . SaveAllChunks ( ) ;
2017-09-07 08:41:16 -04:00
}
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
2021-04-04 20:38:43 -04:00
void cWorld : : ScheduleTask ( const cTickTime a_DelayTicks , std : : function < void ( cWorld & ) > a_Task )
2014-01-14 15:17:03 -05:00
{
2021-04-04 20:38:43 -04:00
const auto TargetTick = a_DelayTicks + m_WorldAge ;
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
2021-01-05 19:35:42 -05:00
void cWorld : : AddEntity ( OwnedEntity a_Entity , cWorld * a_OldWorld )
2012-06-14 09:06:06 -04:00
{
2014-06-06 16:31:16 -04:00
cCSLock Lock ( m_CSEntitiesToAdd ) ;
2021-01-05 19:35:42 -05:00
m_EntitiesToAdd . emplace_back ( std : : move ( a_Entity ) , a_OldWorld ) ;
2013-04-13 17:02:10 -04:00
}
2017-09-05 10:11:35 -04:00
OwnedEntity cWorld : : RemoveEntity ( cEntity & a_Entity )
{
2021-01-05 19:35:42 -05:00
// Remove players from the player list:
if ( a_Entity . IsPlayer ( ) )
{
cLock Lock ( * this ) ;
const auto Player = static_cast < cPlayer * > ( & a_Entity ) ;
LOGD ( " Removing player %s from world \" %s \" " , Player - > GetName ( ) . c_str ( ) , m_WorldName . c_str ( ) ) ;
2021-06-22 14:31:08 -04:00
m_Players . erase ( std : : remove ( m_Players . begin ( ) , m_Players . end ( ) , Player ) , m_Players . end ( ) ) ;
2021-01-05 19:35:42 -05:00
}
2017-09-05 10:11:35 -04:00
// Check if the entity is in the chunkmap:
2020-12-21 09:31:44 -05:00
auto Entity = m_ChunkMap . RemoveEntity ( a_Entity ) ;
2017-09-05 10:11:35 -04:00
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 ( ) ,
2021-01-05 19:35:42 -05:00
[ & a_Entity ] ( const auto & Item )
2017-09-05 10:11:35 -04:00
{
2021-01-05 19:35:42 -05:00
return ( Item . first . get ( ) = = & a_Entity ) ;
2017-09-05 10:11:35 -04:00
}
) ;
if ( itr ! = m_EntitiesToAdd . end ( ) )
{
2021-01-05 19:35:42 -05:00
Entity = std : : move ( itr - > first ) ;
2017-09-05 10:11:35 -04:00
m_EntitiesToAdd . erase ( itr ) ;
}
2021-01-05 19:35:42 -05:00
2017-09-05 10:11:35 -04:00
return Entity ;
}
2016-09-03 11:38:29 -04:00
size_t cWorld : : GetNumChunks ( void ) const
2012-06-14 09:06:06 -04:00
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . GetNumChunks ( ) ;
2012-06-14 09:06:06 -04:00
}
2016-09-03 11:38:29 -04:00
size_t cWorld : : GetNumUnusedDirtyChunks ( void ) const
{
2020-12-21 09:31:44 -05:00
return m_ChunkMap . GetNumUnusedDirtyChunks ( ) ;
2016-09-03 11:38:29 -04:00
}
2012-06-14 09:06:06 -04:00
void cWorld : : GetChunkStats ( int & a_NumValid , int & a_NumDirty , int & a_NumInLightingQueue )
{
2020-12-21 09:31:44 -05:00
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-12-21 09:31:44 -05: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 (
2021-03-14 22:28:18 -04:00
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 } ) )
2012-10-13 04:56:12 -04:00
) ;
}
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 ) ;
2021-06-22 14:31:08 -04:00
for ( const auto Player : m_Players )
2013-07-31 05:16:11 -04:00
{
2021-06-22 14:31:08 -04:00
AString PlayerName = Player - > HasCustomName ( ) ? Player - > GetCustomName ( ) : Player - > GetName ( ) ;
2014-09-02 13:12:35 -04:00
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 )
{
2020-12-21 09:31:44 -05:00
m_ChunkMap . SetChunkAlwaysTicked ( a_ChunkX , a_ChunkZ , a_AlwaysTicked ) ;
2014-07-10 12:18:32 -04:00
}
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-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 ) ;
2021-03-05 08:03:55 -05:00
struct SetChunkData Data ( { a_ChunkDesc . GetChunkX ( ) , a_ChunkDesc . GetChunkZ ( ) } ) ;
{
Data . BlockData . SetAll ( a_ChunkDesc . GetBlockTypes ( ) , BlockMetas ) ;
std : : copy ( a_ChunkDesc . GetBiomeMap ( ) , a_ChunkDesc . GetBiomeMap ( ) + std : : size ( a_ChunkDesc . GetBiomeMap ( ) ) , Data . BiomeMap ) ;
std : : copy ( a_ChunkDesc . GetHeightMap ( ) , a_ChunkDesc . GetHeightMap ( ) + std : : size ( a_ChunkDesc . GetHeightMap ( ) ) , Data . HeightMap ) ;
Data . Entities = std : : move ( a_ChunkDesc . GetEntities ( ) ) ;
Data . BlockEntities = std : : move ( a_ChunkDesc . GetBlockEntities ( ) ) ;
Data . IsLightValid = false ;
}
m_World - > QueueSetChunkData ( std : : move ( Data ) ) ;
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
) ;
}