Merge pull request #93 from mc-server/PerWorldThreads
Per world threads
This commit is contained in:
418
source/World.cpp
418
source/World.cpp
@@ -1,3 +1,4 @@
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "BlockID.h"
|
||||
@@ -128,6 +129,9 @@ protected:
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cWorldLightingProgress:
|
||||
|
||||
/// A simple thread that displays the progress of world lighting in cWorld::InitializeSpawn()
|
||||
class cWorldLightingProgress :
|
||||
public cIsThread
|
||||
@@ -187,10 +191,47 @@ cWorld::cLock::cLock(cWorld & a_World) :
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cWorld::cTickThread:
|
||||
|
||||
cWorld::cTickThread::cTickThread(cWorld & a_World) :
|
||||
super(Printf("WorldTickThread: %s", a_World.GetName().c_str())),
|
||||
m_World(a_World)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cWorld::cTickThread::Execute(void)
|
||||
{
|
||||
const int ClocksPerTick = CLOCKS_PER_SEC / 20;
|
||||
clock_t LastTime = clock();
|
||||
while (!m_ShouldTerminate)
|
||||
{
|
||||
clock_t Start = clock();
|
||||
m_World.Tick((float)(1000 * (Start - LastTime)) / CLOCKS_PER_SEC);
|
||||
clock_t Now = clock();
|
||||
if (Now - Start < ClocksPerTick)
|
||||
{
|
||||
cSleep::MilliSleep(1000 * (ClocksPerTick - (Now - Start)) / CLOCKS_PER_SEC);
|
||||
}
|
||||
LastTime = Start;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cWorld:
|
||||
|
||||
cWorld::cWorld(const AString & a_WorldName) :
|
||||
m_WorldName(a_WorldName),
|
||||
m_IniFileName(m_WorldName + "/world.ini"),
|
||||
m_StorageSchema("Default"),
|
||||
m_WorldAgeSecs(0),
|
||||
m_TimeOfDaySecs(0),
|
||||
m_WorldAge(0),
|
||||
@@ -199,109 +240,12 @@ cWorld::cWorld(const AString & a_WorldName) :
|
||||
m_LastSpawnMonster(0),
|
||||
m_RSList(0),
|
||||
m_Weather(eWeather_Sunny),
|
||||
m_WeatherInterval(24000) // Guaranteed 1 day of sunshine at server start :)
|
||||
m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :)
|
||||
m_TickThread(*this)
|
||||
{
|
||||
LOGD("cWorld::cWorld(%s)", a_WorldName.c_str());
|
||||
m_WorldName = a_WorldName;
|
||||
m_IniFileName = m_WorldName + "/world.ini";
|
||||
LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str());
|
||||
|
||||
cMakeDir::MakeDir(m_WorldName.c_str());
|
||||
|
||||
MTRand r1;
|
||||
m_SpawnX = (double)((r1.randInt() % 1000) - 500);
|
||||
m_SpawnY = cChunkDef::Height;
|
||||
m_SpawnZ = (double)((r1.randInt() % 1000) - 500);
|
||||
m_GameMode = eGameMode_Creative;
|
||||
|
||||
AString StorageSchema("Default");
|
||||
|
||||
cIniFile IniFile(m_IniFileName);
|
||||
IniFile.ReadFile();
|
||||
AString Dimension = IniFile.GetValueSet("General", "Dimension", "Overworld");
|
||||
m_Dimension = StringToDimension(Dimension);
|
||||
switch (m_Dimension)
|
||||
{
|
||||
case dimNether:
|
||||
case dimOverworld:
|
||||
case dimEnd:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
LOGWARNING("Unknown dimension: \"%s\". Setting to Overworld", Dimension.c_str());
|
||||
m_Dimension = dimOverworld;
|
||||
break;
|
||||
}
|
||||
} // switch (m_Dimension)
|
||||
m_SpawnX = IniFile.GetValueSetF("SpawnPosition", "X", m_SpawnX);
|
||||
m_SpawnY = IniFile.GetValueSetF("SpawnPosition", "Y", m_SpawnY);
|
||||
m_SpawnZ = IniFile.GetValueSetF("SpawnPosition", "Z", m_SpawnZ);
|
||||
StorageSchema = IniFile.GetValueSet ("Storage", "Schema", StorageSchema);
|
||||
m_MaxCactusHeight = IniFile.GetValueSetI("Plants", "MaxCactusHeight", 3);
|
||||
m_MaxSugarcaneHeight = IniFile.GetValueSetI("Plants", "MaxSugarcaneHeight", 3);
|
||||
m_IsCactusBonemealable = IniFile.GetValueSetB("Plants", "IsCactusBonemealable", false);
|
||||
m_IsCarrotsBonemealable = IniFile.GetValueSetB("Plants", "IsCarrotsBonemealable", true);
|
||||
m_IsCropsBonemealable = IniFile.GetValueSetB("Plants", "IsCropsBonemealable", true);
|
||||
m_IsGrassBonemealable = IniFile.GetValueSetB("Plants", "IsGrassBonemealable", true);
|
||||
m_IsMelonStemBonemealable = IniFile.GetValueSetB("Plants", "IsMelonStemBonemealable", true);
|
||||
m_IsMelonBonemealable = IniFile.GetValueSetB("Plants", "IsMelonBonemealable", false);
|
||||
m_IsPotatoesBonemealable = IniFile.GetValueSetB("Plants", "IsPotatoesBonemealable", true);
|
||||
m_IsPumpkinStemBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinStemBonemealable", true);
|
||||
m_IsPumpkinBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinBonemealable", false);
|
||||
m_IsSaplingBonemealable = IniFile.GetValueSetB("Plants", "IsSaplingBonemealable", true);
|
||||
m_IsSugarcaneBonemealable = IniFile.GetValueSetB("Plants", "IsSugarcaneBonemealable", false);
|
||||
m_bEnabledPVP = IniFile.GetValueSetB("PVP", "Enabled", true);
|
||||
m_IsDeepSnowEnabled = IniFile.GetValueSetB("Physics", "DeepSnow", false);
|
||||
|
||||
m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode);
|
||||
|
||||
m_Lighting.Start(this);
|
||||
m_Storage.Start(this, StorageSchema);
|
||||
m_Generator.Start(this, IniFile);
|
||||
|
||||
m_bAnimals = true;
|
||||
m_SpawnMonsterRate = 200; // 1 mob each 10 seconds
|
||||
cIniFile IniFile2("settings.ini");
|
||||
if (IniFile2.ReadFile())
|
||||
{
|
||||
m_bAnimals = IniFile2.GetValueB("Monsters", "AnimalsOn", true);
|
||||
m_SpawnMonsterRate = (Int64)(IniFile2.GetValueF("Monsters", "AnimalSpawnInterval", 10) * 20); // Convert from secs to ticks
|
||||
|
||||
// TODO: Move this into cServer instead:
|
||||
SetMaxPlayers(IniFile2.GetValueI("Server", "MaxPlayers", 100));
|
||||
m_Description = IniFile2.GetValue("Server", "Description", "MCServer! - In C++!").c_str();
|
||||
}
|
||||
|
||||
m_ChunkMap = new cChunkMap(this);
|
||||
|
||||
m_ChunkSender.Start(this);
|
||||
|
||||
m_LastSave = 0;
|
||||
m_LastUnload = 0;
|
||||
|
||||
// preallocate some memory for ticking blocks so we don<6F>t need to allocate that often
|
||||
m_BlockTickQueue.reserve(1000);
|
||||
m_BlockTickQueueCopy.reserve(1000);
|
||||
|
||||
// Simulators:
|
||||
m_SimulatorManager = new 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 = new cSandSimulator(*this, IniFile);
|
||||
m_FireSimulator = new cFireSimulator(*this, IniFile);
|
||||
m_RedstoneSimulator = new cRedstoneSimulator(*this);
|
||||
|
||||
// Water and Lava simulators get registered in InitializeFluidSimulator()
|
||||
m_SimulatorManager->RegisterSimulator(m_SandSimulator, 1);
|
||||
m_SimulatorManager->RegisterSimulator(m_FireSimulator, 1);
|
||||
m_SimulatorManager->RegisterSimulator(m_RedstoneSimulator, 1);
|
||||
|
||||
// Save any changes that the defaults may have done to the ini file:
|
||||
if (!IniFile.WriteFile())
|
||||
{
|
||||
LOGWARNING("Could not write world config to %s", m_IniFileName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -504,10 +448,126 @@ void cWorld::InitializeSpawn(void)
|
||||
|
||||
|
||||
|
||||
void cWorld::StopThreads(void)
|
||||
void cWorld::Start(void)
|
||||
{
|
||||
// TODO: Find a proper spawn location, based on the biomes (not in ocean)
|
||||
m_SpawnX = (double)((m_TickRand.randInt() % 1000) - 500);
|
||||
m_SpawnY = cChunkDef::Height;
|
||||
m_SpawnZ = (double)((m_TickRand.randInt() % 1000) - 500);
|
||||
m_GameMode = eGameMode_Creative;
|
||||
|
||||
cIniFile IniFile(m_IniFileName);
|
||||
if (!IniFile.ReadFile())
|
||||
{
|
||||
LOGWARNING("Cannot read world settings from \"%s\", defaults will be used.", m_IniFileName.c_str());
|
||||
}
|
||||
AString Dimension = IniFile.GetValueSet("General", "Dimension", "Overworld");
|
||||
m_Dimension = StringToDimension(Dimension);
|
||||
switch (m_Dimension)
|
||||
{
|
||||
case dimNether:
|
||||
case dimOverworld:
|
||||
case dimEnd:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
LOGWARNING("Unknown dimension: \"%s\". Setting to Overworld", Dimension.c_str());
|
||||
m_Dimension = dimOverworld;
|
||||
break;
|
||||
}
|
||||
} // switch (m_Dimension)
|
||||
m_SpawnX = IniFile.GetValueSetF("SpawnPosition", "X", m_SpawnX);
|
||||
m_SpawnY = IniFile.GetValueSetF("SpawnPosition", "Y", m_SpawnY);
|
||||
m_SpawnZ = IniFile.GetValueSetF("SpawnPosition", "Z", m_SpawnZ);
|
||||
m_StorageSchema = IniFile.GetValueSet ("Storage", "Schema", m_StorageSchema);
|
||||
m_MaxCactusHeight = IniFile.GetValueSetI("Plants", "MaxCactusHeight", 3);
|
||||
m_MaxSugarcaneHeight = IniFile.GetValueSetI("Plants", "MaxSugarcaneHeight", 3);
|
||||
m_IsCactusBonemealable = IniFile.GetValueSetB("Plants", "IsCactusBonemealable", false);
|
||||
m_IsCarrotsBonemealable = IniFile.GetValueSetB("Plants", "IsCarrotsBonemealable", true);
|
||||
m_IsCropsBonemealable = IniFile.GetValueSetB("Plants", "IsCropsBonemealable", true);
|
||||
m_IsGrassBonemealable = IniFile.GetValueSetB("Plants", "IsGrassBonemealable", true);
|
||||
m_IsMelonStemBonemealable = IniFile.GetValueSetB("Plants", "IsMelonStemBonemealable", true);
|
||||
m_IsMelonBonemealable = IniFile.GetValueSetB("Plants", "IsMelonBonemealable", false);
|
||||
m_IsPotatoesBonemealable = IniFile.GetValueSetB("Plants", "IsPotatoesBonemealable", true);
|
||||
m_IsPumpkinStemBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinStemBonemealable", true);
|
||||
m_IsPumpkinBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinBonemealable", false);
|
||||
m_IsSaplingBonemealable = IniFile.GetValueSetB("Plants", "IsSaplingBonemealable", true);
|
||||
m_IsSugarcaneBonemealable = IniFile.GetValueSetB("Plants", "IsSugarcaneBonemealable", false);
|
||||
m_bEnabledPVP = IniFile.GetValueSetB("PVP", "Enabled", true);
|
||||
m_IsDeepSnowEnabled = IniFile.GetValueSetB("Physics", "DeepSnow", false);
|
||||
|
||||
m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode);
|
||||
|
||||
m_bAnimals = true;
|
||||
m_SpawnMonsterRate = 200; // 1 mob each 10 seconds
|
||||
cIniFile IniFile2("settings.ini");
|
||||
if (IniFile2.ReadFile())
|
||||
{
|
||||
m_bAnimals = IniFile2.GetValueB("Monsters", "AnimalsOn", true);
|
||||
m_SpawnMonsterRate = (Int64)(IniFile2.GetValueF("Monsters", "AnimalSpawnInterval", 10) * 20); // Convert from secs to ticks
|
||||
|
||||
}
|
||||
|
||||
m_ChunkMap = new cChunkMap(this);
|
||||
|
||||
m_LastSave = 0;
|
||||
m_LastUnload = 0;
|
||||
|
||||
// preallocate some memory for ticking blocks so we don<6F>t need to allocate that often
|
||||
m_BlockTickQueue.reserve(1000);
|
||||
m_BlockTickQueueCopy.reserve(1000);
|
||||
|
||||
// Simulators:
|
||||
m_SimulatorManager = new 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 = new cSandSimulator(*this, IniFile);
|
||||
m_FireSimulator = new cFireSimulator(*this, IniFile);
|
||||
m_RedstoneSimulator = new cRedstoneSimulator(*this);
|
||||
|
||||
// Water and Lava simulators get registered in InitializeFluidSimulator()
|
||||
m_SimulatorManager->RegisterSimulator(m_SandSimulator, 1);
|
||||
m_SimulatorManager->RegisterSimulator(m_FireSimulator, 1);
|
||||
m_SimulatorManager->RegisterSimulator(m_RedstoneSimulator, 1);
|
||||
|
||||
m_Lighting.Start(this);
|
||||
m_Storage.Start(this, m_StorageSchema);
|
||||
m_Generator.Start(this, IniFile);
|
||||
m_ChunkSender.Start(this);
|
||||
m_TickThread.Start();
|
||||
|
||||
// Save any changes that the defaults may have done to the ini file:
|
||||
if (!IniFile.WriteFile())
|
||||
{
|
||||
LOGWARNING("Could not write world config to %s", m_IniFileName.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cWorld::Stop(void)
|
||||
{
|
||||
// Delete the clients that have been in this world:
|
||||
{
|
||||
cCSLock Lock(m_CSClients);
|
||||
for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr)
|
||||
{
|
||||
(*itr)->Destroy();
|
||||
delete *itr;
|
||||
} // for itr - m_Clients[]
|
||||
m_Clients.clear();
|
||||
}
|
||||
|
||||
m_TickThread.Stop();
|
||||
m_Lighting.Stop();
|
||||
m_Generator.Stop();
|
||||
m_ChunkSender.Stop();
|
||||
m_Storage.Stop();
|
||||
}
|
||||
|
||||
|
||||
@@ -529,7 +589,7 @@ void cWorld::Tick(float a_Dt)
|
||||
m_WorldAge = (Int64)(m_WorldAgeSecs * 20.0);
|
||||
m_TimeOfDay = (Int64)(m_TimeOfDaySecs * 20.0);
|
||||
|
||||
// Broadcase time update every 40 ticks (2 seconds)
|
||||
// Broadcast time update every 40 ticks (2 seconds)
|
||||
if (m_LastTimeUpdate < m_WorldAge - 40)
|
||||
{
|
||||
BroadcastTimeUpdate();
|
||||
@@ -538,7 +598,9 @@ void cWorld::Tick(float a_Dt)
|
||||
|
||||
m_ChunkMap->Tick(a_Dt);
|
||||
|
||||
TickClients(a_Dt);
|
||||
TickQueuedBlocks(a_Dt);
|
||||
TickQueuedTasks();
|
||||
|
||||
GetSimulatorManager()->Simulate(a_Dt);
|
||||
|
||||
@@ -740,6 +802,78 @@ void cWorld::TickSpawnMobs(float a_Dt)
|
||||
|
||||
|
||||
|
||||
void cWorld::TickQueuedTasks(void)
|
||||
{
|
||||
// Make a copy of the tasks to avoid deadlocks on accessing m_Tasks
|
||||
cTasks Tasks;
|
||||
{
|
||||
cCSLock Lock(m_CSTasks);
|
||||
std::swap(Tasks, m_Tasks);
|
||||
}
|
||||
|
||||
// Execute and delete each task:
|
||||
for (cTasks::iterator itr = m_Tasks.begin(), end = m_Tasks.end(); itr != end; ++itr)
|
||||
{
|
||||
(*itr)->Run(*this);
|
||||
delete *itr;
|
||||
} // for itr - m_Tasks[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cWorld::TickClients(float a_Dt)
|
||||
{
|
||||
cClientHandleList RemoveClients;
|
||||
{
|
||||
cCSLock Lock(m_CSClients);
|
||||
|
||||
// Remove clients scheduled for removal:
|
||||
for (cClientHandleList::iterator itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr)
|
||||
{
|
||||
m_Clients.remove(*itr);
|
||||
} // for itr - m_ClientsToRemove[]
|
||||
m_ClientsToRemove.clear();
|
||||
|
||||
// Add clients scheduled for adding:
|
||||
for (cClientHandleList::iterator itr = m_ClientsToAdd.begin(), end = m_ClientsToAdd.end(); itr != end; ++itr)
|
||||
{
|
||||
if (std::find(m_Clients.begin(), m_Clients.end(), *itr) != m_Clients.end())
|
||||
{
|
||||
ASSERT(!"Adding a client that is already in the clientlist");
|
||||
continue;
|
||||
}
|
||||
m_Clients.push_back(*itr);
|
||||
} // for itr - m_ClientsToRemove[]
|
||||
m_ClientsToAdd.clear();
|
||||
|
||||
// Tick the clients, take out those that have been destroyed into RemoveClients
|
||||
for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();)
|
||||
{
|
||||
if ((*itr)->IsDestroyed())
|
||||
{
|
||||
// Remove the client later, when CS is not held, to avoid deadlock
|
||||
RemoveClients.push_back(*itr);
|
||||
itr = m_Clients.erase(itr);
|
||||
continue;
|
||||
}
|
||||
(*itr)->Tick(a_Dt);
|
||||
++itr;
|
||||
} // for itr - m_Clients[]
|
||||
}
|
||||
|
||||
// Delete the clients that have been destroyed
|
||||
for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr)
|
||||
{
|
||||
delete *itr;
|
||||
} // for itr - RemoveClients[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cWorld::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
return m_ChunkMap->WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ);
|
||||
@@ -1900,28 +2034,24 @@ void cWorld::CollectPickupsByPlayer(cPlayer * a_Player)
|
||||
|
||||
|
||||
|
||||
void cWorld::SetMaxPlayers(int iMax)
|
||||
{
|
||||
m_MaxPlayers = MAX_PLAYERS;
|
||||
if (iMax > 0 && iMax < MAX_PLAYERS)
|
||||
{
|
||||
m_MaxPlayers = iMax;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cWorld::AddPlayer(cPlayer * a_Player)
|
||||
{
|
||||
cCSLock Lock(m_CSPlayers);
|
||||
|
||||
ASSERT(std::find(m_Players.begin(), m_Players.end(), a_Player) == m_Players.end()); // Is it already in the list? HOW?
|
||||
|
||||
m_Players.remove(a_Player); // Make sure the player is registered only once
|
||||
m_Players.push_back(a_Player);
|
||||
{
|
||||
cCSLock Lock(m_CSPlayers);
|
||||
|
||||
ASSERT(std::find(m_Players.begin(), m_Players.end(), a_Player) == m_Players.end()); // Is it already in the list? HOW?
|
||||
|
||||
m_Players.remove(a_Player); // Make sure the player is registered only once
|
||||
m_Players.push_back(a_Player);
|
||||
}
|
||||
|
||||
// Add the player's client to the list of clients to be ticked:
|
||||
if (a_Player->GetClientHandle() != NULL)
|
||||
{
|
||||
cCSLock Lock(m_CSClients);
|
||||
m_ClientsToAdd.push_back(a_Player->GetClientHandle());
|
||||
}
|
||||
|
||||
// The player has already been added to the chunkmap as the entity, do NOT add again!
|
||||
}
|
||||
|
||||
@@ -1932,8 +2062,17 @@ void cWorld::AddPlayer(cPlayer * a_Player)
|
||||
void cWorld::RemovePlayer(cPlayer * a_Player)
|
||||
{
|
||||
m_ChunkMap->RemoveEntity(a_Player);
|
||||
cCSLock Lock(m_CSPlayers);
|
||||
m_Players.remove(a_Player);
|
||||
{
|
||||
cCSLock Lock(m_CSPlayers);
|
||||
m_Players.remove(a_Player);
|
||||
}
|
||||
|
||||
// Remove the player's client from the list of clients to be ticked:
|
||||
if (a_Player->GetClientHandle() != NULL)
|
||||
{
|
||||
cCSLock Lock(m_CSClients);
|
||||
m_ClientsToRemove.push_back(a_Player->GetClientHandle());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2279,6 +2418,25 @@ void cWorld::SaveAllChunks(void)
|
||||
|
||||
|
||||
|
||||
void cWorld::QueueSaveAllChunks(void)
|
||||
{
|
||||
QueueTask(new cWorld::cTaskSaveAllChunks);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cWorld::QueueTask(cTask * a_Task)
|
||||
{
|
||||
cCSLock Lock(m_CSTasks);
|
||||
m_Tasks.push_back(a_Task);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cWorld::AddEntity(cEntity * a_Entity)
|
||||
{
|
||||
m_ChunkMap->AddEntity(a_Entity);
|
||||
@@ -2306,11 +2464,13 @@ void cWorld::RemoveEntity(cEntity * a_Entity)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
unsigned int cWorld::GetNumPlayers(void)
|
||||
{
|
||||
cCSLock Lock(m_CSPlayers);
|
||||
return m_Players.size();
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -2522,3 +2682,15 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cWorld::cTaskSaveAllChunks:
|
||||
|
||||
void cWorld::cTaskSaveAllChunks::Run(cWorld & a_World)
|
||||
{
|
||||
a_World.SaveAllChunks();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user