summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/ClientHandle.cpp40
-rw-r--r--src/ClientHandle.h4
-rw-r--r--src/Generating/DungeonRoomsFinisher.cpp4
-rw-r--r--src/OSSupport/Event.cpp2
-rw-r--r--src/Root.cpp18
-rw-r--r--src/Root.h5
-rw-r--r--src/Server.cpp18
-rw-r--r--src/Server.h11
-rw-r--r--src/World.cpp5
-rw-r--r--src/World.h2
-rwxr-xr-x[-rw-r--r--]src/WorldStorage/WSSAnvil.cpp34
-rwxr-xr-x[-rw-r--r--]src/WorldStorage/WSSAnvil.h3
12 files changed, 133 insertions, 13 deletions
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index c4a620565..cb9d34c84 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -1778,6 +1778,43 @@ void cClientHandle::HandleKeepAlive(int a_KeepAliveID)
+bool cClientHandle::CheckMultiLogin(const AString & a_Username)
+{
+ // If the multilogin is allowed, skip this check entirely:
+ if ((cRoot::Get()->GetServer()->DoesAllowMultiLogin()))
+ {
+ return true;
+ }
+
+ // Check if the player is waiting to be transferred to the World.
+ if (cRoot::Get()->GetServer()->IsPlayerInQueue(a_Username))
+ {
+ Kick("A player of the username is already logged in");
+ return false;
+ }
+
+ class cCallback :
+ public cPlayerListCallback
+ {
+ virtual bool Item(cPlayer * a_Player) override
+ {
+ return true;
+ }
+ } Callback;
+
+ // Check if the player is in any World.
+ if (cRoot::Get()->DoWithPlayer(a_Username, Callback))
+ {
+ Kick("A player of the username is already logged in");
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
bool cClientHandle::HandleHandshake(const AString & a_Username)
{
if (!cRoot::Get()->GetPluginManager()->CallHookHandshake(*this, a_Username))
@@ -1788,7 +1825,8 @@ bool cClientHandle::HandleHandshake(const AString & a_Username)
return false;
}
}
- return true;
+
+ return CheckMultiLogin(a_Username);
}
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 495348ac3..25dd250d9 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -280,6 +280,10 @@ public:
void HandleEntityLeaveBed (int a_EntityID);
void HandleEntitySprinting (int a_EntityID, bool a_IsSprinting);
+ /** Kicks the client if the same username is already logged in.
+ Returns false if the client has been kicked, true otherwise. */
+ bool CheckMultiLogin(const AString & a_Username);
+
/** Called when the protocol handshake has been received (for protocol versions that support it;
otherwise the first instant when a username is received).
Returns true if the player is to be let in, false if they were disconnected
diff --git a/src/Generating/DungeonRoomsFinisher.cpp b/src/Generating/DungeonRoomsFinisher.cpp
index 092e232ab..c4bf8839e 100644
--- a/src/Generating/DungeonRoomsFinisher.cpp
+++ b/src/Generating/DungeonRoomsFinisher.cpp
@@ -131,8 +131,8 @@ protected:
{
int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
- int RelStartX = Clamp(a_StartX - BlockX, 0, cChunkDef::Width - 1);
- int RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width - 1);
+ int RelStartX = Clamp(a_StartX - BlockX, 0, cChunkDef::Width);
+ int RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width);
int RelEndX = Clamp(a_EndX - BlockX, 0, cChunkDef::Width);
int RelEndZ = Clamp(a_EndZ - BlockZ, 0, cChunkDef::Width);
for (int y = a_StartY; y < a_EndY; y++)
diff --git a/src/OSSupport/Event.cpp b/src/OSSupport/Event.cpp
index d6ba937f9..760e536a1 100644
--- a/src/OSSupport/Event.cpp
+++ b/src/OSSupport/Event.cpp
@@ -63,7 +63,7 @@ bool cEvent::Wait(unsigned a_TimeoutMSec)
} // switch (wait_until())
} // while (m_ShouldWait && not timeout)
- // The wait timed out in the while() condition:
+ // The wait timed out in the while condition:
return false;
}
diff --git a/src/Root.cpp b/src/Root.cpp
index 865b2a213..29daaedcc 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -196,7 +196,7 @@ void cRoot::Start(void)
}
#endif
- LOG("Startup complete, took %lld ms!", std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count());
+ LOG("Startup complete, took %ld ms!", std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count());
#ifdef _WIN32
EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button
#endif
@@ -643,6 +643,22 @@ bool cRoot::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback
+bool cRoot::DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback)
+{
+ for (auto World : m_WorldsByName)
+ {
+ if (World.second->DoWithPlayer(a_PlayerName, a_Callback))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
AString cRoot::GetProtocolVersionTextFromInt(int a_ProtocolVersion)
{
return cProtocolRecognizer::GetVersionTextFromInt(a_ProtocolVersion);
diff --git a/src/Root.h b/src/Root.h
index e70b284f9..2c512a5df 100644
--- a/src/Root.h
+++ b/src/Root.h
@@ -129,7 +129,10 @@ public:
/** Finds the player over his uuid and calls the callback */
bool DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
-
+
+ /** Finds the player using it's complete username and calls the callback */
+ bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback);
+
// tolua_begin
/// Sends a chat message to all connected clients (in all worlds)
diff --git a/src/Server.cpp b/src/Server.cpp
index d6163df7e..3eaf6e096 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -196,6 +196,7 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth)
m_Description = a_SettingsIni.GetValueSet("Server", "Description", "MCServer - in C++!");
m_MaxPlayers = a_SettingsIni.GetValueSetI("Server", "MaxPlayers", 100);
m_bIsHardcore = a_SettingsIni.GetValueSetB("Server", "HardcoreEnabled", false);
+ m_bAllowMultiLogin = a_SettingsIni.GetValueSetB("Server", "AllowMultiLogin", false);
m_PlayerCount = 0;
m_PlayerCountDiff = 0;
@@ -298,6 +299,23 @@ int cServer::GetNumPlayers(void) const
+bool cServer::IsPlayerInQueue(AString a_Username)
+{
+ cCSLock Lock(m_CSClients);
+ for (auto client : m_Clients)
+ {
+ if ((client->GetUsername()).compare(a_Username) == 0)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
void cServer::PrepareKeys(void)
{
LOGD("Generating protocol encryption keypair...");
diff --git a/src/Server.h b/src/Server.h
index 022794bbc..aab47987f 100644
--- a/src/Server.h
+++ b/src/Server.h
@@ -67,6 +67,14 @@ public: // tolua_export
int GetNumPlayers(void) const;
void SetMaxPlayers(int a_MaxPlayers) { m_MaxPlayers = a_MaxPlayers; }
+ /** Check if the player is queued to be transferred to a World.
+ Returns true is Player is found in queue. */
+ bool IsPlayerInQueue(AString a_Username);
+
+ /** Can login more than once with same username.
+ Returns false if it is not allowed, true otherwise. */
+ bool DoesAllowMultiLogin(void) { return m_bAllowMultiLogin; }
+
// Hardcore mode or not:
bool IsHardcore(void) const { return m_bIsHardcore; }
@@ -216,6 +224,9 @@ private:
int m_MaxPlayers;
bool m_bIsHardcore;
+ /** True - allow same username to login more than once False - only once */
+ bool m_bAllowMultiLogin;
+
cTickThread m_TickThread;
cEvent m_RestartEvent;
diff --git a/src/World.cpp b/src/World.cpp
index ff5a2cf35..1bee6e344 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -743,7 +743,7 @@ void cWorld::InitialiseGeneratorDefaults(cIniFile & a_IniFile)
a_IniFile.GetValueSet("Generator", "BiomeGen", "Grown");
a_IniFile.GetValueSet("Generator", "ShapeGen", "BiomalNoise3D");
a_IniFile.GetValueSet("Generator", "CompositionGen", "Biomal");
- a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, PreSimulator, Animals");
+ a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, NaturalPatches, PreSimulator, Animals");
break;
}
case dimNether:
@@ -755,7 +755,7 @@ void cWorld::InitialiseGeneratorDefaults(cIniFile & a_IniFile)
a_IniFile.GetValueSet("Generator", "HeightGen", "Flat");
a_IniFile.GetValueSet("Generator", "FlatHeight", "128");
a_IniFile.GetValueSet("Generator", "CompositionGen", "Nether");
- a_IniFile.GetValueSet("Generator", "Finishers", "SoulsandRims, WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherForts, PreSimulator");
+ a_IniFile.GetValueSet("Generator", "Finishers", "SoulsandRims, WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherOreNests, NetherForts, PreSimulator");
a_IniFile.GetValueSet("Generator", "BottomLavaHeight", "30");
break;
}
@@ -3678,3 +3678,4 @@ void cWorld::cChunkGeneratorCallbacks::CallHookChunkGenerated (cChunkDesc & a_Ch
+
diff --git a/src/World.h b/src/World.h
index c87ecd8c7..6f28ba534 100644
--- a/src/World.h
+++ b/src/World.h
@@ -814,7 +814,7 @@ public:
This function allows nesting and task-concurrency (multiple separate tasks can request ticking and as long
as at least one requests is active the chunk will be ticked). */
void SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTicked = true); // tolua_export
-
+
private:
friend class cRoot;
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index 5a0932bbe..af65db700 100644..100755
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -2923,6 +2923,8 @@ cWSSAnvil::cMCAFile::cMCAFile(const AString & a_FileName, int a_RegionX, int a_R
bool cWSSAnvil::cMCAFile::OpenFile(bool a_IsForReading)
{
+ bool writeOutNeeded = false;
+
if (m_File.IsOpen())
{
// Already open
@@ -2948,11 +2950,26 @@ bool cWSSAnvil::cMCAFile::OpenFile(bool a_IsForReading)
if (m_File.Read(m_Header, sizeof(m_Header)) != sizeof(m_Header))
{
// Cannot read the header - perhaps the file has just been created?
- // Try writing a nullptr header (both chunk offsets and timestamps):
+ // Try writing a nullptr header for chunk offsets:
memset(m_Header, 0, sizeof(m_Header));
+ writeOutNeeded = true;
+ }
+
+ // Load the TimeStamps:
+ if (m_File.Read(m_TimeStamps, sizeof(m_TimeStamps)) != sizeof(m_TimeStamps))
+ {
+ // Cannot read the time stamps - perhaps the file has just been created?
+ // Try writing a nullptr header for timestamps:
+ memset(m_TimeStamps, 0, sizeof(m_TimeStamps));
+ writeOutNeeded = true;
+ }
+
+ if (writeOutNeeded)
+ {
+ m_File.Seek(0);
if (
- (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) || // Real header - chunk offsets
- (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) // Bogus data for the chunk timestamps
+ (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) || // Write chunk offsets
+ (m_File.Write(m_TimeStamps, sizeof(m_TimeStamps)) != sizeof(m_TimeStamps)) // Write chunk timestamps
)
{
LOGWARNING("Cannot process MCA header in file \"%s\", chunks in that file will be lost", m_FileName.c_str());
@@ -3083,7 +3100,13 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri
);
return false;
}
+
+ // Store the header info in the table
m_Header[LocalX + 32 * LocalZ] = htonl((ChunkSector << 8) | ChunkSize);
+
+ // Set the modification time
+ m_TimeStamps[LocalX + 32 * LocalZ] = htonl(static_cast<u_long>(time(nullptr)));
+
if (m_File.Seek(0) < 0)
{
LOGWARNING("Cannot save chunk [%d, %d], seeking in file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
@@ -3094,6 +3117,11 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri
LOGWARNING("Cannot save chunk [%d, %d], writing header to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
return false;
}
+ if (m_File.Write(m_TimeStamps, sizeof(m_TimeStamps)) != sizeof(m_TimeStamps))
+ {
+ LOGWARNING("Cannot save chunk [%d, %d], writing timestamps to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
+ return false;
+ }
return true;
}
diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h
index 7a98a9a04..974ba932e 100644..100755
--- a/src/WorldStorage/WSSAnvil.h
+++ b/src/WorldStorage/WSSAnvil.h
@@ -80,7 +80,8 @@ protected:
// First 1024 entries are chunk locations - the 3 + 1 byte sector-offset and sector-count
unsigned m_Header[MCA_MAX_CHUNKS];
- // Chunk timestamps, following the chunk headers, are unused by MCS
+ // Chunk timestamps, following the chunk headers
+ unsigned m_TimeStamps[MCA_MAX_CHUNKS];
/// Finds a free location large enough to hold a_Data. Gets a hint of the chunk coords, places the data there if it fits. Returns the sector number.
unsigned FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data);