diff options
Diffstat (limited to 'source/Server.cpp')
-rw-r--r-- | source/Server.cpp | 707 |
1 files changed, 0 insertions, 707 deletions
diff --git a/source/Server.cpp b/source/Server.cpp deleted file mode 100644 index fe8076631..000000000 --- a/source/Server.cpp +++ /dev/null @@ -1,707 +0,0 @@ -// ReDucTor is an awesome guy who helped me a lot - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Server.h" -#include "ClientHandle.h" -#include "OSSupport/Timer.h" -#include "Mobs/Monster.h" -#include "OSSupport/Socket.h" -#include "Root.h" -#include "World.h" -#include "ChunkDef.h" -#include "PluginManager.h" -#include "GroupManager.h" -#include "ChatColor.h" -#include "Entities/Player.h" -#include "Inventory.h" -#include "Item.h" -#include "FurnaceRecipe.h" -#include "WebAdmin.h" -#include "Protocol/ProtocolRecognizer.h" -#include "CommandOutput.h" - -#include "MersenneTwister.h" - -#include "../iniFile/iniFile.h" -#include "Vector3f.h" - -#include <fstream> -#include <sstream> -#include <iostream> - -extern "C" { - #include "zlib.h" -} - - - - -// For the "dumpmem" server command: -/// Synchronize this with main.cpp - the leak finder needs initialization before it can be used to dump memory -#define ENABLE_LEAK_FINDER - -#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) - #pragma warning(push) - #pragma warning(disable:4100) - #include "LeakFinder.h" - #pragma warning(pop) -#endif - - - - - -typedef std::list< cClientHandle* > ClientList; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cServer::cTickThread: - -cServer::cTickThread::cTickThread(cServer & a_Server) : - super("ServerTickThread"), - m_Server(a_Server) -{ -} - - - - - -void cServer::cTickThread::Execute(void) -{ - cTimer Timer; - - long long msPerTick = 50; - long long LastTime = Timer.GetNowTime(); - - while (!m_ShouldTerminate) - { - long long NowTime = Timer.GetNowTime(); - float DeltaTime = (float)(NowTime-LastTime); - m_ShouldTerminate = !m_Server.Tick(DeltaTime); - long long TickTime = Timer.GetNowTime() - NowTime; - - if (TickTime < msPerTick) - { - // Stretch tick time until it's at least msPerTick - cSleep::MilliSleep((unsigned int)(msPerTick - TickTime)); - } - - LastTime = NowTime; - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cServer: - -cServer::cServer(void) : - m_ListenThreadIPv4(*this, cSocket::IPv4, "Client IPv4"), - m_ListenThreadIPv6(*this, cSocket::IPv6, "Client IPv6"), - m_bIsConnected(false), - m_bRestarting(false), - m_RCONServer(*this), - m_TickThread(*this) -{ -} - - - - - -void cServer::ClientDestroying(const cClientHandle * a_Client) -{ - m_SocketThreads.StopReading(a_Client); -} - - - - - -void cServer::NotifyClientWrite(const cClientHandle * a_Client) -{ - m_NotifyWriteThread.NotifyClientWrite(a_Client); -} - - - - - -void cServer::WriteToClient(const cClientHandle * a_Client, const AString & a_Data) -{ - m_SocketThreads.Write(a_Client, a_Data); -} - - - - - -void cServer::QueueClientClose(const cClientHandle * a_Client) -{ - m_SocketThreads.QueueClose(a_Client); -} - - - - - -void cServer::RemoveClient(const cClientHandle * a_Client) -{ - m_SocketThreads.RemoveClient(a_Client); -} - - - - - -void cServer::ClientMovedToWorld(const cClientHandle * a_Client) -{ - cCSLock Lock(m_CSClients); - m_ClientsToRemove.push_back(const_cast<cClientHandle *>(a_Client)); -} - - - - - -void cServer::PlayerCreated(const cPlayer * a_Player) -{ - // To avoid deadlocks, the player count is not handled directly, but rather posted onto the tick thread - cCSLock Lock(m_CSPlayerCountDiff); - m_PlayerCountDiff += 1; -} - - - - - -void cServer::PlayerDestroying(const cPlayer * a_Player) -{ - // To avoid deadlocks, the player count is not handled directly, but rather posted onto the tick thread - cCSLock Lock(m_CSPlayerCountDiff); - m_PlayerCountDiff -= 1; -} - - - - - -bool cServer::InitServer(cIniFile & a_SettingsIni) -{ - m_Description = a_SettingsIni.GetValueSet("Server", "Description", "MCServer - in C++!").c_str(); - m_MaxPlayers = a_SettingsIni.GetValueSetI("Server", "MaxPlayers", 100); - m_bIsHardcore = a_SettingsIni.GetValueSetB("Server", "HardcoreEnabled", false); - m_PlayerCount = 0; - m_PlayerCountDiff = 0; - - if (m_bIsConnected) - { - LOGERROR("ERROR: Trying to initialize server while server is already running!"); - return false; - } - - LOGINFO("Compatible clients: %s", MCS_CLIENT_VERSIONS); - LOGINFO("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS); - - if (cSocket::WSAStartup() != 0) // Only does anything on Windows, but whatever - { - LOGERROR("WSAStartup() != 0"); - return false; - } - - bool HasAnyPorts = false; - AString Ports = a_SettingsIni.GetValueSet("Server", "Port", "25565"); - m_ListenThreadIPv4.SetReuseAddr(true); - if (m_ListenThreadIPv4.Initialize(Ports)) - { - HasAnyPorts = true; - } - - Ports = a_SettingsIni.GetValueSet("Server", "PortsIPv6", "25565"); - m_ListenThreadIPv6.SetReuseAddr(true); - if (m_ListenThreadIPv6.Initialize(Ports)) - { - HasAnyPorts = true; - } - - if (!HasAnyPorts) - { - LOGERROR("Couldn't open any ports. Aborting the server"); - return false; - } - - m_RCONServer.Initialize(a_SettingsIni); - - m_bIsConnected = true; - - m_ServerID = "-"; - if (a_SettingsIni.GetValueSetB("Authentication", "Authenticate", true)) - { - MTRand mtrand1; - unsigned int r1 = (mtrand1.randInt() % 1147483647) + 1000000000; - unsigned int r2 = (mtrand1.randInt() % 1147483647) + 1000000000; - std::ostringstream sid; - sid << std::hex << r1; - sid << std::hex << r2; - m_ServerID = sid.str(); - m_ServerID.resize(16, '0'); - } - - m_ClientViewDistance = a_SettingsIni.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE); - if (m_ClientViewDistance < cClientHandle::MIN_VIEW_DISTANCE) - { - m_ClientViewDistance = cClientHandle::MIN_VIEW_DISTANCE; - LOGINFO("Setting default viewdistance to the minimum of %d", m_ClientViewDistance); - } - if (m_ClientViewDistance > cClientHandle::MAX_VIEW_DISTANCE) - { - m_ClientViewDistance = cClientHandle::MAX_VIEW_DISTANCE; - LOGINFO("Setting default viewdistance to the maximum of %d", m_ClientViewDistance); - } - - m_NotifyWriteThread.Start(this); - - PrepareKeys(); - - return true; -} - - - - - -int cServer::GetNumPlayers(void) -{ - cCSLock Lock(m_CSPlayerCount); - return m_PlayerCount; -} - - - - - -void cServer::PrepareKeys(void) -{ - // TODO: Save and load key for persistence across sessions - // But generating the key takes only a moment, do we even need that? - - LOGD("Generating protocol encryption keypair..."); - - time_t CurTime = time(NULL); - CryptoPP::RandomPool rng; - rng.Put((const byte *)&CurTime, sizeof(CurTime)); - m_PrivateKey.GenerateRandomWithKeySize(rng, 1024); - CryptoPP::RSA::PublicKey pk(m_PrivateKey); - m_PublicKey = pk; -} - - - - - -void cServer::OnConnectionAccepted(cSocket & a_Socket) -{ - if (!a_Socket.IsValid()) - { - return; - } - - const AString & ClientIP = a_Socket.GetIPString(); - if (ClientIP.empty()) - { - LOGWARN("cServer: A client connected, but didn't present its IP, disconnecting."); - a_Socket.CloseSocket(); - return; - } - - LOGD("Client \"%s\" connected!", ClientIP.c_str()); - - cClientHandle * NewHandle = new cClientHandle(&a_Socket, m_ClientViewDistance); - if (!m_SocketThreads.AddClient(a_Socket, NewHandle)) - { - // For some reason SocketThreads have rejected the handle, clean it up - LOGERROR("Client \"%s\" cannot be handled, server probably unstable", ClientIP.c_str()); - a_Socket.CloseSocket(); - delete NewHandle; - return; - } - - cCSLock Lock(m_CSClients); - m_Clients.push_back(NewHandle); -} - - - - - -bool cServer::Tick(float a_Dt) -{ - // Apply the queued playercount adjustments (postponed to avoid deadlocks) - int PlayerCountDiff = 0; - { - cCSLock Lock(m_CSPlayerCountDiff); - std::swap(PlayerCountDiff, m_PlayerCountDiff); - } - { - cCSLock Lock(m_CSPlayerCount); - m_PlayerCount += PlayerCountDiff; - } - - // Send the tick to the plugins, as well as let the plugin manager reload, if asked to (issue #102): - cPluginManager::Get()->Tick(a_Dt); - - // Let the Root process all the queued commands: - cRoot::Get()->TickCommands(); - - // Tick all clients not yet assigned to a world: - TickClients(a_Dt); - - if (!m_bRestarting) - { - return true; - } - else - { - m_bRestarting = false; - m_RestartEvent.Set(); - return false; - } -} - - - - - -void cServer::TickClients(float a_Dt) -{ - cClientHandleList RemoveClients; - { - cCSLock Lock(m_CSClients); - - // Remove clients that have moved to a world (the world will be ticking them from now on) - for (cClientHandleList::const_iterator itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr) - { - m_Clients.remove(*itr); - } // for itr - m_ClientsToRemove[] - m_ClientsToRemove.clear(); - - // Tick the remaining clients, take out those that have been destroyed into RemoveClients - for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();) - { - if ((*itr)->IsDestroyed()) - { - // Remove the client later, when CS is not held, to avoid deadlock ( http://forum.mc-server.org/showthread.php?tid=374 ) - RemoveClients.push_back(*itr); - itr = m_Clients.erase(itr); - continue; - } - (*itr)->Tick(a_Dt); - ++itr; - } // for itr - m_Clients[] - } - - // Delete the clients that have been destroyed - for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr) - { - delete *itr; - } // for itr - RemoveClients[] -} - - - - - -bool cServer::Start(void) -{ - if (!m_ListenThreadIPv4.Start()) - { - return false; - } - if (!m_ListenThreadIPv6.Start()) - { - return false; - } - if (!m_TickThread.Start()) - { - return false; - } - return true; -} - - - - - -bool cServer::Command(cClientHandle & a_Client, AString & a_Cmd) -{ - return cRoot::Get()->GetPluginManager()->CallHookChat(a_Client.GetPlayer(), a_Cmd); -} - - - - - -void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output) -{ - AStringVector split = StringSplit(a_Cmd, " "); - if (split.empty()) - { - return; - } - - // Special handling: "stop" and "restart" are built in - if ((split[0].compare("stop") == 0) || (split[0].compare("restart") == 0)) - { - return; - } - - // "help" and "reload" are to be handled by MCS, so that they work no matter what - if (split[0] == "help") - { - PrintHelp(split, a_Output); - return; - } - if (split[0] == "reload") - { - cPluginManager::Get()->ReloadPlugins(); - return; - } - - // There is currently no way a plugin can do these (and probably won't ever be): - if (split[0].compare("chunkstats") == 0) - { - cRoot::Get()->LogChunkStats(a_Output); - a_Output.Finished(); - return; - } - #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) - if (split[0].compare("dumpmem") == 0) - { - LeakFinderXmlOutput Output("memdump.xml"); - DumpUsedMemory(&Output); - return; - } - - if (split[0].compare("killmem") == 0) - { - while (true) - { - new char[100 * 1024 * 1024]; // Allocate and leak 100 MiB in a loop -> fill memory and kill MCS - } - } - #endif - - if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output)) - { - a_Output.Finished(); - return; - } - - a_Output.Out("Unknown command, type 'help' for all commands."); - a_Output.Finished(); -} - - - - - -void cServer::PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & a_Output) -{ - typedef std::pair<AString, AString> AStringPair; - typedef std::vector<AStringPair> AStringPairs; - - class cCallback : - public cPluginManager::cCommandEnumCallback - { - public: - cCallback(void) : m_MaxLen(0) {} - - virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override - { - if (!a_HelpString.empty()) - { - m_Commands.push_back(AStringPair(a_Command, a_HelpString)); - if (m_MaxLen < a_Command.length()) - { - m_MaxLen = a_Command.length(); - } - } - return false; - } - - AStringPairs m_Commands; - size_t m_MaxLen; - } Callback; - cPluginManager::Get()->ForEachConsoleCommand(Callback); - std::sort(Callback.m_Commands.begin(), Callback.m_Commands.end()); - for (AStringPairs::const_iterator itr = Callback.m_Commands.begin(), end = Callback.m_Commands.end(); itr != end; ++itr) - { - const AStringPair & cmd = *itr; - a_Output.Out(Printf("%-*s%s\n", Callback.m_MaxLen, cmd.first.c_str(), cmd.second.c_str())); - } // for itr - Callback.m_Commands[] - a_Output.Finished(); -} - - - - - -void cServer::BindBuiltInConsoleCommands(void) -{ - cPluginManager * PlgMgr = cPluginManager::Get(); - PlgMgr->BindConsoleCommand("help", NULL, " - Shows the available commands"); - PlgMgr->BindConsoleCommand("reload", NULL, " - Reloads all plugins"); - PlgMgr->BindConsoleCommand("restart", NULL, " - Restarts the server cleanly"); - PlgMgr->BindConsoleCommand("stop", NULL, " - Stops the server cleanly"); - PlgMgr->BindConsoleCommand("chunkstats", NULL, " - Displays detailed chunk memory statistics"); - #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) - PlgMgr->BindConsoleCommand("dumpmem", NULL, " - Dumps all used memory blocks together with their callstacks into memdump.xml"); - #endif -} - - - - - -void cServer::Shutdown(void) -{ - m_ListenThreadIPv4.Stop(); - m_ListenThreadIPv6.Stop(); - - m_bRestarting = true; - m_RestartEvent.Wait(); - - cRoot::Get()->SaveAllChunks(); - - cCSLock Lock(m_CSClients); - for( ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr ) - { - (*itr)->Destroy(); - delete *itr; - } - m_Clients.clear(); -} - - - - - -void cServer::KickUser(int a_ClientID, const AString & a_Reason) -{ - cCSLock Lock(m_CSClients); - for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) - { - if ((*itr)->GetUniqueID() == a_ClientID) - { - (*itr)->Kick(a_Reason); - } - } // for itr - m_Clients[] -} - - - - - -void cServer::AuthenticateUser(int a_ClientID) -{ - cCSLock Lock(m_CSClients); - for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) - { - if ((*itr)->GetUniqueID() == a_ClientID) - { - (*itr)->Authenticate(); - return; - } - } // for itr - m_Clients[] -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cServer::cNotifyWriteThread: - -cServer::cNotifyWriteThread::cNotifyWriteThread(void) : - super("ClientPacketThread"), - m_Server(NULL) -{ -} - - - - - -cServer::cNotifyWriteThread::~cNotifyWriteThread() -{ - m_ShouldTerminate = true; - m_Event.Set(); - Wait(); -} - - - - - -bool cServer::cNotifyWriteThread::Start(cServer * a_Server) -{ - m_Server = a_Server; - return super::Start(); -} - - - - - -void cServer::cNotifyWriteThread::Execute(void) -{ - cClientHandleList Clients; - while (!m_ShouldTerminate) - { - cCSLock Lock(m_CS); - while (m_Clients.size() == 0) - { - cCSUnlock Unlock(Lock); - m_Event.Wait(); - if (m_ShouldTerminate) - { - return; - } - } - - // Copy the clients to notify and unlock the CS: - Clients.splice(Clients.begin(), m_Clients); - Lock.Unlock(); - - for (cClientHandleList::iterator itr = Clients.begin(); itr != Clients.end(); ++itr) - { - m_Server->m_SocketThreads.NotifyWrite(*itr); - } // for itr - Clients[] - Clients.clear(); - } // while (!mShouldTerminate) -} - - - - - -void cServer::cNotifyWriteThread::NotifyClientWrite(const cClientHandle * a_Client) -{ - { - cCSLock Lock(m_CS); - m_Clients.remove(const_cast<cClientHandle *>(a_Client)); // Put it there only once - m_Clients.push_back(const_cast<cClientHandle *>(a_Client)); - } - m_Event.Set(); -} - - - - |