diff options
-rw-r--r-- | src/ClientHandle.cpp | 40 | ||||
-rw-r--r-- | src/ClientHandle.h | 4 | ||||
-rw-r--r-- | src/Generating/DungeonRoomsFinisher.cpp | 4 | ||||
-rw-r--r-- | src/OSSupport/Event.cpp | 2 | ||||
-rw-r--r-- | src/Root.cpp | 18 | ||||
-rw-r--r-- | src/Root.h | 5 | ||||
-rw-r--r-- | src/Server.cpp | 18 | ||||
-rw-r--r-- | src/Server.h | 11 | ||||
-rw-r--r-- | src/World.cpp | 5 | ||||
-rw-r--r-- | src/World.h | 2 | ||||
-rwxr-xr-x[-rw-r--r--] | src/WorldStorage/WSSAnvil.cpp | 34 | ||||
-rwxr-xr-x[-rw-r--r--] | src/WorldStorage/WSSAnvil.h | 3 |
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); |