diff options
Diffstat (limited to 'src/World.cpp')
-rw-r--r-- | src/World.cpp | 352 |
1 files changed, 172 insertions, 180 deletions
diff --git a/src/World.cpp b/src/World.cpp index c0a79b9d0..123e4a8da 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -58,6 +58,8 @@ #endif #include "Broadcaster.h" +#include "SpawnPrepare.h" +#include "FastRandom.h" @@ -72,140 +74,6 @@ const int TIME_SPAWN_DIVISOR = 148; //////////////////////////////////////////////////////////////////////////////// -// cSpawnPrepare: - -/** Generates and lights the spawn area of the world. Runs as a separate thread. */ -class cSpawnPrepare: - public cIsThread, - public cChunkCoordCallback -{ - typedef cIsThread super; - -public: - cSpawnPrepare(cWorld & a_World, int a_SpawnChunkX, int a_SpawnChunkZ, int a_PrepareDistance): - super("SpawnPrepare"), - m_World(a_World), - m_SpawnChunkX(a_SpawnChunkX), - m_SpawnChunkZ(a_SpawnChunkZ), - m_PrepareDistance(a_PrepareDistance), - m_MaxIdx(a_PrepareDistance * a_PrepareDistance), - m_NumPrepared(0), - m_LastReportChunkCount(0) - { - // Start the thread: - Start(); - - // Wait for start confirmation, so that the thread can be waited-upon after the constructor returns: - m_EvtStarted.Wait(); - } - - - // cIsThread override: - virtual void Execute(void) override - { - // Confirm thread start: - m_EvtStarted.Set(); - - // Queue the initial chunks: - m_MaxIdx = m_PrepareDistance * m_PrepareDistance; - int maxQueue = std::min(m_MaxIdx - 1, 100); // Number of chunks to queue at once - m_NextIdx = maxQueue; - m_LastReportTime = std::chrono::steady_clock::now(); - for (int i = 0; i < maxQueue; i++) - { - int chunkX, chunkZ; - DecodeChunkCoords(i, chunkX, chunkZ); - m_World.PrepareChunk(chunkX, chunkZ, this); - } // for i - - // Wait for the lighting thread to prepare everything. Event is set in the Call() callback: - m_EvtFinished.Wait(); - } - -protected: - cWorld & m_World; - int m_SpawnChunkX; - int m_SpawnChunkZ; - int m_PrepareDistance; - - /** The index of the next chunk to be queued in the lighting thread. */ - int m_NextIdx; - - /** The maximum index of the prepared chunks. Queueing stops when m_NextIdx reaches this number. */ - int m_MaxIdx; - - /** Total number of chunks already finished preparing. Preparation finishes when this number reaches m_MaxIdx. */ - int m_NumPrepared; - - /** Event used to signal that the thread has started. */ - cEvent m_EvtStarted; - - /** Event used to signal that the preparation is finished. */ - cEvent m_EvtFinished; - - /** The timestamp of the last progress report emitted. */ - std::chrono::steady_clock::time_point m_LastReportTime; - - /** Number of chunks prepared when the last progress report was emitted. */ - int m_LastReportChunkCount; - - // cChunkCoordCallback override: - virtual void Call(int a_ChunkX, int a_ChunkZ) - { - // Check if this was the last chunk: - m_NumPrepared += 1; - if (m_NumPrepared >= m_MaxIdx) - { - m_EvtFinished.Set(); - // Must return here, because "this" may have gotten deleted by the previous line - return; - } - - // Queue another chunk, if appropriate: - if (m_NextIdx < m_MaxIdx) - { - int chunkX, chunkZ; - DecodeChunkCoords(m_NextIdx, chunkX, chunkZ); - m_World.GetLightingThread().QueueChunk(chunkX, chunkZ, this); - m_NextIdx += 1; - } - - // Report progress every 1 second: - auto Now = std::chrono::steady_clock::now(); - if (Now - m_LastReportTime > std::chrono::seconds(1)) - { - float PercentDone = static_cast<float>(m_NumPrepared * 100) / m_MaxIdx; - float ChunkSpeed = static_cast<float>((m_NumPrepared - m_LastReportChunkCount) * 1000) / std::chrono::duration_cast<std::chrono::milliseconds>(Now - m_LastReportTime).count(); - LOG("Preparing spawn (%s): %.02f%% (%d/%d; %.02f chunks / sec)", - m_World.GetName().c_str(), PercentDone, m_NumPrepared, m_MaxIdx, ChunkSpeed - ); - m_LastReportTime = Now; - m_LastReportChunkCount = m_NumPrepared; - } - } - - - /** Decodes the index into chunk coords. Provides the specific chunk ordering. */ - void DecodeChunkCoords(int a_Idx, int & a_ChunkX, int & a_ChunkZ) - { - // A zigzag pattern from the top to bottom, each row alternating between forward-x and backward-x: - int z = a_Idx / m_PrepareDistance; - int x = a_Idx % m_PrepareDistance; - if ((z & 1) == 0) - { - // Reverse every second row: - x = m_PrepareDistance - 1 - x; - } - a_ChunkZ = m_SpawnChunkZ + z - m_PrepareDistance / 2; - a_ChunkX = m_SpawnChunkX + x - m_PrepareDistance / 2; - } -}; - - - - - -//////////////////////////////////////////////////////////////////////////////// // cWorld::cLock: cWorld::cLock::cLock(cWorld & a_World) : @@ -319,6 +187,7 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin m_Scoreboard(this), m_MapManager(this), m_GeneratorCallbacks(*this), + m_ChunkSender(*this), m_TickThread(*this) { LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str()); @@ -362,6 +231,7 @@ cWorld::~cWorld() void cWorld::CastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) { BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ); + BroadcastSoundEffect("ambient.weather.thunder", a_BlockX, a_BlockY, a_BlockZ, 50, 1); } @@ -469,8 +339,7 @@ void cWorld::InitializeSpawn(void) int ViewDist = IniFile.GetValueSetI("SpawnPosition", "PregenerateDistance", DefaultViewDist); IniFile.WriteFile(m_IniFileName); - cSpawnPrepare prep(*this, ChunkX, ChunkZ, ViewDist); - prep.Wait(); + cSpawnPrepare::PrepareChunks(*this, ChunkX, ChunkZ, ViewDist); #ifdef TEST_LINEBLOCKTRACER // DEBUG: Test out the cLineBlockTracer class by tracing a few lines: @@ -597,6 +466,10 @@ void cWorld::Start(void) 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", (int)m_GameMode); @@ -621,18 +494,18 @@ void cWorld::Start(void) InitialiseAndLoadMobSpawningValues(IniFile); SetTimeOfDay(IniFile.GetValueSetI("General", "TimeInTicks", GetTimeOfDay())); - m_ChunkMap = make_unique<cChunkMap>(this); + m_ChunkMap = cpp14::make_unique<cChunkMap>(this); // preallocate some memory for ticking blocks so we don't need to allocate that often m_BlockTickQueue.reserve(1000); m_BlockTickQueueCopy.reserve(1000); // Simulators: - m_SimulatorManager = make_unique<cSimulatorManager>(*this); + m_SimulatorManager = cpp14::make_unique<cSimulatorManager>(*this); m_WaterSimulator = InitializeFluidSimulator(IniFile, "Water", E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER); m_LavaSimulator = InitializeFluidSimulator(IniFile, "Lava", E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA); - m_SandSimulator = make_unique<cSandSimulator>(*this, IniFile); - m_FireSimulator = make_unique<cFireSimulator>(*this, IniFile); + m_SandSimulator = cpp14::make_unique<cSandSimulator>(*this, IniFile); + m_FireSimulator = cpp14::make_unique<cFireSimulator>(*this, IniFile); m_RedstoneSimulator = InitializeRedstoneSimulator(IniFile); // Water, Lava and Redstone simulators get registered in their initialize function. @@ -642,7 +515,7 @@ void cWorld::Start(void) m_Lighting.Start(this); m_Storage.Start(this, m_StorageSchema, m_StorageCompressionFactor); m_Generator.Start(m_GeneratorCallbacks, m_GeneratorCallbacks, IniFile); - m_ChunkSender.Start(this); + m_ChunkSender.Start(); m_TickThread.Start(); // Init of the spawn monster time (as they are supposed to have different spawn rate) @@ -800,7 +673,7 @@ void cWorld::InitialiseGeneratorDefaults(cIniFile & a_IniFile) a_IniFile.GetValueSet("Generator", "BiomeGen", "Grown"); a_IniFile.GetValueSet("Generator", "ShapeGen", "BiomalNoise3D"); a_IniFile.GetValueSet("Generator", "CompositionGen", "Biomal"); - a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, NaturalPatches, PreSimulator, Animals"); + a_IniFile.GetValueSet("Generator", "Finishers", "RoughRavines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, TallGrass, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, NaturalPatches, PreSimulator, Animals"); break; } case dimNether: @@ -1459,6 +1332,30 @@ bool cWorld::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback +bool cWorld::DoWithChunk(int a_ChunkX, int a_ChunkZ, std::function<bool(cChunk &)> a_Callback) +{ + struct cCallBackWrapper : cChunkCallback + { + cCallBackWrapper(std::function<bool(cChunk &)> a_InnerCallback) : + m_Callback(a_InnerCallback) + { + } + + virtual bool Item(cChunk * a_Chunk) + { + return m_Callback(*a_Chunk); + } + + private: + std::function<bool(cChunk &)> m_Callback; + } callback(a_Callback); + return m_ChunkMap->DoWithChunk(a_ChunkX, a_ChunkZ, callback); +} + + + + + bool cWorld::DoWithChunkAt(Vector3i a_BlockPos, std::function<bool(cChunk &)> a_Callback) { return m_ChunkMap->DoWithChunkAt(a_BlockPos, a_Callback); @@ -1496,9 +1393,23 @@ void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, NIBBLETYPE a_Sapling case E_META_SAPLING_APPLE: GetAppleTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; case E_META_SAPLING_BIRCH: GetBirchTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; case E_META_SAPLING_CONIFER: GetConiferTreeImage(a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; - case E_META_SAPLING_JUNGLE: GetJungleTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; case E_META_SAPLING_ACACIA: GetAcaciaTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; - case E_META_SAPLING_DARK_OAK: GetDarkoakTreeImage(a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; + case E_META_SAPLING_JUNGLE: + { + bool IsLarge = GetLargeTreeAdjustment(*this, a_X, a_Y, a_Z, a_SaplingMeta); + GetJungleTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other, IsLarge); + break; + } + case E_META_SAPLING_DARK_OAK: + { + if (!GetLargeTreeAdjustment(*this, a_X, a_Y, a_Z, a_SaplingMeta)) + { + return; + } + + GetDarkoakTreeImage(a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); + break; + } } Other.insert(Other.begin(), Logs.begin(), Logs.end()); Logs.clear(); @@ -1569,6 +1480,7 @@ void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks) bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal) { + cFastRandom random; BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); @@ -1582,10 +1494,19 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy } if (BlockMeta < 7) { - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + if (!a_IsByBonemeal) + { + ++BlockMeta; + } + else + { + BlockMeta += random.NextInt(4) + 2; + BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); + } + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); BroadcastSoundParticleEffect(2005, a_BlockX, a_BlockY, a_BlockZ, 0); } - return true; + return BlockMeta == 7; } case E_BLOCK_COCOA_POD: @@ -1599,7 +1520,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, (NIBBLETYPE) (GrowState << 2 | TypeMeta)); BroadcastSoundParticleEffect(2005, a_BlockX, a_BlockY, a_BlockZ, 0); } - return true; + return GrowState == 2; } case E_BLOCK_CROPS: @@ -1610,10 +1531,19 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy } if (BlockMeta < 7) { - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + if (!a_IsByBonemeal) + { + ++BlockMeta; + } + else + { + BlockMeta += random.NextInt(4) + 2; + BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); + } + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); BroadcastSoundParticleEffect(2005, a_BlockX, a_BlockY, a_BlockZ, 0); } - return true; + return BlockMeta == 7; } case E_BLOCK_MELON_STEM: @@ -1624,7 +1554,17 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy { return false; } - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + + if (!a_IsByBonemeal) + { + ++BlockMeta; + } + else + { + BlockMeta += random.NextInt(4) + 2; + BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); + } + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); BroadcastSoundParticleEffect(2005, a_BlockX, a_BlockY, a_BlockZ, 0); } else @@ -1635,7 +1575,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy } GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType); } - return true; + return BlockMeta == 7; } case E_BLOCK_POTATOES: @@ -1646,10 +1586,19 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy } if (BlockMeta < 7) { - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + if (!a_IsByBonemeal) + { + ++BlockMeta; + } + else + { + BlockMeta += random.NextInt(4) + 2; + BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); + } + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); BroadcastSoundParticleEffect(2005, a_BlockX, a_BlockY, a_BlockZ, 0); } - return true; + return BlockMeta == 7; } case E_BLOCK_PUMPKIN_STEM: @@ -1660,7 +1609,17 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy { return false; } - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + + if (!a_IsByBonemeal) + { + ++BlockMeta; + } + else + { + BlockMeta += random.NextInt(4) + 2; + BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); + } + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); BroadcastSoundParticleEffect(2005, a_BlockX, a_BlockY, a_BlockZ, 0); } else @@ -1671,7 +1630,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy } GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType); } - return true; + return BlockMeta == 7; } case E_BLOCK_SAPLING: @@ -1680,8 +1639,31 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy { return false; } - GrowTreeFromSapling(a_BlockX, a_BlockY, a_BlockZ, BlockMeta); - return true; + NIBBLETYPE TypeMeta = BlockMeta & 0x07; + int GrowState = BlockMeta >> 3; + + if (GrowState < 1) + { + // Non-bonemeal forces a growth, while bonemeal only has a chance of growing it + if (!a_IsByBonemeal) + { + ++GrowState; + } + else if (random.NextInt(99) < 45) + { + ++GrowState; + } + + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, static_cast<NIBBLETYPE>(GrowState << 3 | TypeMeta)); + BroadcastSoundParticleEffect(2005, a_BlockX, a_BlockY, a_BlockZ, 0); + } + else if (random.NextInt(99) < 45) + { + + GrowTreeFromSapling(a_BlockX, a_BlockY, a_BlockZ, BlockMeta); + return true; + } + return false; } case E_BLOCK_GRASS: @@ -2071,7 +2053,7 @@ void cWorld::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_V -void cWorld::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) +void cWorld::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, Byte a_Byte1, Byte a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) { m_ChunkMap->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType, a_Exclude); } @@ -2134,15 +2116,6 @@ void cWorld::BroadcastChat(const cCompositeChat & a_Message, const cClientHandle -void cWorld::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastChunkData(a_ChunkX, a_ChunkZ, a_Serializer, a_Exclude); -} - - - - - void cWorld::BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude) { m_ChunkMap->BroadcastCollectEntity(a_Entity, a_Player, a_Exclude); @@ -2594,10 +2567,23 @@ void cWorld::SetChunkData(cSetChunkData & a_SetChunkData) // If a client is requesting this chunk, send it to them: int ChunkX = a_SetChunkData.GetChunkX(); int ChunkZ = a_SetChunkData.GetChunkZ(); - if (m_ChunkMap->HasChunkAnyClients(ChunkX, ChunkZ)) - { - m_ChunkSender.ChunkReady(ChunkX, ChunkZ); - } + cChunkSender & ChunkSender = m_ChunkSender; + DoWithChunk( + ChunkX, ChunkZ, + [&ChunkSender] (cChunk & a_Chunk) -> bool + { + if (a_Chunk.HasAnyClients()) + { + ChunkSender.QueueSendChunkTo( + a_Chunk.GetPosX(), + a_Chunk.GetPosZ(), + cChunkSender::E_CHUNK_PRIORITY_MEDIUM, + a_Chunk.GetAllClients() + ); + } + return true; + } + ); // Save the chunk right after generating, so that we don't have to generate it again on next run if (a_SetChunkData.ShouldMarkDirty()) @@ -2680,7 +2666,7 @@ void cWorld::UnloadUnusedChunks(void) void cWorld::QueueUnloadUnusedChunks(void) { - QueueTask(make_unique<cWorld::cTaskUnloadUnusedChunks>()); + QueueTask(cpp14::make_unique<cWorld::cTaskUnloadUnusedChunks>()); } @@ -3005,9 +2991,9 @@ void cWorld::TouchChunk(int a_ChunkX, int a_ChunkZ) -void cWorld::PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter) +void cWorld::PrepareChunk(int a_ChunkX, int a_ChunkZ, std::unique_ptr<cChunkCoordCallback> a_CallAfter) { - m_ChunkMap->PrepareChunk(a_ChunkX, a_ChunkZ, a_CallAfter); + m_ChunkMap->PrepareChunk(a_ChunkX, a_ChunkZ, std::move(a_CallAfter)); } @@ -3097,7 +3083,7 @@ bool cWorld::SetTrapdoorOpen(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_Op return false; } - bool IsOpen = (Meta & 0x4) > 0; + bool IsOpen = (Meta & 0x4) != 0; if (a_Open != IsOpen) { SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta ^ 0x4); @@ -3131,9 +3117,9 @@ void cWorld::GenerateChunk(int a_ChunkX, int a_ChunkZ) -void cWorld::QueueLightChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback) +void cWorld::QueueLightChunk(int a_ChunkX, int a_ChunkZ, std::unique_ptr<cChunkCoordCallback> a_Callback) { - m_Lighting.QueueChunk(a_ChunkX, a_ChunkZ, a_Callback); + m_Lighting.QueueChunk(a_ChunkX, a_ChunkZ, std::move(a_Callback)); } @@ -3184,7 +3170,11 @@ void cWorld::QueueTask(cTaskPtr a_Task) } - +void cWorld::ScheduleTask(int a_DelayTicks, std::function<void (cWorld&)> a_Func) +{ + cTaskLambda task(a_Func); + ScheduleTask(a_DelayTicks, static_cast<cTaskPtr>(std::make_shared<cTaskLambda>(task))); +} void cWorld::ScheduleTask(int a_DelayTicks, cTaskPtr a_Task) @@ -3687,8 +3677,10 @@ void cWorld::cTaskSendBlockToAllPlayers::Run(cWorld & a_World) a_World.ForEachPlayer(PlayerCallback); } - - +void cWorld::cTaskLambda::Run(cWorld & a_World) +{ + m_func(a_World); +} //////////////////////////////////////////////////////////////////////////////// |