summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Server/Plugins/APIDump/APIDesc.lua1
-rwxr-xr-xcompile.sh2
-rw-r--r--src/ClientHandle.cpp2
-rw-r--r--src/Entities/Entity.cpp14
-rw-r--r--src/Entities/Player.cpp2
-rw-r--r--src/IniFile.cpp2
-rw-r--r--src/Root.cpp108
-rw-r--r--src/Root.h15
-rw-r--r--src/World.cpp48
9 files changed, 141 insertions, 53 deletions
diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua
index 97e17c7c6..0cbdff479 100644
--- a/Server/Plugins/APIDump/APIDesc.lua
+++ b/Server/Plugins/APIDump/APIDesc.lua
@@ -2126,7 +2126,6 @@ a_Player:OpenWindow(Window);
BroadcastChatLeave = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtLeave. Use for players leaving the server." },
BroadcastChatSuccess = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtSuccess. Use for success messages." },
BroadcastChatWarning = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtWarning. Use for concerning events, such as plugin reload etc." },
- CreateAndInitializeWorld = { Params = "WorldName", Return = "{{cWorld|cWorld}}", Notes = "Creates a new world and initializes it. If there is a world whith the same name it returns nil.<br><br><b>NOTE:</b> This function is currently unsafe, do not use!" },
FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "bool", Notes = "Calls the given callback function for the player with the name best matching the name string provided.<br>This function is case-insensitive and will match partial names.<br>Returns false if player not found or there is ambiguity, true otherwise. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre>" },
DoWithPlayerByUUID = { Params = "PlayerUUID, CallbackFunction", Return = "bool", Notes = "If there is the player with the uuid, calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." },
ForEachPlayer = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each player. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|cPlayer}})</pre>" },
diff --git a/compile.sh b/compile.sh
index 84ed478a2..38aa5f69e 100755
--- a/compile.sh
+++ b/compile.sh
@@ -215,7 +215,7 @@ cmake .. -DCMAKE_BUILD_TYPE=$BUILDTYPE || error "cmake failed"
# Make.
echo " --- Compiling..."
-make -j`nproc` || error "Compiling failed"
+make -j 2 || error "Compiling failed"
echo
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 792ca49b5..df85d9b67 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -1033,6 +1033,7 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
{
// A plugin doesn't agree with the action, replace the block on the client and quit:
m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
+ SendPlayerPosition(); // Prevents the player from falling through the block that was temporarily broken client side.
return;
}
@@ -1229,6 +1230,7 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo
{
// A plugin doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows:
m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
+ SendPlayerPosition(); // Prevents the player from falling through the block that was temporarily broken client side.
return;
}
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index b207e79c9..d0540b4eb 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -1392,7 +1392,8 @@ bool cEntity::DetectPortal()
TargetPos.x *= 8.0;
TargetPos.z *= 8.0;
- cWorld * TargetWorld = cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName(), dimNether, GetWorld()->GetName(), true);
+ cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName());
+ ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
LOGD("Jumping nether -> overworld");
new cNetherPortalScanner(this, TargetWorld, TargetPos, 256);
return true;
@@ -1416,7 +1417,8 @@ bool cEntity::DetectPortal()
TargetPos.x /= 8.0;
TargetPos.z /= 8.0;
- cWorld * TargetWorld = cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedNetherWorldName(), dimNether, GetWorld()->GetName(), true);
+ cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedNetherWorldName());
+ ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
LOGD("Jumping overworld -> nether");
new cNetherPortalScanner(this, TargetWorld, TargetPos, 128);
return true;
@@ -1446,7 +1448,9 @@ bool cEntity::DetectPortal()
Player->GetClientHandle()->SendRespawn(dimOverworld);
}
- return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false);
+ cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName());
+ ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
+ return MoveToWorld(TargetWorld, false);
}
else
{
@@ -1463,7 +1467,9 @@ bool cEntity::DetectPortal()
reinterpret_cast<cPlayer *>(this)->GetClientHandle()->SendRespawn(dimEnd);
}
- return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedEndWorldName(), dimEnd, GetWorld()->GetName()), false);
+ cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedEndWorldName());
+ ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
+ return MoveToWorld(TargetWorld, false);
}
}
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 5606e9668..b7f6f4d05 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -1871,7 +1871,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
cEnderChestEntity::LoadFromJson(root["enderchestinventory"], m_EnderChestContents);
m_LoadedWorldName = root.get("world", "world").asString();
- a_World = cRoot::Get()->GetWorld(GetLoadedWorldName(), false);
+ a_World = cRoot::Get()->GetWorld(GetLoadedWorldName());
if (a_World == nullptr)
{
a_World = cRoot::Get()->GetDefaultWorld();
diff --git a/src/IniFile.cpp b/src/IniFile.cpp
index 1e5416813..4a1c8e735 100644
--- a/src/IniFile.cpp
+++ b/src/IniFile.cpp
@@ -146,7 +146,7 @@ bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect)
case '=':
{
valuename = line.substr(0, pLeft);
- value = line.substr(pLeft + 1);
+ value = TrimString(line.substr(pLeft + 1));
AddValue(keyname, valuename, value);
break;
}
diff --git a/src/Root.cpp b/src/Root.cpp
index 737d350ff..87c255b9c 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -327,6 +327,17 @@ void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo)
+void cRoot::StopServer()
+{
+ m_TerminateEventRaised = true;
+ m_StopEvent.Set();
+ m_InputThreadRunFlag.clear();
+}
+
+
+
+
+
void cRoot::LoadGlobalSettings()
{
// Nothing needed yet
@@ -338,9 +349,9 @@ void cRoot::LoadGlobalSettings()
void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIniFile)
{
- // First get the default world
if (a_IsNewIniFile)
{
+ a_Settings.AddValue("Worlds", "DefaultWorld", "world");
a_Settings.AddValue("Worlds", "World", "world_nether");
a_Settings.AddValue("Worlds", "World", "world_end");
m_pDefaultWorld = new cWorld("world");
@@ -350,6 +361,7 @@ void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIn
return;
}
+ // First get the default world
AString DefaultWorldName = a_Settings.GetValueSet("Worlds", "DefaultWorld", "world");
m_pDefaultWorld = new cWorld(DefaultWorldName.c_str());
m_WorldsByName[ DefaultWorldName ] = m_pDefaultWorld;
@@ -398,6 +410,7 @@ void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIn
a_Settings.AddValue("Worlds", "World", "world_nether");
a_Settings.AddValue("Worlds", "World", "world_end");
Worlds = a_Settings.GetValues("Worlds"); // Refresh the Worlds list so that the rest of the function works as usual
+ LOG("The server detected an old default config with bad world linkages. This has been autofixed by adding \"world_nether\" and \"world_end\" to settings.ini");
}
}
}
@@ -409,6 +422,28 @@ void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIn
return;
}
+ /* Here are the world creation rules. Note that these only apply for a world which is in settings.ini but has no world.ini file.
+ If an ini file is present, it overrides the world linkages and the dimension type in cWorld::start()
+ The creation rules are as follows:
+
+ - If a world exists in settings.ini but has no world.ini, then:
+ - If the world name is x_nether, create a world.ini with the dimension type "nether".
+ - If a world called x exists, set it as x_nether's overworld.
+ - Otherwise set the default world as x_nether's overworld.
+
+ - If the world name is x_end, create a world.ini with the dimension type "end".
+ - If a world called x exists, set it as x_end's overworld.
+ - Otherwise set the default world as x_end's overworld.
+
+ - If the world name is x (and doesn't end with _end or _nether)
+ - Create a world.ini with a dimension type of "overworld".
+ - If a world called x_nether exists, set it as x's nether world.
+ - Otherwise set x's nether world to blank.h
+ - If a world called x_end exists, set it as x's end world.
+ - Otherwise set x's nether world to blank.
+
+ */
+
bool FoundAdditionalWorlds = false;
for (auto WorldNameValue : Worlds)
{
@@ -423,7 +458,43 @@ void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIn
continue;
}
FoundAdditionalWorlds = true;
- cWorld * NewWorld = new cWorld(WorldName.c_str());
+ cWorld * NewWorld;
+ AString LowercaseName = StrToLower(WorldName);
+ AString NetherAppend="_nether";
+ AString EndAppend="_end";
+
+ // if the world is called x_nether
+ if ((LowercaseName.size() > NetherAppend.size()) && (LowercaseName.substr(LowercaseName.size() - NetherAppend.size()) == NetherAppend))
+ {
+ // The world is called x_nether, see if a world called x exists. If yes, choose it as the linked world,
+ // 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());
+ if (GetWorld(LinkTo) == nullptr)
+ {
+ LinkTo = DefaultWorldName;
+ }
+ NewWorld = new cWorld(WorldName.c_str(), dimNether, LinkTo);
+ }
+ // if the world is called x_end
+ else if ((LowercaseName.size() > EndAppend.size()) && (LowercaseName.substr(LowercaseName.size() - EndAppend.size()) == EndAppend))
+ {
+ // The world is called x_end, see if a world called x exists. If yes, choose it as the linked world,
+ // 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() - EndAppend.size());
+ if (GetWorld(LinkTo) == nullptr)
+ {
+ LinkTo = DefaultWorldName;
+ }
+ NewWorld = new cWorld(WorldName.c_str(), dimEnd, LinkTo);
+ }
+ else
+ {
+ NewWorld = new cWorld(WorldName.c_str());
+ }
m_WorldsByName[WorldName] = NewWorld;
} // for i - Worlds
@@ -441,29 +512,6 @@ void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIn
-cWorld * cRoot::CreateAndInitializeWorld(const AString & a_WorldName, eDimension a_Dimension, const AString & a_OverworldName, bool a_InitSpawn)
-{
- cWorld * World = m_WorldsByName[a_WorldName];
- if (World != nullptr)
- {
- return World;
- }
-
- cWorld * NewWorld = new cWorld(a_WorldName.c_str(), a_Dimension, a_OverworldName);
- m_WorldsByName[a_WorldName] = NewWorld;
- NewWorld->Start();
- if (a_InitSpawn)
- {
- NewWorld->InitializeSpawn();
- }
- m_PluginManager->CallHookWorldStarted(*NewWorld);
- return NewWorld;
-}
-
-
-
-
-
void cRoot::StartWorlds(void)
{
for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr)
@@ -513,7 +561,7 @@ cWorld * cRoot::GetDefaultWorld()
-cWorld * cRoot::GetWorld(const AString & a_WorldName, bool a_SearchForFolder)
+cWorld * cRoot::GetWorld(const AString & a_WorldName)
{
WorldMap::iterator itr = m_WorldsByName.find(a_WorldName);
if (itr != m_WorldsByName.end())
@@ -521,10 +569,6 @@ cWorld * cRoot::GetWorld(const AString & a_WorldName, bool a_SearchForFolder)
return itr->second;
}
- if (a_SearchForFolder && cFile::IsFolder(FILE_IO_PREFIX + a_WorldName))
- {
- return CreateAndInitializeWorld(a_WorldName);
- }
return nullptr;
}
@@ -597,9 +641,7 @@ void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback
// Some commands are built-in:
if (a_Cmd == "stop")
{
- m_TerminateEventRaised = true;
- m_StopEvent.Set();
- m_InputThreadRunFlag.clear();
+ StopServer();
return;
}
else if (a_Cmd == "restart")
diff --git a/src/Root.h b/src/Root.h
index 261dac29f..24c8216d9 100644
--- a/src/Root.h
+++ b/src/Root.h
@@ -57,20 +57,15 @@ public:
void Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo);
+ /** Stops the server, as if "/stop" was typed in the console. */
+ void StopServer();
+
// tolua_begin
cServer * GetServer(void) { return m_Server; }
cWorld * GetDefaultWorld(void);
- /** Returns a pointer to the world specified
- If no world of that name was currently loaded and a_SearchForFolder was true, it will consult cFile::IsFolder() to see if a world folder of that name exists and if so, initialise a world based on that name
- */
- cWorld * GetWorld(const AString & a_WorldName, bool a_SearchForFolder = false);
-
- /** Returns a pointer to a world of specified name - will search loaded worlds first, then create anew if not found
- The dimension parameter is used to create a world with a specific dimension
- a_OverworldName should be set for non-overworld dimensions if one wishes that world to link back to an overworld via portals
- */
- cWorld * CreateAndInitializeWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_OverworldName = "", bool a_InitSpawn = true);
+ /** Returns a pointer to the world specified. If no world of that name exists, returns a nullptr. */
+ cWorld * GetWorld(const AString & a_WorldName);
/** Returns the up time of the server in seconds */
int GetServerUpTime(void)
diff --git a/src/World.cpp b/src/World.cpp
index 127621069..c704b46bb 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -512,14 +512,58 @@ void cWorld::Start(void)
if (GetDimension() == dimOverworld)
{
- m_LinkedNetherWorldName = IniFile.GetValueSet("LinkedWorlds", "NetherWorldName", GetName() + "_nether");
- m_LinkedEndWorldName = IniFile.GetValueSet("LinkedWorlds", "EndWorldName", GetName() + "_end");
+ AString MyNetherName = GetName() + "_nether";
+ AString MyEndName = GetName() + "_end";
+ if (cRoot::Get()->GetWorld(MyNetherName) == nullptr)
+ {
+ MyNetherName = "";
+ }
+ 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, ask the server to stop.
+ AString BadWorlds = "";
+ cRoot * Root = cRoot::Get();
+ if (GetDimension() == dimOverworld)
+ {
+ if ((!m_LinkedNetherWorldName.empty()) && (Root->GetWorld(m_LinkedNetherWorldName) == nullptr))
+ {
+ BadWorlds = m_LinkedNetherWorldName;
+ }
+ if ((!m_LinkedEndWorldName.empty()) && (Root->GetWorld(m_LinkedEndWorldName) == nullptr))
+ {
+ if (!(BadWorlds.empty()))
+ {
+ BadWorlds += ", ";
+ }
+ BadWorlds += m_LinkedEndWorldName;
+ }
+ }
+ else
+ {
+ if ((!m_LinkedOverworldName.empty()) && (Root->GetWorld(m_LinkedOverworldName) == nullptr))
+ {
+ BadWorlds = m_LinkedOverworldName;
+ }
+ }
+ if (!BadWorlds.empty())
+ {
+ const char * WorldName = m_WorldName.c_str();
+ LOGERROR("\n###### ERROR: \"%s\" is linked to the following nonexisting world/s:\n%s\n\nPlease edit %s/world.ini and fix this.\n\nNote that the server started enforcing proper world linkages recently. And people with older configs may naturally get this error. If you just want a working default config and don't mind losing this world, delete the folder \"%s\" and the server will receate one for you. Otherwise edit the world.ini file and fix the invalid linkages.\n\nMore help and info:\nhttps://forum.cuberite.org/thread-2366.html\n######\n",
+ WorldName, BadWorlds.c_str(), WorldName, WorldName);
+ cRoot::Get()->StopServer();
+ }
+
+
// Adjust the enum-backed variables into their respective bounds:
m_GameMode = static_cast<eGameMode> (Clamp<int>(GameMode, gmSurvival, gmSpectator));
m_TNTShrapnelLevel = static_cast<eShrapnelLevel>(Clamp<int>(TNTShrapnelLevel, slNone, slAll));