29 #include "zlib/zlib.h" 60 LOGWARNING(
"Cannot listen on port %d: %d (%s).", m_Port, a_ErrorCode, a_ErrorMsg.c_str());
79 super(
"ServerTickThread"),
90 auto LastTime = std::chrono::steady_clock::now();
91 static const auto msPerTick = std::chrono::milliseconds(50);
95 auto NowTime = std::chrono::steady_clock::now();
96 auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(NowTime - LastTime).count();
98 auto TickTime = std::chrono::steady_clock::now() - NowTime;
100 if (TickTime < msPerTick)
103 std::this_thread::sleep_for(msPerTick - TickTime);
178 LOGERROR(
"ERROR: Trying to initialize server while server is already running!");
196 unsigned int r1 = rand.RandInt<
unsigned int>(1000000000U, 0x7fffffffU);
197 unsigned int r2 = rand.RandInt<
unsigned int>(1000000000U, 0x7fffffffU);
198 std::ostringstream sid;
199 sid << std::hex << r1;
200 sid << std::hex << r2;
209 LOGWARNING(
"WARNING: BungeeCord is allowed and server set to online mode. This is unsafe and will not work properly. Disable either authentication or BungeeCord in settings.ini.");
242 return Mods.insert({a_ModName, a_ModVersion}).second;
253 auto it = Mods.find(a_ModName);
254 if (it != Mods.end())
296 if ((client->GetUsername()).compare(a_Username) == 0)
310 LOGD(
"Generating protocol encryption keypair...");
321 LOGD(
"Client \"%s\" connected!", a_RemoteIPAddress.c_str());
323 NewHandle->SetSelf(NewHandle);
326 return std::move(NewHandle);
371 if (itrC->get() == *itr)
383 if ((*itr)->IsDestroyed())
386 RemoveClients.push_back(*itr);
390 (*itr)->ServerTick(a_Dt);
396 RemoveClients.clear();
410 LOGWARNING(
"Invalid port specified for server: \"%s\". Ignoring.", port.c_str());
413 auto Handle =
cNetwork::Listen(PortNum, std::make_shared<cServerListenCallbacks>(*
this, PortNum));
414 if (Handle->IsListening())
421 LOGERROR(
"Couldn't open any ports. Aborting the server");
455 if (split[0] ==
"help")
461 else if (split[0] ==
"reload")
467 else if (split[0] ==
"reloadplugins")
470 a_Output.
Out(
"Plugins reloaded");
474 else if (split[0] ==
"load")
476 if (split.size() > 1)
479 a_Output.
Out(
cPluginManager::Get()->LoadPlugin(split[1]) ?
"Plugin loaded" :
"Error occurred loading plugin");
483 a_Output.
Out(
"Usage: load <PluginFolder>");
488 else if (split[0] ==
"unload")
490 if (split.size() > 1)
493 a_Output.
Out(
"Plugin unload scheduled");
497 a_Output.
Out(
"Usage: unload <PluginFolder>");
502 if (split[0] ==
"destroyentities")
518 a_Output.
Out(
"Destroyed all entities");
524 else if (split[0].compare(
"chunkstats") == 0)
531 else if (split[0].compare(
"luastats") == 0)
543 a_Output.
Out(
"Unknown command, type 'help' for all commands.");
554 typedef std::pair<AString, AString> AStringPair;
555 typedef std::vector<AStringPair> AStringPairs;
561 cCallback(
void) : m_MaxLen(0) {}
567 if (!a_HelpString.empty())
569 m_Commands.push_back(AStringPair(a_Command, a_HelpString));
570 if (m_MaxLen < a_Command.length())
572 m_MaxLen = a_Command.length();
578 AStringPairs m_Commands;
582 std::sort(Callback.m_Commands.begin(), Callback.m_Commands.end());
583 for (AStringPairs::const_iterator itr = Callback.m_Commands.begin(), end = Callback.m_Commands.end(); itr != end; ++itr)
585 const AStringPair & cmd = *itr;
586 a_Output.
Out(
Printf(
"%-*s - %s\n", static_cast<int>(Callback.m_MaxLen), cmd.first.c_str(), cmd.second.c_str()));
600 virtual bool ExecuteCommand(
610 auto handler = std::make_shared<cEmptyHandler>();
614 PlgMgr->BindConsoleCommand(
"help",
nullptr, handler,
"Shows the available commands");
615 PlgMgr->BindConsoleCommand(
"reload",
nullptr, handler,
"Reloads all plugins");
616 PlgMgr->BindConsoleCommand(
"restart",
nullptr, handler,
"Restarts the server cleanly");
617 PlgMgr->BindConsoleCommand(
"stop",
nullptr, handler,
"Stops the server cleanly");
618 PlgMgr->BindConsoleCommand(
"chunkstats",
nullptr, handler,
"Displays detailed chunk memory statistics");
619 PlgMgr->BindConsoleCommand(
"load",
nullptr, handler,
"Adds and enables the specified plugin");
620 PlgMgr->BindConsoleCommand(
"unload",
nullptr, handler,
"Disables the specified plugin");
621 PlgMgr->BindConsoleCommand(
"destroyentities",
nullptr, handler,
"Destroys all entities in all worlds");
635 m_ServerHandles.clear();
661 if ((*itr)->GetUniqueID() == a_ClientID)
663 (*itr)->Kick(a_Reason);
679 KickUser(a_ClientID,
"The server is currently full :(" "\n" "Try again later?");
685 if ((*itr)->GetUniqueID() == a_ClientID)
687 (*itr)->Authenticate(a_Name, a_UUID, a_Properties);
void PrepareKeys(void)
Loads, or generates, if missing, RSA keys for protocol encryption.
AStringMap & RegisteredForgeMods(const UInt32 a_Protocol)
Get the Forge mods registered for a given protocol, for modification.
void RefreshPluginList()
Refreshes the m_Plugins list based on the current contents of the Plugins folder. ...
void Set(void)
Sets the event - releases one thread that has been waiting in Wait().
cServerListenCallbacks(cServer &a_Server, UInt16 a_Port)
cClientHandles m_ClientsToRemove
Clients that have just been moved into a world and are to be removed from m_Clients in the next Tick(...
std::atomic< bool > m_ShouldTerminate
The overriden Execute() method should check this value periodically and terminate if this is true...
static void BindBuiltInConsoleCommands(void)
Binds the built-in console commands with the plugin manager.
std::list< cClientHandlePtr > cClientHandlePtrs
void ExecuteConsoleCommand(const AString &a_Cmd, cCommandOutputCallback &a_Output)
Executes the console command, sends output through the specified callback.
AStringVector m_Ports
The list of ports on which the server should listen for connections.
void PlayerDestroyed()
Notifies the server that a player is being destroyed; the server uses this to adjust the number of pl...
void LogChunkStats(cCommandOutputCallback &a_Output)
Writes chunkstats, for each world and totals, to the output callback.
static AString GetStats(void)
Returns the statistics for all the registered LuaStates.
std::atomic_size_t m_PlayerCount
Number of players currently playing in the server.
bool RegisterForgeMod(const AString &a_ModName, const AString &a_ModVersion, UInt32 a_ProtocolVersionNumber)
Add a Forge mod to the server ping list.
bool m_ShouldLoadOfflinePlayerData
True if offline UUIDs should be used to load data for players whose normal UUIDs cannot be found...
bool StringToInteger(const AString &a_str, T &a_Num)
Parses any integer type.
void UnloadPlugin(const AString &a_PluginFolder)
Queues the specified plugin to be unloaded in the next call to Tick().
const AStringMap & GetRegisteredForgeMods(const UInt32 a_Protocol)
Get the Forge mods (map of ModName -> ModVersionString) registered for a given protocol.
bool m_ShouldLoadNamedPlayerData
True if old-style playernames should be used to load data for players whose regular datafiles cannot ...
Interface that must be provided by any class that implements a command handler, either in-game or con...
bool m_bAllowMultiLogin
True - allow same username to login more than once False - only once.
MTRand & GetRandomProvider()
Returns the current thread's random number source.
bool ForEachWorld(cWorldListCallback a_Callback)
Calls the callback for each world; returns true if the callback didn't abort (return true) ...
void ReloadPlugins()
Schedules a reload of the plugins to happen within the next call to Tick().
static AString ReadWholeFile(const AString &a_FileName)
Returns the entire contents of the specified file as a string.
bool m_ShouldAllowBungeeCord
True if BungeeCord handshake packets (with player UUID) should be accepted.
AString m_ShutdownMessage
cCriticalSection m_CSClients
Protects m_Clients and m_ClientsToRemove against multithreaded access.
AString GetPubKeyDER(void)
Returns the public key part encoded in ASN1 DER encoding.
static cPluginManager * Get(void)
Returns the instance of the Plugin Manager (there is only ever one)
void LOGERROR(const char *a_Format, fmt::ArgList a_ArgList)
void UnregisterForgeMod(const AString &a_ModName, UInt32 a_ProtocolVersionNumber)
Remove a Forge mod to the server ping list.
static const int MIN_VIEW_DISTANCE
virtual void Destroy(bool a_ShouldBroadcast=true)
Destroys the entity and schedules it for memory freeing; if a_ShouldBroadcast is set to true...
virtual void OnError(int a_ErrorCode, const AString &a_ErrorMsg) override
Called when the socket fails to listen on the specified port.
virtual void Execute(void) override
This is the main thread entrypoint.
void Tick(float a_Dt)
Called each tick, calls the plugins' OnTick hook, as well as processes plugin events (addition...
bool m_ShouldAllowMultiWorldTabCompletion
True if usernames should be completed across worlds.
cServerHandlePtrs m_ServerHandles
The network sockets listening for client connections.
virtual void Finished(void)
Called when the command processing has been finished.
void ClientMovedToWorld(const cClientHandle *a_Client)
Don't tick a_Client anymore, it will be ticked from its cPlayer instead.
std::vector< AString > AStringVector
cTickThread(cServer &a_Server)
size_t GetMaxPlayers(void) const
void PlayerCreated()
Notifies the server that a player was created; the server uses this to adjust the number of players...
void Wait(void)
Waits until the event has been set.
void Out(const char *a_Fmt, fmt::ArgList)
Syntax sugar function, calls Out() with Printf()-ed parameters; appends a newline".
void KickUser(int a_ClientID, const AString &a_Reason)
virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString &a_RemoteIPAddress, UInt16 a_RemotePort) override
Called when the TCP server created with Listen() receives a new incoming connection.
std::map< UInt32, AStringMap > m_ForgeModsByVersion
Map of protocol version to Forge mods (map of ModName -> ModVersionString)
cPlayer * GetPlayer(void)
bool IsPlayerInQueue(AString a_Username)
Check if the player is queued to be transferred to a World.
static const int DEFAULT_VIEW_DISTANCE
bool m_ShouldLimitPlayerBlockChanges
True if limit for number of block changes per tick by a player should be enabled. ...
void SaveAllChunks(void)
Saves all chunks in all worlds.
bool ForEachEntity(cEntityCallback a_Callback)
Calls the callback for each entity in the entire world; returns true if all entities processed...
void LOGINFO(const char *a_Format, fmt::ArgList a_ArgList)
AString & Printf(AString &str, const char *format, fmt::ArgList args)
Output the formatted text into the string.
std::atomic< bool > m_bRestarting
cTCPLink::cCallbacksPtr OnConnectionAccepted(const AString &a_RemoteIPAddress)
Creates a new cClientHandle instance and adds it to the list of clients.
#define MCS_CLIENT_VERSIONS
std::map< AString, AString > AStringMap
A string dictionary, used for key-value pairs.
void LOGWARNING(const char *a_Format, fmt::ArgList a_ArgList)
#define MCS_PROTOCOL_VERSIONS
bool m_ShouldAuthenticate
If true, players will be online-authenticated agains Mojang servers.
size_t GetNumPlayers(void) const
virtual AString GetValueSet(const AString &keyname, const AString &valuename, const AString &defValue="")=0
Gets the value; if not found, write the default to the repository.
void TickClients(float a_Dt)
Ticks the clients in m_Clients, manages the list in respect to removing clients.
std::list< cClientHandle * > ClientList
bool InitServer(cSettingsRepositoryInterface &a_Settings, bool a_ShouldAuth)
std::shared_ptr< cCallbacks > cCallbacksPtr
bool Command(cClientHandle &a_Client, AString &a_Cmd)
Callbacks used when listening for incoming connections as a server.
void TickCommands(void)
Executes commands queued in the command queue.
virtual bool GetValueSetB(const AString &keyname, const AString &valuename, const bool defValue=false)=0
bool IsPlayer(void) const
void Initialize(cSettingsRepositoryInterface &a_Settings)
AString m_ServerID
The server ID used for client authentication.
void PrintHelp(const AStringVector &a_Split, cCommandOutputCallback &a_Output)
Lists all available console commands and their helpstrings.
AStringVector StringSplit(const AString &str, const AString &delim)
Split the string at any of the listed delimiters.
static cServerHandlePtr Listen(UInt16 a_Port, cListenCallbacksPtr a_ListenCallbacks)
Opens up the specified port for incoming connections.
RAII for cCriticalSection - locks the CS on creation, unlocks on destruction.
Interface that provides the methods available on a single TCP connection.
virtual void OnAccepted(cTCPLink &a_Link) override
Called when the TCP server created with Listen() creates a new link for an incoming connection...
bool ForEachConsoleCommand(cCommandEnumCallback &a_Callback)
Calls a_Callback for each bound console command, returns true if all commands were enumerated...
AStringVector ReadUpgradeIniPorts(cSettingsRepositoryInterface &a_Settings, const AString &a_KeyName, const AString &a_PortsValueName, const AString &a_OldIPv4ValueName, const AString &a_OldIPv6ValueName, const AString &a_DefaultValue)
Reads the list of ports from the INI file, possibly upgrading from IPv4 / IPv6-specific values into n...
Interface for a callback that receives command output The Out() function is called for any output the...
cClientHandlePtrs m_Clients
Clients that are connected to the server and not yet assigned to a cWorld.
static const int MAX_VIEW_DISTANCE
Used as a callback for enumerating bound commands.
AString m_PublicKeyDER
Public key for m_PrivateKey, ASN1-DER-encoded.
std::shared_ptr< cClientHandle > cClientHandlePtr
bool CallHookChat(cPlayer &a_Player, AString &a_Message)
void AuthenticateUser(int a_ClientID, const AString &a_Name, const cUUID &a_UUID, const Json::Value &a_Properties)
Authenticates the specified user, called by cAuthenticator.
virtual int GetValueSetI(const AString &keyname, const AString &valuename, const int defValue=0)=0
bool Start(void)
Starts the thread; returns without waiting for the actual start.
cPluginManager * GetPluginManager(void)
AString Base64Encode(const AString &a_Input)
Encodes a string into Base64.
cRsaPrivateKey m_PrivateKey
The private key used for the assymetric encryption start in the protocols.
bool Generate(unsigned a_KeySizeBits=1024)
Generates a new key within this object, with the specified size in bits.