49 LOGWARNING(
"Cannot listen on port %d: %d (%s).",
m_Port, a_ErrorCode, a_ErrorMsg.c_str());
68 Super(
"Server Ticker"),
79 auto LastTime = std::chrono::steady_clock::now();
80 static const auto msPerTick = std::chrono::milliseconds(50);
82 while (!m_ShouldTerminate)
84 auto NowTime = std::chrono::steady_clock::now();
85 auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(NowTime - LastTime).count();
86 m_Server.Tick(
static_cast<float>(msec));
87 auto TickTime = std::chrono::steady_clock::now() - NowTime;
89 if (TickTime < msPerTick)
92 std::this_thread::sleep_for(msPerTick - TickTime);
168 LOGERROR(
"ERROR: Trying to initialize server while server is already running!");
186 unsigned int r1 = rand.RandInt<
unsigned int>(1000000000U, 0x7fffffffU);
187 unsigned int r2 = rand.RandInt<
unsigned int>(1000000000U, 0x7fffffffU);
188 std::ostringstream sid;
189 sid << std::hex << r1;
190 sid << std::hex << r2;
202 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.");
207 LOGWARNING(
"WARNING: There is not a Proxy Forward Secret set up, and any proxy server can forward a player to this server unless closed from the internet.");
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());
365 if (itrC->get() == *itr)
377 auto & Client = *itr;
379 Client->ServerTick(a_Dt);
380 if (Client->IsDestroyed())
383 RemoveClients.push_back(std::move(Client));
393 RemoveClients.clear();
402 for (
const auto & port:
m_Ports)
407 LOGWARNING(
"Invalid port specified for server: \"%s\". Ignoring.", port.c_str());
410 auto Handle =
cNetwork::Listen(PortNum, std::make_shared<cServerListenCallbacks>(*
this, PortNum));
411 if (Handle->IsListening())
418 LOGERROR(
"Couldn't open any ports. Aborting the server");
435 return cRoot::Get()->GetPluginManager()->CallHookChat(a_Player, a_Cmd);
458 const auto TargetTick = a_DelayTicks +
m_UpTime;
462 m_Tasks.emplace_back(TargetTick, std::move(a_Task));
481 if (split[0] ==
"help")
487 else if (split[0] ==
"reload")
489 if (split.size() > 1)
492 a_Output.
OutLn(
"Plugin reload scheduled");
501 else if (split[0] ==
"reloadplugins")
504 a_Output.
OutLn(
"Plugins reloaded");
508 else if (split[0] ==
"reloadweb")
511 a_Output.
OutLn(
"WebAdmin configuration reloaded");
515 else if (split[0] ==
"load")
517 if (split.size() > 1)
524 a_Output.
OutLn(
"Usage: load <PluginFolder>");
529 else if (split[0] ==
"unload")
531 if (split.size() > 1)
534 a_Output.
OutLn(
"Plugin unload scheduled");
538 a_Output.
OutLn(
"Usage: unload <PluginFolder>");
543 if (split[0] ==
"destroyentities")
559 a_Output.
OutLn(
"Destroyed all entities");
565 else if (split[0].compare(
"chunkstats") == 0)
572 else if (split[0].compare(
"luastats") == 0)
584 a_Output.
OutLn(
"Unknown command, type 'help' for all commands.");
595 typedef std::pair<AString, AString> AStringPair;
596 typedef std::vector<AStringPair> AStringPairs;
602 cCallback(
void) : m_MaxLen(0) {}
608 if (!a_HelpString.empty())
610 m_Commands.push_back(AStringPair(a_Command, a_HelpString));
611 if (m_MaxLen < a_Command.length())
613 m_MaxLen = a_Command.length();
619 AStringPairs m_Commands;
623 std::sort(Callback.m_Commands.begin(), Callback.m_Commands.end());
624 for (AStringPairs::const_iterator itr = Callback.m_Commands.begin(), end = Callback.m_Commands.end(); itr != end; ++itr)
626 const AStringPair & cmd = *itr;
628 a_Output.
OutLn(fmt::format(FMT_STRING(
"{1:{0}s} - {2}"), Callback.m_MaxLen, cmd.first, cmd.second));
642 virtual bool ExecuteCommand(
652 auto handler = std::make_shared<cEmptyHandler>();
658 PlgMgr->
BindConsoleCommand(
"reloadweb",
nullptr, handler,
"Reloads the webadmin configuration");
659 PlgMgr->
BindConsoleCommand(
"restart",
nullptr, handler,
"Restarts the server cleanly");
661 PlgMgr->
BindConsoleCommand(
"chunkstats",
nullptr, handler,
"Displays detailed chunk memory statistics");
662 PlgMgr->
BindConsoleCommand(
"load",
nullptr, handler,
"Adds and enables the specified plugin");
663 PlgMgr->
BindConsoleCommand(
"unload",
nullptr, handler,
"Disables the specified plugin");
664 PlgMgr->
BindConsoleCommand(
"destroyentities",
nullptr, handler,
"Destroys all entities in all worlds");
704 if ((*itr)->GetUniqueID() == a_ClientID)
706 (*itr)->Kick(a_Reason);
722 KickUser(a_ClientID,
"The server is currently full :(" "\n" "Try again later?");
728 if (Client->GetUniqueID() == a_ClientID)
730 Client->Authenticate(std::move(a_Username), a_UUID, std::move(a_Properties));
749 for (
const auto &
Command : PendingCommands)
773 auto MoveBeginIterator = std::partition(
775 [
this](
const decltype(
m_Tasks)::value_type & a_Task)
782 Tasks.end(), std::make_move_iterator(MoveBeginIterator),
783 std::make_move_iterator(
m_Tasks.end()));
788 for (
const auto & Task : Tasks)
std::shared_ptr< cClientHandle > cClientHandlePtr
MTRand & GetRandomProvider()
Returns the current thread's random number source.
std::chrono::duration< signed int, std::ratio_multiply< std::chrono::milliseconds::period, std::ratio< 50 > >> cTickTime
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...
void LOGERROR(std::string_view a_Format, const Args &... args)
void LOGWARNING(std::string_view a_Format, const Args &... args)
void LOGINFO(std::string_view a_Format, const Args &... args)
#define MCS_CLIENT_VERSIONS
#define MCS_PROTOCOL_VERSIONS
std::list< cClientHandlePtr > cClientHandlePtrs
AStringVector StringSplit(const AString &str, const AString &delim)
Split the string at any of the listed delimiters.
AString Base64Encode(const AString &a_Input)
Encodes a string into Base64.
std::vector< AString > AStringVector
bool StringToInteger(const AString &a_str, T &a_Num)
Parses any integer type.
std::map< AString, AString > AStringMap
A string dictionary, used for key-value pairs.
static AString GetStats(void)
Returns the statistics for all the registered LuaStates.
static cPluginManager * Get(void)
Returns the instance of the Plugin Manager (there is only ever one)
void RefreshPluginList()
Refreshes the m_Plugins list based on the current contents of the Plugins folder.
void UnloadPlugin(const AString &a_PluginFolder)
Queues the specified plugin to be unloaded in the next call to Tick().
bool ForEachConsoleCommand(cCommandEnumCallback &a_Callback)
Calls a_Callback for each bound console command, returns true if all commands were enumerated.
void Tick(float a_Dt)
Called each tick, calls the plugins' OnTick hook, as well as processes plugin events (addition,...
void ReloadPlugins()
Schedules a reload of the plugins to happen within the next call to Tick().
void ReloadPlugin(const AString &a_PluginFolder)
Queues the specified plugin to be reloaded in the next call to Tick().
bool BindConsoleCommand(const AString &a_Command, cPlugin *a_Plugin, cCommandHandlerPtr a_Handler, const AString &a_HelpString)
Binds a console command to the specified handler.
Used as a callback for enumerating bound commands.
Interface that must be provided by any class that implements a command handler, either in-game or con...
static const int MAX_VIEW_DISTANCE
const cUUID & GetUUID(void) const
Returns the player's UUID, as used by the protocol.
static const int MIN_VIEW_DISTANCE
static const int DEFAULT_VIEW_DISTANCE
Interface for a callback that receives command output The Out() function is called for any output the...
virtual void Finished()
Called when the command processing has been finished.
void OutLn(const AString &aText)
Outputs the specified text, plus a newline.
bool IsPlayer(void) const
ContiguousByteBuffer GetPubKeyDER(void)
Returns the public key part encoded in ASN1 DER encoding.
bool Generate(unsigned a_KeySizeBits=1024)
Generates a new key within this object, with the specified size in bits.
RAII for cCriticalSection - locks the CS on creation, unlocks on destruction.
static AString ReadWholeFile(const AString &a_FileName)
Returns the entire contents of the specified file as a string.
void Stop(void)
Signals the thread to terminate and waits until it's finished.
void Start(void)
Starts the thread; returns without waiting for the actual start.
Interface that provides the methods available on a single TCP connection.
std::shared_ptr< cCallbacks > cCallbacksPtr
static cServerHandlePtr Listen(UInt16 a_Port, cListenCallbacksPtr a_ListenCallbacks)
Opens up the specified port for incoming connections.
Callbacks used when listening for incoming connections as a server.
void Initialize(cSettingsRepositoryInterface &a_Settings)
bool ForEachWorld(cWorldListCallback a_Callback)
Calls the callback for each world; returns true if the callback didn't abort (return true)
void SaveAllChunksNow(void)
Saves all chunks in all worlds synchronously (waits until dirty chunks have been sent to the ChunkSto...
bool DoWithPlayerByUUID(const cUUID &a_PlayerUUID, cPlayerListCallback a_Callback)
Finds the player over his uuid and calls the callback.
cWebAdmin * GetWebAdmin(void)
void LogChunkStats(cCommandOutputCallback &a_Output)
Writes chunkstats, for each world and totals, to the output callback.
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.
virtual void OnError(int a_ErrorCode, const AString &a_ErrorMsg) override
Called when the socket fails to listen on the specified port.
virtual void OnAccepted(cTCPLink &a_Link) override
Called when the TCP server created with Listen() creates a new link for an incoming connection.
cServerListenCallbacks(cServer &a_Server, UInt16 a_Port)
AString m_CustomRedirectUrl
void TickClients(float a_Dt)
Ticks the clients in m_Clients, manages the list in respect to removing clients.
void PlayerCreated()
Notifies the server that a player was created; the server uses this to adjust the number of players.
void ExecuteConsoleCommand(const AString &a_Cmd, cCommandOutputCallback &a_Output)
Executes the console command, sends output through the specified callback.
bool m_ShouldLimitPlayerBlockChanges
True if limit for number of block changes per tick by a player should be enabled.
size_t GetMaxPlayers(void) const
void PrepareKeys(void)
Loads, or generates, if missing, RSA keys for protocol encryption.
cTCPLink::cCallbacksPtr OnConnectionAccepted(const AString &a_RemoteIPAddress)
Creates a new cClientHandle instance and adds it to the list of clients.
void PrintHelp(const AStringVector &a_Split, cCommandOutputCallback &a_Output)
Lists all available console commands and their helpstrings.
bool m_bAllowMultiLogin
True - allow same username to login more than once False - only once.
void TickCommands(void)
Executes commands queued in the command queue.
void UnregisterForgeMod(const AString &a_ModName, UInt32 a_ProtocolVersionNumber)
Remove a Forge mod to the server ping list.
ContiguousByteBuffer m_PublicKeyDER
Public key for m_PrivateKey, ASN1-DER-encoded.
bool IsPlayerInQueue(const AString &a_Username)
Check if the player is queued to be transferred to a World.
std::vector< std::pair< std::chrono::milliseconds, std::function< void(class cServer &)> > > m_Tasks
Tasks that have been queued onto the tick thread, possibly to be executed at target tick in the futur...
bool m_ShouldAllowBungeeCord
True if BungeeCord handshake packets (with player UUID) should be accepted.
cServerHandlePtrs m_ServerHandles
The network sockets listening for client connections.
bool RegisterForgeMod(const AString &a_ModName, const AString &a_ModVersion, UInt32 a_ProtocolVersionNumber)
Add a Forge mod to the server ping list.
const AStringMap & GetRegisteredForgeMods(const UInt32 a_Protocol)
Get the Forge mods (map of ModName -> ModVersionString) registered for a given protocol.
bool Command(cClientHandle &a_Client, AString &a_Cmd)
AString m_ServerID
The server ID used for client authentication.
std::atomic_size_t m_PlayerCount
Number of players currently playing in the server.
bool m_ShouldAuthenticate
If true, players will be online-authenticated agains Mojang servers.
cClientHandles m_ClientsToRemove
Clients that have just been moved into a world and are to be removed from m_Clients in the next Tick(...
void TickQueuedTasks(void)
Executes all tasks queued onto the tick thread.
AString m_ResourcePackUrl
cCriticalSection m_CSTasks
Guards the m_Tasks.
std::vector< std::pair< AString, cCommandOutputCallback * > > m_PendingCommands
void ClientMovedToWorld(const cClientHandle *a_Client)
Don't tick a_Client anymore, it will be ticked from its cPlayer instead.
cCriticalSection m_CSClients
Protects m_Clients and m_ClientsToRemove against multithreaded access.
bool InitServer(cSettingsRepositoryInterface &a_Settings, bool a_ShouldAuth)
static void BindBuiltInConsoleCommands(void)
Binds the built-in console commands with the plugin manager.
bool m_RequireResourcePack
void QueueExecuteConsoleCommand(const AString &a_Cmd, cCommandOutputCallback &a_Output)
Queues a console command for execution through the cServer class.
size_t GetNumPlayers(void) const
std::map< UInt32, AStringMap > m_ForgeModsByVersion
Map of protocol version to Forge mods (map of ModName -> ModVersionString)
bool m_ShouldAllowMultiWorldTabCompletion
True if usernames should be completed across worlds.
cRsaPrivateKey m_PrivateKey
The private key used for the assymetric encryption start in the protocols.
AStringVector m_Ports
The list of ports on which the server should listen for connections.
AString m_ProxySharedSecret
Security string that the proxy server should send, compatible with BungeeGuard.
void KickUser(int a_ClientID, const AString &a_Reason)
void PlayerDestroyed()
Notifies the server that a player is being destroyed; the server uses this to adjust the number of pl...
cClientHandlePtrs m_Clients
Clients that are connected to the server and not yet assigned to a cWorld.
AStringMap & RegisteredForgeMods(const UInt32 a_Protocol)
Get the Forge mods registered for a given protocol, for modification.
void AuthenticateUser(int a_ClientID, AString &&a_Username, const cUUID &a_UUID, Json::Value &&a_Properties)
Authenticates the specified user, called by cAuthenticator supplying player details from Mojang.
cCriticalSection m_CSPendingCommands
AString m_ShutdownMessage
void ScheduleTask(cTickTime a_DelayTicks, std::function< void(class cServer &)> a_Task)
Queues a lambda task onto the server tick thread, with the specified delay in ticks.
bool m_OnlyAllowBungeeCord
True if BungeeCord handshake packets should be the only ones accepted.
cTickTimeLong m_UpTime
Time, in ticks, since the server started Not persistent across server restarts.
cTickThread(cServer &a_Server)
virtual void Execute(void) override
This function, overloaded by the descendants, is called in the new thread.
virtual bool GetValueSetB(const AString &keyname, const AString &valuename, const bool defValue=false)=0
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.
virtual int GetValueSetI(const AString &keyname, const AString &valuename, const int defValue=0)=0
void Reload(void)
Reloads m_IniFile, m_LoginPage and m_TemplateScript.
bool ForEachEntity(cEntityCallback a_Callback)
Calls the callback for each entity in the entire world; returns true if all entities processed,...