From b12f4ef7d58cfe4d815feb2db1f88f223c7f2a61 Mon Sep 17 00:00:00 2001 From: Lane Kolbly Date: Thu, 7 Sep 2017 07:41:16 -0500 Subject: Made world data paths adjustable, and added API to temporarily disable saving chunks to disk. (#3912) --- src/Entities/Player.cpp | 4 ++-- src/MapManager.cpp | 8 ++++---- src/Root.cpp | 48 ++++++++++++++++++++++++++++++------------- src/Root.h | 3 +++ src/World.cpp | 32 +++++++++++++++++++---------- src/World.h | 18 +++++++++++++++- src/WorldStorage/WSSAnvil.cpp | 6 +++--- 7 files changed, 84 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 614edef75..8d35d0bf5 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -2203,7 +2203,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) // Load the player stats. // We use the default world name (like bukkit) because stats are shared between dimensions / worlds. - cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), GetUUID().ToLongString(), &m_Stats); + cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetDataPath(), GetName(), GetUUID().ToLongString(), &m_Stats); StatSerializer.Load(); LOGD("Player %s was read from file \"%s\", spawning at {%.2f, %.2f, %.2f} in world \"%s\"", @@ -2301,7 +2301,7 @@ bool cPlayer::SaveToDisk() // Save the player stats. // We use the default world name (like bukkit) because stats are shared between dimensions / worlds. - cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), GetUUID().ToLongString(), &m_Stats); + cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetDataPath(), GetName(), GetUUID().ToLongString(), &m_Stats); if (!StatSerializer.Save()) { LOGWARNING("Could not save stats for player %s", GetName().c_str()); diff --git a/src/MapManager.cpp b/src/MapManager.cpp index 2729e67dd..4408af76b 100644 --- a/src/MapManager.cpp +++ b/src/MapManager.cpp @@ -96,7 +96,7 @@ void cMapManager::LoadMapData(void) { cCSLock Lock(m_CS); - cIDCountSerializer IDSerializer(m_World->GetName()); + cIDCountSerializer IDSerializer(m_World->GetDataPath()); if (!IDSerializer.Load()) { @@ -111,7 +111,7 @@ void cMapManager::LoadMapData(void) { cMap Map(i, m_World); - cMapSerializer Serializer(m_World->GetName(), &Map); + cMapSerializer Serializer(m_World->GetDataPath(), &Map); if (!Serializer.Load()) { @@ -135,7 +135,7 @@ void cMapManager::SaveMapData(void) return; } - cIDCountSerializer IDSerializer(m_World->GetName()); + cIDCountSerializer IDSerializer(m_World->GetDataPath()); IDSerializer.SetMapCount(static_cast(m_MapData.size())); @@ -149,7 +149,7 @@ void cMapManager::SaveMapData(void) { cMap & Map = *it; - cMapSerializer Serializer(m_World->GetName(), &Map); + cMapSerializer Serializer(m_World->GetDataPath(), &Map); if (!Serializer.Save()) { diff --git a/src/Root.cpp b/src/Root.cpp index 3e30d8a07..38c95f822 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -396,16 +396,20 @@ void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIn a_Settings.AddValue("Worlds", "DefaultWorld", "world"); a_Settings.AddValue("Worlds", "World", "world_nether"); a_Settings.AddValue("Worlds", "World", "world_the_end"); - m_pDefaultWorld = new cWorld("world"); + a_Settings.AddValue("WorldPaths", "world", "world"); + a_Settings.AddValue("WorldPaths", "world_nether", "world_nether"); + a_Settings.AddValue("WorldPaths", "world_the_end", "world_the_end"); + m_pDefaultWorld = new cWorld("world", "world"); m_WorldsByName["world"] = m_pDefaultWorld; - m_WorldsByName["world_nether"] = new cWorld("world_nether", dimNether, "world"); - m_WorldsByName["world_the_end"] = new cWorld("world_the_end", dimEnd, "world"); + m_WorldsByName["world_nether"] = new cWorld("world_nether", "world_nether", dimNether, "world"); + m_WorldsByName["world_the_end"] = new cWorld("world_the_end", "world_the_end", dimEnd, "world"); return; } // First get the default world AString DefaultWorldName = a_Settings.GetValueSet("Worlds", "DefaultWorld", "world"); - m_pDefaultWorld = new cWorld(DefaultWorldName.c_str()); + AString DefaultWorldPath = a_Settings.GetValueSet("WorldPaths", DefaultWorldName, DefaultWorldName); + m_pDefaultWorld = new cWorld(DefaultWorldName.c_str(), DefaultWorldPath.c_str()); m_WorldsByName[ DefaultWorldName ] = m_pDefaultWorld; auto Worlds = a_Settings.GetValues("Worlds"); @@ -453,10 +457,15 @@ void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIn FoundAdditionalWorlds = true; cWorld * NewWorld; AString LowercaseName = StrToLower(WorldName); + AString WorldPath = a_Settings.GetValueSet("WorldPaths", WorldName, WorldName); AString NetherAppend = "_nether"; AString EndAppend1 = "_the_end"; AString EndAppend2 = "_end"; + // The default world is an overworld with no links + eDimension Dimension = dimOverworld; + AString LinkTo = ""; + // if the world is called x_nether if ((LowercaseName.size() > NetherAppend.size()) && (LowercaseName.substr(LowercaseName.size() - NetherAppend.size()) == NetherAppend)) { @@ -464,12 +473,12 @@ void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIn // otherwise, choose the default world as the linked world. // As before, any ini settings will completely override this if an ini is already present. - AString LinkTo = WorldName.substr(0, WorldName.size() - NetherAppend.size()); + LinkTo = WorldName.substr(0, WorldName.size() - NetherAppend.size()); if (GetWorld(LinkTo) == nullptr) { LinkTo = DefaultWorldName; } - NewWorld = new cWorld(WorldName.c_str(), dimNether, LinkTo); + Dimension = dimNether; } // if the world is called x_the_end else if ((LowercaseName.size() > EndAppend1.size()) && (LowercaseName.substr(LowercaseName.size() - EndAppend1.size()) == EndAppend1)) @@ -478,12 +487,12 @@ void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIn // otherwise, choose the default world as the linked world. // As before, any ini settings will completely override this if an ini is already present. - AString LinkTo = WorldName.substr(0, WorldName.size() - EndAppend1.size()); + LinkTo = WorldName.substr(0, WorldName.size() - EndAppend1.size()); if (GetWorld(LinkTo) == nullptr) { LinkTo = DefaultWorldName; } - NewWorld = new cWorld(WorldName.c_str(), dimEnd, LinkTo); + Dimension = dimEnd; } // if the world is called x_end else if ((LowercaseName.size() > EndAppend2.size()) && (LowercaseName.substr(LowercaseName.size() - EndAppend2.size()) == EndAppend2)) @@ -492,17 +501,14 @@ void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIn // otherwise, choose the default world as the linked world. // As before, any ini settings will completely override this if an ini is already present. - AString LinkTo = WorldName.substr(0, WorldName.size() - EndAppend2.size()); + LinkTo = WorldName.substr(0, WorldName.size() - EndAppend2.size()); if (GetWorld(LinkTo) == nullptr) { LinkTo = DefaultWorldName; } - NewWorld = new cWorld(WorldName.c_str(), dimEnd, LinkTo); - } - else - { - NewWorld = new cWorld(WorldName.c_str()); + Dimension = dimEnd; } + NewWorld = new cWorld(WorldName.c_str(), WorldPath.c_str(), Dimension, LinkTo); m_WorldsByName[WorldName] = NewWorld; } // for i - Worlds @@ -709,6 +715,20 @@ void cRoot::SaveAllChunks(void) + + +void cRoot::SetSavingEnabled(bool a_SavingEnabled) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) + { + itr->second->SetSavingEnabled(a_SavingEnabled); + } +} + + + + + void cRoot::SendPlayerLists(cPlayer * a_DestPlayer) { for (const auto & itr : m_WorldsByName) diff --git a/src/Root.h b/src/Root.h index 44567018d..46b202c88 100644 --- a/src/Root.h +++ b/src/Root.h @@ -135,6 +135,9 @@ public: /** Saves all chunks in all worlds */ void SaveAllChunks(void); // tolua_export + /** Sets whether saving chunks is enabled in all worlds (overrides however the worlds were already set) */ + void SetSavingEnabled(bool a_SavingEnabled); // tolua_export + /** Calls the callback for each player in all worlds */ bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << diff --git a/src/World.cpp b/src/World.cpp index f610d4d2e..e2ac24e71 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -120,16 +120,18 @@ void cWorld::cTickThread::Execute(void) //////////////////////////////////////////////////////////////////////////////// // cWorld: -cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AString & a_LinkedOverworldName) : +cWorld::cWorld(const AString & a_WorldName, const AString & a_DataPath, eDimension a_Dimension, const AString & a_LinkedOverworldName) : m_WorldName(a_WorldName), + m_DataPath(a_DataPath), m_LinkedOverworldName(a_LinkedOverworldName), - m_IniFileName(m_WorldName + "/world.ini"), + m_IniFileName(m_DataPath + "/world.ini"), m_StorageSchema("Default"), #ifdef __arm__ m_StorageCompressionFactor(0), #else m_StorageCompressionFactor(6), #endif + m_IsSavingEnabled(true), m_Dimension(a_Dimension), m_IsSpawnExplicitlySet(false), m_SpawnX(0), @@ -194,10 +196,10 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin { LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str()); - cFile::CreateFolder(FILE_IO_PREFIX + m_WorldName); + cFile::CreateFolderRecursive(FILE_IO_PREFIX + m_DataPath); // Load the scoreboard - cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard); + cScoreboardSerializer Serializer(m_DataPath, &m_Scoreboard); Serializer.Load(); } @@ -213,11 +215,14 @@ cWorld::~cWorld() m_Storage.WaitForFinish(); - // Unload the scoreboard - cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard); - Serializer.Save(); + if (IsSavingEnabled()) + { + // Unload the scoreboard + cScoreboardSerializer Serializer(m_DataPath, &m_Scoreboard); + Serializer.Save(); - m_MapManager.SaveMapData(); + m_MapManager.SaveMapData(); + } // Explicitly destroy the chunkmap, so that it's guaranteed to be destroyed before the other internals // This fixes crashes on stopping the server, because chunk destructor deletes entities and those access the world. @@ -2965,7 +2970,9 @@ void cWorld::SetChunkData(cSetChunkData & a_SetChunkData) ); // Save the chunk right after generating, so that we don't have to generate it again on next run - if (a_SetChunkData.ShouldMarkDirty()) + // If saving is disabled, then the chunk was marked dirty so it will get + // saved if saving is later enabled. + if (a_SetChunkData.ShouldMarkDirty() && IsSavingEnabled()) { m_Storage.QueueSaveChunk(ChunkX, ChunkZ); } @@ -3629,8 +3636,11 @@ bool cWorld::ForEachLoadedChunk(std::function a_Callback) void cWorld::SaveAllChunks(void) { - m_LastSave = std::chrono::duration_cast(m_WorldAge); - m_ChunkMap->SaveAllChunks(); + if (IsSavingEnabled()) + { + m_LastSave = std::chrono::duration_cast(m_WorldAge); + m_ChunkMap->SaveAllChunks(); + } } diff --git a/src/World.h b/src/World.h index 87110a5cf..22847b975 100644 --- a/src/World.h +++ b/src/World.h @@ -12,6 +12,7 @@ #include "ChunkSender.h" #include "Defines.h" #include "LightingThread.h" +#include "IniFile.h" #include "Item.h" #include "Mobs/Monster.h" #include "Entities/ProjectileEntity.h" @@ -103,6 +104,12 @@ public: // tolua_begin + /** Get whether saving chunks is enabled */ + bool IsSavingEnabled(void) const { return m_IsSavingEnabled; } + + /** Set whether saving chunks is enabled */ + void SetSavingEnabled(bool a_IsSavingEnabled) { m_IsSavingEnabled = a_IsSavingEnabled; } + int GetTicksUntilWeatherChange(void) const { return m_WeatherInterval; } /** Is the daylight cycle enabled? */ @@ -657,6 +664,9 @@ public: /** Returns the name of the world */ const AString & GetName(void) const { return m_WorldName; } + /** Returns the data path to the world data */ + const AString & GetDataPath(void) const { return m_DataPath; } + /** Returns the name of the world.ini file used by this world */ const AString & GetIniFileName(void) const {return m_IniFileName; } @@ -900,6 +910,9 @@ private: AString m_WorldName; + /** The path to the root directory for the world files. Does not including trailing path specifier. */ + AString m_DataPath; + /** The name of the overworld that portals in this world should link to. Only has effect if this world is a Nether or End world. */ AString m_LinkedOverworldName; @@ -911,6 +924,9 @@ private: int m_StorageCompressionFactor; + /** Whether or not writing chunks to disk is currently enabled */ + std::atomic m_IsSavingEnabled; + /** The dimension of the world, used by the client to provide correct lighting scheme */ eDimension m_Dimension; @@ -1065,7 +1081,7 @@ private: cSetChunkDataPtrs m_SetChunkDataQueue; - cWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_LinkedOverworldName = ""); + cWorld(const AString & a_WorldName, const AString & a_DataPath, eDimension a_Dimension = dimOverworld, const AString & a_LinkedOverworldName = ""); virtual ~cWorld() override; void Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec); diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 8f4d41598..158f7a819 100755 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -85,7 +85,7 @@ cWSSAnvil::cWSSAnvil(cWorld * a_World, int a_CompressionFactor) : { // Create a level.dat file for mapping tools, if it doesn't already exist: AString fnam; - Printf(fnam, "%s%clevel.dat", a_World->GetName().c_str(), cFile::PathSeparator); + Printf(fnam, "%s%clevel.dat", a_World->GetDataPath().c_str(), cFile::PathSeparator); if (!cFile::Exists(fnam)) { cFastNBTWriter Writer; @@ -180,7 +180,7 @@ void cWSSAnvil::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ, const AString & a_Re { // Construct the filename for offloading: AString OffloadFileName; - Printf(OffloadFileName, "%s%cregion%cbadchunks", m_World->GetName().c_str(), cFile::PathSeparator, cFile::PathSeparator); + Printf(OffloadFileName, "%s%cregion%cbadchunks", m_World->GetDataPath().c_str(), cFile::PathSeparator, cFile::PathSeparator); cFile::CreateFolder(FILE_IO_PREFIX + OffloadFileName); auto t = time(nullptr); struct tm stm; @@ -286,7 +286,7 @@ cWSSAnvil::cMCAFile * cWSSAnvil::LoadMCAFile(const cChunkCoords & a_Chunk) // Load it anew: AString FileName; - Printf(FileName, "%s%cregion", m_World->GetName().c_str(), cFile::PathSeparator); + Printf(FileName, "%s%cregion", m_World->GetDataPath().c_str(), cFile::PathSeparator); cFile::CreateFolder(FILE_IO_PREFIX + FileName); AppendPrintf(FileName, "/r.%d.%d.mca", RegionX, RegionZ); cMCAFile * f = new cMCAFile(*this, FileName, RegionX, RegionZ); -- cgit v1.2.3