From 1537ebed6fa880ca8f9df92e5ac99324375b91a5 Mon Sep 17 00:00:00 2001 From: peterbell10 Date: Fri, 22 Sep 2017 16:55:42 +0100 Subject: cWorld: Move Initialization from Start to the constructor. Start now does nothing more than launch the world's threads. --- src/Generating/ChunkGenerator.cpp | 5 +- src/Generating/ChunkGenerator.h | 6 +- src/Root.cpp | 29 ++- src/Root.h | 2 +- src/World.cpp | 442 +++++++++++++++++++------------------- src/World.h | 16 +- 6 files changed, 261 insertions(+), 239 deletions(-) diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp index 8c0997d6b..d66838f9e 100644 --- a/src/Generating/ChunkGenerator.cpp +++ b/src/Generating/ChunkGenerator.cpp @@ -47,7 +47,7 @@ cChunkGenerator::~cChunkGenerator() -bool cChunkGenerator::Start(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile) +bool cChunkGenerator::Initialize(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile) { m_PluginInterface = &a_PluginInterface; m_ChunkSink = &a_ChunkSink; @@ -86,8 +86,7 @@ bool cChunkGenerator::Start(cPluginInterface & a_PluginInterface, cChunkSink & a } m_Generator->Initialize(a_IniFile); - - return super::Start(); + return true; } diff --git a/src/Generating/ChunkGenerator.h b/src/Generating/ChunkGenerator.h index cbbe1d1c7..c4be505da 100644 --- a/src/Generating/ChunkGenerator.h +++ b/src/Generating/ChunkGenerator.h @@ -33,7 +33,7 @@ class cChunkDesc; class cChunkGenerator : - cIsThread + public cIsThread { typedef cIsThread super; @@ -110,7 +110,9 @@ public: cChunkGenerator (void); virtual ~cChunkGenerator() override; - bool Start(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile); + /** Read settings from the ini file and initialize in preperation for being started. */ + bool Initialize(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile); + void Stop(void); /** Queues the chunk for generation diff --git a/src/Root.cpp b/src/Root.cpp index 5c461a9ac..cdc25b2a0 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -213,7 +213,7 @@ void cRoot::Start(std::unique_ptr a_OverridesRepo) m_BrewingRecipes.reset(new cBrewingRecipes()); LOGD("Loading worlds..."); - LoadWorlds(*settingsRepo, IsNewIniFile); + LoadWorlds(dd, *settingsRepo, IsNewIniFile); LOGD("Loading plugin manager..."); m_PluginManager = new cPluginManager(dd); @@ -397,7 +397,7 @@ void cRoot::LoadGlobalSettings() -void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIniFile) +void cRoot::LoadWorlds(cDeadlockDetect & a_dd, cSettingsRepositoryInterface & a_Settings, bool a_IsNewIniFile) { if (a_IsNewIniFile) { @@ -407,19 +407,28 @@ void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIn 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"); + + AStringVector WorldNames{ "world", "world_nether", "world_the_end" }; + m_pDefaultWorld = new cWorld("world", "world", a_dd, WorldNames); m_WorldsByName["world"] = m_pDefaultWorld; - 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"); + m_WorldsByName["world_nether"] = new cWorld("world_nether", "world_nether", a_dd, WorldNames, dimNether, "world"); + m_WorldsByName["world_the_end"] = new cWorld("world_the_end", "world_the_end", a_dd, WorldNames, dimEnd, "world"); return; } - // First get the default world + // Build a list of all world names + auto Worlds = a_Settings.GetValues("Worlds"); + AStringVector WorldNames(Worlds.size()); + for (const auto & World : Worlds) + { + WorldNames.push_back(World.second); + } + + // Get the default world AString DefaultWorldName = a_Settings.GetValueSet("Worlds", "DefaultWorld", "world"); AString DefaultWorldPath = a_Settings.GetValueSet("WorldPaths", DefaultWorldName, DefaultWorldName); - m_pDefaultWorld = new cWorld(DefaultWorldName.c_str(), DefaultWorldPath.c_str()); + m_pDefaultWorld = new cWorld(DefaultWorldName.c_str(), DefaultWorldPath.c_str(), a_dd, WorldNames); m_WorldsByName[ DefaultWorldName ] = m_pDefaultWorld; - auto Worlds = a_Settings.GetValues("Worlds"); // Then load the other worlds if (Worlds.size() <= 0) @@ -516,7 +525,7 @@ void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIn } Dimension = dimEnd; } - NewWorld = new cWorld(WorldName.c_str(), WorldPath.c_str(), Dimension, LinkTo); + NewWorld = new cWorld(WorldName.c_str(), WorldPath.c_str(), a_dd, WorldNames, Dimension, LinkTo); m_WorldsByName[WorldName] = NewWorld; } // for i - Worlds @@ -538,7 +547,7 @@ void cRoot::StartWorlds(cDeadlockDetect & a_DeadlockDetect) { for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) { - itr->second->Start(a_DeadlockDetect); + itr->second->Start(); itr->second->InitializeSpawn(); m_PluginManager->CallHookWorldStarted(*itr->second); } diff --git a/src/Root.h b/src/Root.h index 4f191cf35..0735d9493 100644 --- a/src/Root.h +++ b/src/Root.h @@ -228,7 +228,7 @@ private: void LoadGlobalSettings(); /** Loads the worlds from settings.ini, creates the worldmap */ - void LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIniFile); + void LoadWorlds(cDeadlockDetect & a_dd, cSettingsRepositoryInterface & a_Settings, bool a_IsNewIniFile); /** Starts each world's life */ void StartWorlds(cDeadlockDetect & a_DeadlockDetect); diff --git a/src/World.cpp b/src/World.cpp index 48c26da5a..d86329395 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -120,7 +120,11 @@ void cWorld::cTickThread::Execute(void) //////////////////////////////////////////////////////////////////////////////// // cWorld: -cWorld::cWorld(const AString & a_WorldName, const AString & a_DataPath, eDimension a_Dimension, const AString & a_LinkedOverworldName) : +cWorld::cWorld( + const AString & a_WorldName, const AString & a_DataPath, + cDeadlockDetect & a_DeadlockDetect, const AStringVector & a_WorldNames, + eDimension a_Dimension, const AString & a_LinkedOverworldName +): m_WorldName(a_WorldName), m_DataPath(a_DataPath), m_LinkedOverworldName(a_LinkedOverworldName), @@ -135,7 +139,7 @@ cWorld::cWorld(const AString & a_WorldName, const AString & a_DataPath, eDimensi m_Dimension(a_Dimension), m_IsSpawnExplicitlySet(false), m_SpawnX(0), - m_SpawnY(0), + m_SpawnY(cChunkDef::Height), m_SpawnZ(0), m_BroadcastDeathMessages(true), m_BroadcastAchievementMessages(true), @@ -146,7 +150,7 @@ cWorld::cWorld(const AString & a_WorldName, const AString & a_DataPath, eDimensi m_LastChunkCheck(0), m_LastSave(0), m_SkyDarkness(0), - m_GameMode(gmNotSet), + m_GameMode(gmSurvival), m_bEnabledPVP(false), m_IsDeepSnowEnabled(false), m_ShouldLavaSpawnFire(true), @@ -201,6 +205,221 @@ cWorld::cWorld(const AString & a_WorldName, const AString & a_DataPath, eDimensi // Load the scoreboard cScoreboardSerializer Serializer(m_DataPath, &m_Scoreboard); Serializer.Load(); + + // Track the CSs used by this world in the deadlock detector: + a_DeadlockDetect.TrackCriticalSection(m_CSClients, Printf("World %s clients", m_WorldName.c_str())); + a_DeadlockDetect.TrackCriticalSection(m_CSPlayers, Printf("World %s players", m_WorldName.c_str())); + a_DeadlockDetect.TrackCriticalSection(m_CSTasks, Printf("World %s tasks", m_WorldName.c_str())); + + // 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(UnusedDirtyChunksCap); + + m_BroadcastDeathMessages = IniFile.GetValueSetB("Broadcasting", "BroadcastDeathMessages", true); + m_BroadcastAchievementMessages = IniFile.GetValueSetB("Broadcasting", "BroadcastAchievementMessages", true); + + SetMaxViewDistance(IniFile.GetValueSetI("SpawnPosition", "MaxViewDistance", 12)); + + // 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); + 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); + m_IsDeepSnowEnabled = IniFile.GetValueSetB("Physics", "DeepSnow", true); + m_ShouldLavaSpawnFire = IniFile.GetValueSetB("Physics", "ShouldLavaSpawnFire", true); + int TNTShrapnelLevel = IniFile.GetValueSetI("Physics", "TNTShrapnelLevel", static_cast(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(m_GameMode)); + int Weather = IniFile.GetValueSetI("General", "Weather", static_cast(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 (Clamp(GameMode, gmSurvival, gmSpectator)); + m_TNTShrapnelLevel = static_cast(Clamp(TNTShrapnelLevel, slNone, slAll)); + m_Weather = static_cast (Clamp(Weather, wSunny, wStorm)); + + InitialiseGeneratorDefaults(IniFile); + InitialiseAndLoadMobSpawningValues(IniFile); + SetTimeOfDay(IniFile.GetValueSetI("General", "TimeInTicks", GetTimeOfDay())); + + m_ChunkMap = cpp14::make_unique(this); + m_ChunkMap->TrackInDeadlockDetect(a_DeadlockDetect, m_WorldName); + + // 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 = cpp14::make_unique(*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 = cpp14::make_unique(*this, IniFile); + m_FireSimulator = cpp14::make_unique(*this, IniFile); + 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); + + 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) + m_LastSpawnMonster.emplace(cMonster::mfHostile, cTickTimeLong(0)); + m_LastSpawnMonster.emplace(cMonster::mfPassive, cTickTimeLong(0)); + m_LastSpawnMonster.emplace(cMonster::mfAmbient, cTickTimeLong(0)); + m_LastSpawnMonster.emplace(cMonster::mfWater, cTickTimeLong(0)); } @@ -386,226 +605,13 @@ void cWorld::InitializeSpawn(void) -void cWorld::Start(cDeadlockDetect & a_DeadlockDetect) +void cWorld::Start() { - // Track the CSs used by this world in the deadlock detector: - a_DeadlockDetect.TrackCriticalSection(m_CSClients, Printf("World %s clients", m_WorldName.c_str())); - a_DeadlockDetect.TrackCriticalSection(m_CSPlayers, Printf("World %s players", m_WorldName.c_str())); - a_DeadlockDetect.TrackCriticalSection(m_CSTasks, Printf("World %s tasks", m_WorldName.c_str())); - - m_SpawnX = 0; - m_SpawnY = cChunkDef::Height; - m_SpawnZ = 0; - m_GameMode = eGameMode_Survival; - - 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(UnusedDirtyChunksCap); - - m_BroadcastDeathMessages = IniFile.GetValueSetB("Broadcasting", "BroadcastDeathMessages", true); - m_BroadcastAchievementMessages = IniFile.GetValueSetB("Broadcasting", "BroadcastAchievementMessages", true); - - SetMaxViewDistance(IniFile.GetValueSetI("SpawnPosition", "MaxViewDistance", 12)); - - // 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); - 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); - m_IsDeepSnowEnabled = IniFile.GetValueSetB("Physics", "DeepSnow", true); - m_ShouldLavaSpawnFire = IniFile.GetValueSetB("Physics", "ShouldLavaSpawnFire", true); - int TNTShrapnelLevel = IniFile.GetValueSetI("Physics", "TNTShrapnelLevel", static_cast(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(m_GameMode)); - int Weather = IniFile.GetValueSetI("General", "Weather", static_cast(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); - } - } - - if (GetDimension() == dimOverworld) - { - AString MyNetherName = GetName() + "_nether"; - AString MyEndName = GetName() + "_the_end"; - if (cRoot::Get()->GetWorld(MyNetherName) == nullptr) - { - MyNetherName = ""; - } - if (cRoot::Get()->GetWorld(MyEndName) == nullptr) - { - MyEndName = GetName() + "_end"; - if (cRoot::Get()->GetWorld(MyEndName) == nullptr) - { - MyEndName = ""; - } - } - - 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 - cRoot * Root = cRoot::Get(); - if (GetDimension() == dimOverworld) - { - if ((!m_LinkedNetherWorldName.empty()) && (Root->GetWorld(m_LinkedNetherWorldName) == nullptr)) - { - 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 = ""; - } - if ((!m_LinkedEndWorldName.empty()) && (Root->GetWorld(m_LinkedEndWorldName) == nullptr)) - { - 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 = ""; - } - } - else - { - if ((!m_LinkedOverworldName.empty()) && (Root->GetWorld(m_LinkedOverworldName) == nullptr)) - { - 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 = ""; - } - } - - - - // Adjust the enum-backed variables into their respective bounds: - m_GameMode = static_cast (Clamp(GameMode, gmSurvival, gmSpectator)); - m_TNTShrapnelLevel = static_cast(Clamp(TNTShrapnelLevel, slNone, slAll)); - m_Weather = static_cast (Clamp(Weather, wSunny, wStorm)); - - InitialiseGeneratorDefaults(IniFile); - InitialiseAndLoadMobSpawningValues(IniFile); - SetTimeOfDay(IniFile.GetValueSetI("General", "TimeInTicks", GetTimeOfDay())); - - m_ChunkMap = cpp14::make_unique(this); - m_ChunkMap->TrackInDeadlockDetect(a_DeadlockDetect, m_WorldName); - - // 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 = cpp14::make_unique(*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 = cpp14::make_unique(*this, IniFile); - m_FireSimulator = cpp14::make_unique(*this, IniFile); - 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); - m_Lighting.Start(this); m_Storage.Start(this, m_StorageSchema, m_StorageCompressionFactor); - m_Generator.Start(m_GeneratorCallbacks, m_GeneratorCallbacks, IniFile); + m_Generator.Start(); m_ChunkSender.Start(); m_TickThread.Start(); - - // Init of the spawn monster time (as they are supposed to have different spawn rate) - m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfHostile, cTickTimeLong(0))); - m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfPassive, cTickTimeLong(0))); - m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfAmbient, cTickTimeLong(0))); - m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfWater, cTickTimeLong(0))); - - 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()); - } } diff --git a/src/World.h b/src/World.h index 5f18f7a39..5b26ec197 100644 --- a/src/World.h +++ b/src/World.h @@ -731,9 +731,8 @@ public: void InitializeSpawn(void); - /** Starts threads that belong to this world. - a_DeadlockDetect is used for tracking this world's age, detecting a possible deadlock. */ - void Start(cDeadlockDetect & a_DeadlockDetect); + /** Starts threads that belong to this world. */ + void Start(); /** Stops threads that belong to this world (part of deinit). a_DeadlockDetect is used for tracking this world's age, detecting a possible deadlock. */ @@ -1066,8 +1065,15 @@ private: /** Queue for the chunk data to be set into m_ChunkMap by the tick thread. Protected by m_CSSetChunkDataQueue */ cSetChunkDataPtrs m_SetChunkDataQueue; - - cWorld(const AString & a_WorldName, const AString & a_DataPath, eDimension a_Dimension = dimOverworld, const AString & a_LinkedOverworldName = ""); + /** Construct the world and read settings from its ini file. + @param a_DeadlockDetect is used for tracking this world's age, detecting a possible deadlock. + @param a_WorldNames is a list of all world names, used to validate linked worlds + */ + cWorld( + const AString & a_WorldName, const AString & a_DataPath, + cDeadlockDetect & a_DeadlockDetect, const AStringVector & a_WorldNames, + eDimension a_Dimension = dimOverworld, const AString & a_LinkedOverworldName = {} + ); virtual ~cWorld() override; void Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec); -- cgit v1.2.3