13 #elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
15 #if defined(__linux__)
18 #if !defined(__GLIBC__)
19 #include <sys/select.h>
21 #elif defined(__APPLE__)
22 #include <mach/mach.h>
23 #elif defined(__FreeBSD__)
26 #include <sys/sysctl.h>
48 #include "BuildInfo.h"
59 #pragma clang diagnostic push
60 #pragma clang diagnostic ignored "-Wglobal-constructors"
68 #pragma clang diagnostic pop
76 m_pDefaultWorld(nullptr),
78 m_MonsterConfig(nullptr),
79 m_CraftingRecipes(nullptr),
80 m_FurnaceRecipe(nullptr),
81 m_BrewingRecipes(nullptr),
83 m_PluginManager(nullptr),
109 if (!a_OverridesRepo.
HasValue(
"Server",
"DisableLogFile"))
112 if (!fileLogListenerRet.first)
114 throw std::runtime_error(
"failed to open log file");
119 LOG(
"--- Started Log ---");
122 LOG(
"Cuberite " BUILD_SERIES_NAME
" (id: " BUILD_ID
")");
123 LOG(
"from commit " BUILD_COMMIT_ID
" built at: " BUILD_DATETIME);
127 auto BeginTime = std::chrono::steady_clock::now();
131 LOG(
"Creating new server instance...");
134 LOG(
"Reading server config...");
137 if (a_OverridesRepo.
HasValue(
"Server",
"ConfigFile"))
142 auto IniFile = std::make_unique<cIniFile>();
147 LOGWARN(
"Regenerating settings.ini, all settings will be reset");
148 IniFile->AddHeaderComment(
" This is the main server configuration");
149 IniFile->AddHeaderComment(
" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini");
152 auto settingsRepo = std::make_unique<cOverridesSettingsRepository>(std::move(IniFile), a_OverridesRepo);
154 LOG(
"Starting server...");
160 bool ShouldAuthenticate = settingsRepo->GetValueSetB(
"Authentication",
"Authenticate",
true);
164 settingsRepo->Flush();
165 throw std::runtime_error(
"failure starting server");
171 LOGD(
"Loading settings...");
179 LOGD(
"Loading worlds...");
182 LOGD(
"Loading plugin manager...");
186 LOGD(
"Loading MonsterConfig...");
190 LOGD(
"Starting Authenticator...");
193 LOGD(
"Starting worlds...");
196 if (settingsRepo->GetValueSetB(
"DeadlockDetect",
"Enabled",
true))
198 LOGD(
"Starting deadlock detector...");
199 dd.
Start(settingsRepo->GetValueSetI(
"DeadlockDetect",
"IntervalSec", 20));
202 settingsRepo->Flush();
204 LOGD(
"Finalising startup...");
209 LOG(
"Startup complete, took %ldms!",
static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count()));
220 LOG(
"Shutting down server...");
226 LOGD(
"Shutting down deadlock detector...");
229 LOGD(
"Stopping world threads...");
232 LOGD(
"Stopping authenticator...");
235 LOGD(
"Freeing MonsterConfig...");
239 LOGD(
"Unloading recipes...");
243 LOGD(
"Stopping plugin manager...");
246 LOG(
"Cleaning up...");
249 LOG(
"Shutdown successful!");
250 LOG(
"--- Stopped Log ---");
290 a_Settings.
AddValue(
"Worlds",
"DefaultWorld",
"world");
291 a_Settings.
AddValue(
"Worlds",
"World",
"world_nether");
292 a_Settings.
AddValue(
"Worlds",
"World",
"world_the_end");
293 a_Settings.
AddValue(
"WorldPaths",
"world",
"world");
294 a_Settings.
AddValue(
"WorldPaths",
"world_nether",
"world_nether");
295 a_Settings.
AddValue(
"WorldPaths",
"world_the_end",
"world_the_end");
297 const AStringVector WorldNames{
"world",
"world_nether",
"world_the_end" };
299 m_WorldsByName.try_emplace(
"world_nether",
"world_nether",
"world_nether", a_dd, WorldNames,
dimNether,
"world");
300 m_WorldsByName.try_emplace(
"world_the_end",
"world_the_end",
"world_the_end", a_dd, WorldNames,
dimEnd,
"world");
305 auto Worlds = a_Settings.
GetValues(
"Worlds");
307 for (
const auto &
World : Worlds)
309 WorldNames.push_back(
World.second);
314 AString DefaultWorldPath = a_Settings.
GetValueSet(
"WorldPaths", DefaultWorldName, DefaultWorldName);
318 if (Worlds.size() <= 0)
345 bool FoundAdditionalWorlds =
false;
346 for (
const auto & WorldNameValue : Worlds)
348 AString ValueName = WorldNameValue.first;
349 if (ValueName.compare(
"World") != 0)
353 AString WorldName = WorldNameValue.second;
354 if (WorldName.empty())
358 FoundAdditionalWorlds =
true;
361 AString NetherAppend =
"_nether";
362 AString EndAppend1 =
"_the_end";
370 if ((LowercaseName.size() > NetherAppend.size()) && (LowercaseName.substr(LowercaseName.size() - NetherAppend.size()) == NetherAppend))
376 LinkTo = WorldName.substr(0, WorldName.size() - NetherAppend.size());
379 LinkTo = DefaultWorldName;
384 else if ((LowercaseName.size() > EndAppend1.size()) && (LowercaseName.substr(LowercaseName.size() - EndAppend1.size()) == EndAppend1))
390 LinkTo = WorldName.substr(0, WorldName.size() - EndAppend1.size());
393 LinkTo = DefaultWorldName;
398 else if ((LowercaseName.size() > EndAppend2.size()) && (LowercaseName.substr(LowercaseName.size() - EndAppend2.size()) == EndAppend2))
404 LinkTo = WorldName.substr(0, WorldName.size() - EndAppend2.size());
407 LinkTo = DefaultWorldName;
411 m_WorldsByName.try_emplace(WorldName, WorldName, WorldPath, a_dd, WorldNames, Dimension, LinkTo);
414 if (!FoundAdditionalWorlds)
416 if (a_Settings.
GetKeyComment(
"Worlds", 0) !=
" World=secondworld")
432 auto &
World = Entry.second;
434 World.InitializeSpawn();
447 Entry.second.Stop(a_DeadlockDetect);
470 return &FindResult->second;
484 if (a_Callback(
World.second))
498 const auto KickPlayers = [
this]
502 bool SentDisconnect =
false;
507 SentDisconnect =
true;
514 std::this_thread::sleep_for(std::chrono::seconds(1));
525 else if (a_Cmd ==
"restart")
532 LOG(
"Executing console command: \"%s\"", a_Cmd.c_str());
564 Count += Entry.second.GetNumChunks();
577 Entry.second.QueueSaveAllChunks();
589 Entry.second.SaveAllChunks();
601 Entry.second.SetSavingEnabled(a_SavingEnabled);
613 Entry.second.SendPlayerList(a_DestPlayer);
625 Entry.second.BroadcastPlayerListAddPlayer(a_Player);
637 Entry.second.BroadcastPlayerListRemovePlayer(a_Player);
649 Entry.second.BroadcastPlayerListHeaderFooter(a_Header, a_Footer);
661 Entry.second.BroadcastChat(a_Message,
nullptr, a_ChatPrefix);
673 Entry.second.BroadcastChat(a_Message);
685 if (!Entry.second.ForEachPlayer(a_Callback))
707 bool operator () (
cPlayer & a_Player)
710 if ((Rating > 0) && (Rating >= m_BestRating))
712 m_BestMatch = a_Player.
GetName();
713 if (Rating > m_BestRating)
717 m_BestRating = Rating;
720 return (Rating == m_NameLength);
723 cCallback (
const AString & a_CBPlayerName) :
725 m_NameLength(a_CBPlayerName.length()),
726 m_PlayerName(a_CBPlayerName),
732 unsigned m_NumMatches;
733 } Callback (a_PlayerName);
736 if (Callback.m_NumMatches == 1)
751 if (Entry.second.DoWithPlayerByUUID(a_PlayerUUID, a_Callback))
767 if (Entry.second.DoWithPlayer(a_PlayerName, a_Callback))
791 PROCESS_MEMORY_COUNTERS_EX pmc;
792 if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS *)&pmc,
sizeof(pmc)))
794 return (
int)(pmc.PrivateUsage / 1024);
797 #elif defined(__linux__)
799 std::ifstream StatFile(
"/proc/self/status");
800 if (!StatFile.good())
804 while (StatFile.good())
807 std::getline(StatFile, Line);
808 if (strncmp(Line.c_str(),
"VmSize:", 7) == 0)
810 int res = atoi(Line.c_str() + 8);
811 return (res == 0) ? -1 : res;
815 #elif defined (__APPLE__)
817 struct task_basic_info t_info;
818 mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
820 if (KERN_SUCCESS == task_info(
823 reinterpret_cast<task_info_t
>(&t_info),
827 return static_cast<int>(t_info.virtual_size / 1024);
831 LOGINFO(
"%s: Unknown platform, cannot query memory usage", __FUNCTION__);
843 PROCESS_MEMORY_COUNTERS pmc;
844 if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc,
sizeof(pmc)))
846 return (
int)(pmc.WorkingSetSize / 1024);
849 #elif defined(__linux__)
851 std::ifstream StatFile(
"/proc/self/status");
852 if (!StatFile.good())
856 while (StatFile.good())
859 std::getline(StatFile, Line);
860 if (strncmp(Line.c_str(),
"VmRSS:", 6) == 0)
862 int res = atoi(Line.c_str() + 7);
863 return (res == 0) ? -1 : res;
867 #elif defined (__APPLE__)
869 struct task_basic_info t_info;
870 mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
872 if (KERN_SUCCESS == task_info(
875 reinterpret_cast<task_info_t
>(&t_info),
879 return static_cast<int>(t_info.resident_size / 1024);
882 #elif defined (__FreeBSD__)
896 static kvm_t* kd = NULL;
900 kd = kvm_open(NULL,
"/dev/null", NULL, O_RDONLY,
"kvm_open");
905 struct kinfo_proc* kp;
906 kp = kvm_getprocs(kd, KERN_PROC_PID, getpid(), &pc);
907 if ((kp != NULL) && (pc >= 1))
909 return static_cast<int>(kp->ki_rssize * getpagesize() / 1024);
914 LOGINFO(
"%s: Unknown platform, cannot query memory usage", __FUNCTION__);
927 int SumNumInLighting = 0;
928 size_t SumNumInGenerator = 0;
932 auto &
World = Entry.second;
933 const auto NumInGenerator =
World.GetGeneratorQueueLength();
934 const auto NumInSaveQueue =
World.GetStorageSaveQueueLength();
935 const auto NumInLoadQueue =
World.GetStorageLoadQueueLength();
938 int NumInLighting = 0;
939 World.GetChunkStats(NumValid, NumDirty, NumInLighting);
940 a_Output.
OutLn(fmt::format(FMT_STRING(
"World {}:"),
World.GetName()));
941 a_Output.
OutLn(fmt::format(FMT_STRING(
" Num loaded chunks: {}"), NumValid));
942 a_Output.
OutLn(fmt::format(FMT_STRING(
" Num dirty chunks: {}"), NumDirty));
943 a_Output.
OutLn(fmt::format(FMT_STRING(
" Num chunks in lighting queue: {}"), NumInLighting));
944 a_Output.
OutLn(fmt::format(FMT_STRING(
" Num chunks in generator queue: {}"), NumInGenerator));
945 a_Output.
OutLn(fmt::format(FMT_STRING(
" Num chunks in storage load queue: {}"), NumInLoadQueue));
946 a_Output.
OutLn(fmt::format(FMT_STRING(
" Num chunks in storage save queue: {}"), NumInSaveQueue));
947 int Mem = NumValid *
static_cast<int>(
sizeof(
cChunk));
948 a_Output.
OutLn(fmt::format(FMT_STRING(
" Memory used by chunks: {} KiB ({} MiB)"), (Mem + 1023) / 1024, (Mem + 1024 * 1024 - 1) / (1024 * 1024)));
949 SumNumValid += NumValid;
950 SumNumDirty += NumDirty;
951 SumNumInLighting += NumInLighting;
952 SumNumInGenerator += NumInGenerator;
955 a_Output.
OutLn(
"Totals:");
956 a_Output.
OutLn(fmt::format(FMT_STRING(
" Num loaded chunks: {}"), SumNumValid));
957 a_Output.
OutLn(fmt::format(FMT_STRING(
" Num dirty chunks: {}"), SumNumDirty));
958 a_Output.
OutLn(fmt::format(FMT_STRING(
" Num chunks in lighting queue: {}"), SumNumInLighting));
959 a_Output.
OutLn(fmt::format(FMT_STRING(
" Num chunks in generator queue: {}"), SumNumInGenerator));
960 a_Output.
OutLn(fmt::format(FMT_STRING(
" Memory used by chunks: {} KiB ({} MiB)"), (SumMem + 1023) / 1024, (SumMem + 1024 * 1024 - 1) / (1024 * 1024)));
961 a_Output.
OutLn(
"Per-chunk memory size breakdown:");
1013 timeval Timeout{ 0, 0 };
1014 Timeout.tv_usec = 100 * 1000;
1018 FD_SET(STDIN_FILENO, &ReadSet);
1020 if (select(STDIN_FILENO + 1, &ReadSet,
nullptr,
nullptr, &Timeout) <= 0)
1027 if (!std::getline(std::cin, Command))
1039 if (!Command.empty())
1063 while (!
s_NextState.compare_exchange_strong(Current, a_NextState));
1083 static_cast<WORD
>(MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC)),
1093 WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &Record, 1, &Length);
eDimension
Dimension of a world.
std::unique_ptr< cLogger::cListener > MakeConsoleListener(bool a_IsService)
std::pair< bool, std::unique_ptr< cLogger::cListener > > MakeFileListener()
void LOG(std::string_view a_Format, const Args &... args)
void LOGINFO(std::string_view a_Format, const Args &... args)
bool g_RunAsService
If set to true, binary will attempt to run as a service.
AString TrimString(const AString &str)
Trims whitespace at both ends of the string.
AString StrToLower(const AString &s)
Returns a lower-cased copy of the string.
size_t RateCompareString(const AString &s1, const AString &s2)
Case-insensitive string comparison that returns a rating of equal-ness between [0 - s1....
std::vector< AString > AStringVector
Utilities to allow casting a cWorld to one of its interfaces without including World....
void ReloadPluginsNow(void)
Reloads all plugins, defaulting to settings.ini for settings location.
bool CallHookWorldStarted(cWorld &a_World)
BLOCKTYPE BlockTypes[NumBlocks]
The type used for block type operations and storage, AXIS_ORDER ordering.
HEIGHTTYPE HeightMap[Width *Width]
The type used for any heightmap operations and storage; idx = x + Width * z; Height points to the hig...
NIBBLETYPE BlockNibbles[NumBlocks/2]
The type used for block data in nibble format, AXIS_ORDER ordering.
EMCSBiome BiomeMap[Width *Width]
The type used for any biomemap operations and storage inside Cuberite, using Cuberite biomes (need no...
static float FASTBREAK_PERCENTAGE
The percentage how much a block has to be broken.
Interface for a callback that receives command output The Out() function is called for any output the...
void OutLn(const AString &aText)
Outputs the specified text, plus a newline.
Sends all command output to a log, line by line, when the command finishes processing.
Sends all command output to a log, line by line; deletes self when command finishes processing.
Container for a single chat message composed of multiple functional parts.
The crafting recipes are the configurations to build a result item out of a set of ingredient items.
void Start(int a_IntervalSec)
Starts the detection.
cClientHandle * GetClientHandle(void) const
const AString & GetName(void) const
int GetBurnTime(const cItem &a_Fuel) const
Returns the amount of time that the specified fuel burns, in ticks.
static cLogger & GetInstance(void)
cAttachment AttachListener(std::unique_ptr< cListener > a_Listener)
void Wait(void)
Waits until the event has been set.
void Set(void)
Sets the event - releases one thread that has been waiting in Wait().
void Stop(void)
Signals the thread to terminate and waits until it's finished.
void Stop(void)
Stops the authenticator thread.
void Start(cSettingsRepositoryInterface &a_Settings)
Starts the authenticator thread.
void Start(cSettingsRepositoryInterface &a_Settings, bool a_ShouldAuth)
Initializes the API; reads the settings from the specified ini file.
Version
The protocol version number, received from the client in the Handshake packet.
static AString GetVersionTextFromInt(cProtocol::Version a_ProtocolVersion)
Translates protocol version number into protocol version text: 49 -> "1.4.4".
The RecipeMapper handles the translation of crafting recipes into protocol specific recipe Ids.
cMonsterConfig * m_MonsterConfig
AString m_SettingsFilename
which ini file to load settings from, default is settings.ini
std::unique_ptr< cRecipeMapper > m_RecipeMapper
void SetSavingEnabled(bool a_SavingEnabled)
Sets whether saving chunks is enabled in all worlds (overrides however the worlds were already set)
void BroadcastPlayerListsHeaderFooter(const cCompositeChat &a_Header, const cCompositeChat &a_Footer)
Broadcast playerlist header and footer through all worlds.
static int GetPhysicalRAMUsage(void)
Returns the amount of virtual RAM used, in KiB.
void BroadcastChat(const AString &a_Message, eMessageType a_ChatPrefix=mtCustom)
Sends a chat message to all connected clients (in all worlds)
bool ForEachWorld(cWorldListCallback a_Callback)
Calls the callback for each world; returns true if the callback didn't abort (return true)
bool FindAndDoWithPlayer(const AString &a_PlayerName, cPlayerListCallback a_Callback)
Finds a player from a partial or complete player name and calls the callback - case-insensitive.
cPluginManager * m_PluginManager
void SaveAllChunksNow(void)
Saves all chunks in all worlds synchronously (waits until dirty chunks have been sent to the ChunkSto...
cFurnaceRecipe * GetFurnaceRecipe(void)
void KickUser(int a_ClientID, const AString &a_Reason)
Kicks the user, no matter in what world they are.
AStringVector GetPlayerTabCompletionMultiWorld(const AString &a_Text)
Returns the completions for a player name across all worlds.
void SendPlayerLists(cPlayer *a_DestPlayer)
Send playerlist of all worlds to player.
void StartWorlds(cDeadlockDetect &a_DeadlockDetect)
Starts each world's life.
cCraftingRecipes * m_CraftingRecipes
bool DoWithPlayerByUUID(const cUUID &a_PlayerUUID, cPlayerListCallback a_Callback)
Finds the player over his uuid and calls the callback.
std::chrono::steady_clock::time_point m_StartTime
The current time where the startup of the server has been completed.
void HandleInput()
Blocking reads and processes console input.
void SaveAllChunks(void)
Saves all chunks in all worlds.
cWorld * GetDefaultWorld(void)
NextState
States that the global cRoot can be in.
static void Restart()
Interrupts the server and restarts it, as if "/restart" was typed in the console.
static void Stop()
Interrupts the server and stops it, as if "/stop" typed in the console.
static int GetVirtualRAMUsage(void)
Returns the amount of virtual RAM used, in KiB.
static int GetFurnaceFuelBurnTime(const cItem &a_Fuel)
Returns the (read-write) storage for registered block types.
std::unique_ptr< cBrewingRecipes > m_BrewingRecipes
static cEvent s_StopEvent
bool DoWithPlayer(const AString &a_PlayerName, cPlayerListCallback a_Callback)
Finds the player using it's complete username and calls the callback.
void QueueExecuteConsoleCommand(const AString &a_Cmd, cCommandOutputCallback &a_Output)
Queues a console command for execution through the cServer class.
size_t GetTotalChunkCount(void)
Returns the number of chunks loaded.
static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum)
Returns the textual description of the protocol version: 49 -> "1.4.4".
void StopWorlds(cDeadlockDetect &a_DeadlockDetect)
Stops each world's threads, so that it's safe to unload them.
cWorld * GetWorld(const AString &a_WorldName)
Returns a pointer to the world specified.
static void TransitionNextState(NextState a_NextState)
Performs run state transition, enforcing guarantees about state transitions.
void BroadcastPlayerListsAddPlayer(const cPlayer &a_Player, const cClientHandle *a_Exclude=nullptr)
Broadcast playerlist addition through all worlds.
void LoadGlobalSettings()
The storage for all registered block types.
cAuthenticator m_Authenticator
void BroadcastPlayerListsRemovePlayer(const cPlayer &a_Player, const cClientHandle *a_Exclude=nullptr)
Broadcast playerlist removal through all worlds.
void LogChunkStats(cCommandOutputCallback &a_Output)
Writes chunkstats, for each world and totals, to the output callback.
cFurnaceRecipe * m_FurnaceRecipe
bool ForEachPlayer(cPlayerListCallback a_Callback)
Calls the callback for each player in all worlds.
static std::atomic< NextState > s_NextState
Indicates the next action of cRoot, whether to run, stop or restart.
void LoadWorlds(cDeadlockDetect &a_dd, cSettingsRepositoryInterface &a_Settings, bool a_IsNewIniFile)
Loads the worlds from settings.ini, creates the worldmap.
std::unique_ptr< cRankManager > m_RankManager
bool Run(cSettingsRepositoryInterface &a_OverridesRepo)
Run the server.
const AString & GetShutdownMessage(void) const
void ExecuteConsoleCommand(const AString &a_Cmd, cCommandOutputCallback &a_Output)
Executes the console command, sends output through the specified callback.
bool InitServer(cSettingsRepositoryInterface &a_Settings, bool a_ShouldAuth)
void KickUser(int a_ClientID, const AString &a_Reason)
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 bool AddKeyComment(const AString &keyname, const AString &comment)=0
Add a key comment, will always fail if the repository does not support comments.
virtual bool DeleteKeyComment(const AString &keyname, const int commentID)=0
Delete a key comment, will always fail if the repository does not support comments.
virtual std::vector< std::pair< AString, AString > > GetValues(AString a_keyName)=0
returns a vector containing a name, value pair for each value under the key
virtual AString GetValue(const AString &keyname, const AString &valuename, const AString &defValue="") const =0
Get the value at the specified key and value, returns defValue on failure.
virtual void AddValue(const AString &a_KeyName, const AString &a_ValueName, const AString &a_Value)=0
Adds a new value to the specified key.
virtual AString GetKeyComment(const AString &keyname, const int commentID) const =0
Return a key comment, returns "" for repositories that do not return comments.
virtual bool HasValue(const AString &a_KeyName, const AString &a_ValueName) const =0
Returns true iff the specified value exists.
void Stop(void)
Stops the HTTP server, if it was started.
bool Start(void)
Starts the HTTP server taking care of the webadmin.
bool Init(void)
Initializes the object.
void TabCompleteUserName(const AString &a_Text, AStringVector &a_Results)
Appends all usernames starting with a_Text (case-insensitive) into Results.