From 4c5590636cf4a311f03e735878557b1e7b3362dd Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sun, 11 Aug 2013 20:16:41 +0200 Subject: [PATCH] Each world now ticks in a separate thread. --- source/Root.cpp | 15 +- source/Root.h | 5 +- source/Server.cpp | 2 +- source/Server.h | 2 +- source/World.cpp | 204 ++++++++++++++------------- source/World.h | 9 +- source/WorldStorage/WorldStorage.cpp | 9 ++ source/WorldStorage/WorldStorage.h | 1 + 8 files changed, 135 insertions(+), 112 deletions(-) diff --git a/source/Root.cpp b/source/Root.cpp index 5ec27aa0d..166932cf2 100644 --- a/source/Root.cpp +++ b/source/Root.cpp @@ -270,8 +270,9 @@ void cRoot::LoadWorlds(void) void cRoot::StartWorlds(void) { - for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) + for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) { + itr->second->Start(); itr->second->InitializeSpawn(); } } @@ -282,9 +283,9 @@ void cRoot::StartWorlds(void) void cRoot::StopWorlds(void) { - for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) + for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) { - itr->second->StopThreads(); + itr->second->Stop(); } } @@ -344,7 +345,7 @@ bool cRoot::ForEachWorld(cWorldListCallback & a_Callback) -void cRoot::TickWorlds(float a_Dt) +void cRoot::TickCommands(void) { // Execute any pending commands: cCommandQueue PendingCommands; @@ -356,12 +357,6 @@ void cRoot::TickWorlds(float a_Dt) { ExecuteConsoleCommand(itr->m_Command, *(itr->m_Output)); } - - // Tick the worlds: - for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) - { - itr->second->Tick(a_Dt); - } } diff --git a/source/Root.h b/source/Root.h index 1e2befcd4..262c9b0e5 100644 --- a/source/Root.h +++ b/source/Root.h @@ -85,9 +85,10 @@ public: /// Called by cAuthenticator to auth the specified user void AuthenticateUser(int a_ClientID); - - void TickWorlds(float a_Dt); + /// Executes commands queued in the command queue + void TickCommands(void); + /// Returns the number of chunks loaded int GetTotalChunkCount(void); // tolua_export diff --git a/source/Server.cpp b/source/Server.cpp index 4247a1dfe..0045d4808 100644 --- a/source/Server.cpp +++ b/source/Server.cpp @@ -317,7 +317,7 @@ void cServer::BroadcastChat(const AString & a_Message, const cClientHandle * a_E bool cServer::Tick(float a_Dt) { - cRoot::Get()->TickWorlds(a_Dt); + cRoot::Get()->TickCommands(); cClientHandleList RemoveClients; { diff --git a/source/Server.h b/source/Server.h index 983dc4de8..a00485fa2 100644 --- a/source/Server.h +++ b/source/Server.h @@ -62,7 +62,7 @@ public: // tolua_export /// Binds the built-in console commands with the plugin manager static void BindBuiltInConsoleCommands(void); - void Shutdown(); + void Shutdown(void); void SendMessage(const AString & a_Message, cPlayer * a_Player = NULL, bool a_bExclude = false ); // tolua_export diff --git a/source/World.cpp b/source/World.cpp index ebfee971b..af66d1ead 100644 --- a/source/World.cpp +++ b/source/World.cpp @@ -230,6 +230,7 @@ void cWorld::cTickThread::Execute(void) 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), @@ -244,102 +245,6 @@ cWorld::cWorld(const AString & a_WorldName) : LOGD("cWorld::cWorld(%s)", a_WorldName.c_str()); cMakeDir::MakeDir(m_WorldName.c_str()); - - // 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; - - AString StorageSchema("Default"); - - 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); - 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 - - } - - m_ChunkMap = new cChunkMap(this); - - m_ChunkSender.Start(this); - - m_LastSave = 0; - m_LastUnload = 0; - - // 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 = 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()); - } } @@ -542,10 +447,115 @@ 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�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) +{ + m_TickThread.Stop(); + m_Lighting.Stop(); m_Generator.Stop(); m_ChunkSender.Stop(); + m_Storage.Stop(); } diff --git a/source/World.h b/source/World.h index 1ae56a410..a12a9e40e 100644 --- a/source/World.h +++ b/source/World.h @@ -477,8 +477,12 @@ public: void InitializeSpawn(void); + /// Starts threads that belong to this world + void Start(void); + /// Stops threads that belong to this world (part of deinit) - void StopThreads(void); + void Stop(void); + void TickQueuedBlocks(float a_Dt); struct BlockTickQueueItem @@ -548,6 +552,9 @@ private: AString m_WorldName; AString m_IniFileName; + /// Name of the storage schema used to load and save chunks + AString m_StorageSchema; + /// The dimension of the world, used by the client to provide correct lighting scheme eDimension m_Dimension; diff --git a/source/WorldStorage/WorldStorage.cpp b/source/WorldStorage/WorldStorage.cpp index 8b055b240..7ff5ae8e8 100644 --- a/source/WorldStorage/WorldStorage.cpp +++ b/source/WorldStorage/WorldStorage.cpp @@ -84,6 +84,15 @@ bool cWorldStorage::Start(cWorld * a_World, const AString & a_StorageSchemaName) +void cWorldStorage::Stop(void) +{ + WaitForFinish(); +} + + + + + void cWorldStorage::WaitForFinish(void) { LOG("Waiting for the world storage to finish saving"); diff --git a/source/WorldStorage/WorldStorage.h b/source/WorldStorage/WorldStorage.h index 064b2ffaf..bf8dbd3d5 100644 --- a/source/WorldStorage/WorldStorage.h +++ b/source/WorldStorage/WorldStorage.h @@ -75,6 +75,7 @@ public: void UnqueueSave(const cChunkCoords & a_Chunk); bool Start(cWorld * a_World, const AString & a_StorageSchemaName); // Hide the cIsThread's Start() method, we need to provide args + void Stop(void); // Hide the cIsThread's Stop() method, we need to signal the event void WaitForFinish(void); void WaitForQueuesEmpty(void);