Allow servers to be run from directories other than build. Read/write files relative to binary instead of cwd (#834)

Allows the server to be run from a non-build directory.  Also only read or write files relative to the build directory, regardless of where the server is run from
This commit is contained in:
Jonathan Romano 2022-11-27 06:59:59 -05:00 committed by GitHub
parent e40a597f18
commit f8f5b731f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 158 additions and 46 deletions

View File

@ -11,6 +11,7 @@
#include "Database.h" #include "Database.h"
#include "dConfig.h" #include "dConfig.h"
#include "Diagnostics.h" #include "Diagnostics.h"
#include "BinaryPathFinder.h"
//RakNet includes: //RakNet includes:
#include "RakNetDefines.h" #include "RakNetDefines.h"
@ -150,7 +151,7 @@ int main(int argc, char** argv) {
} }
dLogger* SetupLogger() { dLogger* SetupLogger() {
std::string logPath = "./logs/AuthServer_" + std::to_string(time(nullptr)) + ".log"; std::string logPath = (BinaryPathFinder::GetBinaryDir() / ("logs/AuthServer_" + std::to_string(time(nullptr)) + ".log")).string();
bool logToConsole = false; bool logToConsole = false;
bool logDebugStatements = false; bool logDebugStatements = false;
#ifdef _DEBUG #ifdef _DEBUG

View File

@ -13,6 +13,7 @@
#include "dChatFilter.h" #include "dChatFilter.h"
#include "Diagnostics.h" #include "Diagnostics.h"
#include "AssetManager.h" #include "AssetManager.h"
#include "BinaryPathFinder.h"
#include "PlayerContainer.h" #include "PlayerContainer.h"
#include "ChatPacketHandler.h" #include "ChatPacketHandler.h"
@ -53,9 +54,14 @@ int main(int argc, char** argv) {
Game::logger->SetLogDebugStatements(config.GetValue("log_debug_statements") == "1"); Game::logger->SetLogDebugStatements(config.GetValue("log_debug_statements") == "1");
try { try {
std::string client_path = config.GetValue("client_location"); std::string clientPathStr = config.GetValue("client_location");
if (client_path.empty()) client_path = "./res"; if (clientPathStr.empty()) clientPathStr = "./res";
Game::assetManager = new AssetManager(client_path); std::filesystem::path clientPath = std::filesystem::path(clientPathStr);
if (clientPath.is_relative()) {
clientPath = BinaryPathFinder::GetBinaryDir() / clientPath;
}
Game::assetManager = new AssetManager(clientPath);
} catch (std::runtime_error& ex) { } catch (std::runtime_error& ex) {
Game::logger->Log("ChatServer", "Got an error while setting up assets: %s", ex.what()); Game::logger->Log("ChatServer", "Got an error while setting up assets: %s", ex.what());
@ -167,7 +173,7 @@ int main(int argc, char** argv) {
} }
dLogger* SetupLogger() { dLogger* SetupLogger() {
std::string logPath = "./logs/ChatServer_" + std::to_string(time(nullptr)) + ".log"; std::string logPath = (BinaryPathFinder::GetBinaryDir() / ("logs/ChatServer_" + std::to_string(time(nullptr)) + ".log")).string();
bool logToConsole = false; bool logToConsole = false;
bool logDebugStatements = false; bool logDebugStatements = false;
#ifdef _DEBUG #ifdef _DEBUG

View File

@ -0,0 +1,71 @@
#include <filesystem>
#include <string>
#include "BinaryPathFinder.h"
#include "dPlatforms.h"
#if defined(DARKFLAME_PLATFORM_WIN32)
#include <Windows.h>
#elif defined(DARKFLAME_PLATFORM_MACOS) || defined(DARKFLAME_PLATFORM_IOS)
#include <mach-o/dyld.h>
#elif defined(DARKFLAME_PLATFORM_FREEBSD)
#include <sys/types.h>
#include <sys/sysctl.h>
#include <stdlib.h>
#endif
std::filesystem::path BinaryPathFinder::binaryDir;
std::filesystem::path BinaryPathFinder::GetBinaryDir() {
if (!binaryDir.empty()) {
return binaryDir;
}
std::string pathStr;
// Derived from boost::dll::program_location, licensed under the Boost Software License: http://www.boost.org/LICENSE_1_0.txt
#if defined(DARKFLAME_PLATFORM_WIN32)
char path[MAX_PATH];
GetModuleFileName(NULL, path, MAX_PATH);
pathStr = std::string(path);
#elif defined(DARKFLAME_PLATFORM_MACOS) || defined(DARKFLAME_PLATFORM_IOS)
char path[1024];
uint32_t size = sizeof(path);
if (_NSGetExecutablePath(path, &size) == 0) {
pathStr = std::string(path);
} else {
// The filepath size is greater than our initial buffer size, so try again with the size
// that _NSGetExecutablePath told us it actually is
char *p = new char[size];
if (_NSGetExecutablePath(p, &size) != 0) {
throw std::runtime_error("Failed to get binary path from _NSGetExecutablePath");
}
pathStr = std::string(p);
delete[] p;
}
#elif defined(DARKFLAME_PLATFORM_FREEBSD)
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
char buf[10240];
size_t cb = sizeof(buf);
sysctl(mib, 4, buf, &cb, NULL, 0);
pathStr = std::string(buf);
#else // DARKFLAME_PLATFORM_LINUX || DARKFLAME_PLATFORM_UNIX || DARKFLAME_PLATFORM_ANDROID
pathStr = std::filesystem::read_symlink("/proc/self/exe");
#endif
// Some methods like _NSGetExecutablePath could return a symlink
// Either way, we need to get the parent path because we want the directory, not the binary itself
// We also ensure that it is an absolute path so that it is valid if we need to construct a path
// to exucute on unix systems (eg sudo BinaryPathFinder::GetBinaryDir() / WorldServer)
if (std::filesystem::is_symlink(pathStr)) {
binaryDir = std::filesystem::absolute(std::filesystem::read_symlink(pathStr).parent_path());
} else {
binaryDir = std::filesystem::absolute(std::filesystem::path(pathStr).parent_path());
}
return binaryDir;
}

View File

@ -0,0 +1,15 @@
#pragma once
#ifndef __BINARYPATHFINDER__H__
#define __BINARYPATHFINDER__H__
#include <filesystem>
class BinaryPathFinder {
private:
static std::filesystem::path binaryDir;
public:
static std::filesystem::path GetBinaryDir();
};
#endif //!__BINARYPATHFINDER__H__

View File

@ -15,6 +15,7 @@ set(DCOMMON_SOURCES "AMFFormat.cpp"
"Type.cpp" "Type.cpp"
"ZCompression.cpp" "ZCompression.cpp"
"BrickByBrickFix.cpp" "BrickByBrickFix.cpp"
"BinaryPathFinder.cpp"
) )
add_subdirectory(dClient) add_subdirectory(dClient)

View File

@ -1,15 +1,17 @@
#include <filesystem>
#include "AssetManager.h" #include "AssetManager.h"
#include "Game.h" #include "Game.h"
#include "dLogger.h" #include "dLogger.h"
#include <zlib.h> #include <zlib.h>
AssetManager::AssetManager(const std::string& path) { AssetManager::AssetManager(const std::filesystem::path& path) {
if (!std::filesystem::is_directory(path)) { if (!std::filesystem::is_directory(path)) {
throw std::runtime_error("Attempted to load asset bundle (" + path + ") however it is not a valid directory."); throw std::runtime_error("Attempted to load asset bundle (" + path.string() + ") however it is not a valid directory.");
} }
m_Path = std::filesystem::path(path); m_Path = path;
if (std::filesystem::exists(m_Path / "client") && std::filesystem::exists(m_Path / "versions")) { if (std::filesystem::exists(m_Path / "client") && std::filesystem::exists(m_Path / "versions")) {
m_AssetBundleType = eAssetBundleType::Packed; m_AssetBundleType = eAssetBundleType::Packed;

View File

@ -48,7 +48,7 @@ struct AssetMemoryBuffer : std::streambuf {
class AssetManager { class AssetManager {
public: public:
AssetManager(const std::string& path); AssetManager(const std::filesystem::path& path);
~AssetManager(); ~AssetManager();
std::filesystem::path GetResPath(); std::filesystem::path GetResPath();

View File

@ -1,9 +1,10 @@
#include "dConfig.h" #include "dConfig.h"
#include <sstream> #include <sstream>
#include "BinaryPathFinder.h"
dConfig::dConfig(const std::string& filepath) { dConfig::dConfig(const std::string& filepath) {
m_EmptyString = ""; m_EmptyString = "";
std::ifstream in(filepath); std::ifstream in(BinaryPathFinder::GetBinaryDir() / filepath);
if (!in.good()) return; if (!in.good()) return;
std::string line; std::string line;
@ -13,7 +14,7 @@ dConfig::dConfig(const std::string& filepath) {
} }
} }
std::ifstream sharedConfig("sharedconfig.ini", std::ios::in); std::ifstream sharedConfig(BinaryPathFinder::GetBinaryDir() / "sharedconfig.ini", std::ios::in);
if (!sharedConfig.good()) return; if (!sharedConfig.good()) return;
while (std::getline(sharedConfig, line)) { while (std::getline(sharedConfig, line)) {

View File

@ -6,12 +6,13 @@
#include "Game.h" #include "Game.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "dLogger.h" #include "dLogger.h"
#include "BinaryPathFinder.h"
#include <istream> #include <istream>
Migration LoadMigration(std::string path) { Migration LoadMigration(std::string path) {
Migration migration{}; Migration migration{};
std::ifstream file("./migrations/" + path); std::ifstream file(BinaryPathFinder::GetBinaryDir() / "migrations/" / path);
if (file.is_open()) { if (file.is_open()) {
std::string line; std::string line;
@ -37,7 +38,7 @@ void MigrationRunner::RunMigrations() {
sql::SQLString finalSQL = ""; sql::SQLString finalSQL = "";
bool runSd0Migrations = false; bool runSd0Migrations = false;
for (const auto& entry : GeneralUtils::GetFileNamesFromFolder("./migrations/dlu/")) { for (const auto& entry : GeneralUtils::GetFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/").string())) {
auto migration = LoadMigration("dlu/" + entry); auto migration = LoadMigration("dlu/" + entry);
if (migration.data.empty()) { if (migration.data.empty()) {
@ -97,7 +98,7 @@ void MigrationRunner::RunSQLiteMigrations() {
stmt->execute(); stmt->execute();
delete stmt; delete stmt;
for (const auto& entry : GeneralUtils::GetFileNamesFromFolder("./migrations/cdserver/")) { for (const auto& entry : GeneralUtils::GetFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "migrations/cdserver/").string())) {
auto migration = LoadMigration("cdserver/" + entry); auto migration = LoadMigration("cdserver/" + entry);
if (migration.data.empty()) continue; if (migration.data.empty()) continue;

View File

@ -64,6 +64,7 @@
#include "ScriptedActivityComponent.h" #include "ScriptedActivityComponent.h"
#include "LevelProgressionComponent.h" #include "LevelProgressionComponent.h"
#include "AssetManager.h" #include "AssetManager.h"
#include "BinaryPathFinder.h"
void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entity* entity, const SystemAddress& sysAddr) { void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entity* entity, const SystemAddress& sysAddr) {
std::string chatCommand; std::string chatCommand;
@ -248,7 +249,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
} }
if (chatCommand == "credits" || chatCommand == "info") { if (chatCommand == "credits" || chatCommand == "info") {
const auto& customText = chatCommand == "credits" ? VanityUtilities::ParseMarkdown("./vanity/CREDITS.md") : VanityUtilities::ParseMarkdown("./vanity/INFO.md"); const auto& customText = chatCommand == "credits" ? VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/CREDITS.md").string()) : VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/INFO.md").string());
{ {
AMFArrayValue args; AMFArrayValue args;

View File

@ -13,6 +13,7 @@
#include "tinyxml2.h" #include "tinyxml2.h"
#include "Game.h" #include "Game.h"
#include "dLogger.h" #include "dLogger.h"
#include "BinaryPathFinder.h"
#include <fstream> #include <fstream>
@ -27,7 +28,7 @@ void VanityUtilities::SpawnVanity() {
const uint32_t zoneID = Game::server->GetZoneID(); const uint32_t zoneID = Game::server->GetZoneID();
ParseXML("./vanity/NPC.xml"); ParseXML((BinaryPathFinder::GetBinaryDir() / "vanity/NPC.xml").string());
// Loop through all parties // Loop through all parties
for (const auto& party : m_Parties) { for (const auto& party : m_Parties) {
@ -131,7 +132,7 @@ void VanityUtilities::SpawnVanity() {
info.spawnerID = EntityManager::Instance()->GetZoneControlEntity()->GetObjectID(); info.spawnerID = EntityManager::Instance()->GetZoneControlEntity()->GetObjectID();
info.settings = { new LDFData<bool>(u"hasCustomText", true), info.settings = { new LDFData<bool>(u"hasCustomText", true),
new LDFData<std::string>(u"customText", ParseMarkdown("./vanity/TESTAMENT.md")) }; new LDFData<std::string>(u"customText", ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/TESTAMENT.md").string())) };
auto* entity = EntityManager::Instance()->CreateEntity(info); auto* entity = EntityManager::Instance()->CreateEntity(info);

View File

@ -9,13 +9,14 @@
#include "tinyxml2.h" #include "tinyxml2.h"
#include "Game.h" #include "Game.h"
#include "dConfig.h" #include "dConfig.h"
#include "BinaryPathFinder.h"
dLocale::dLocale() { dLocale::dLocale() {
if (Game::config->GetValue("locale_enabled") != "1") { if (Game::config->GetValue("locale_enabled") != "1") {
return; return;
} }
std::ifstream file(m_LocalePath); std::ifstream file(BinaryPathFinder::GetBinaryDir() / m_LocalePath);
if (!file.good()) { if (!file.good()) {
return; return;

View File

@ -10,6 +10,7 @@
#include "dMessageIdentifiers.h" #include "dMessageIdentifiers.h"
#include "MasterPackets.h" #include "MasterPackets.h"
#include "PacketUtils.h" #include "PacketUtils.h"
#include "BinaryPathFinder.h"
InstanceManager::InstanceManager(dLogger* logger, const std::string& externalIP) { InstanceManager::InstanceManager(dLogger* logger, const std::string& externalIP) {
mLogger = logger; mLogger = logger;
@ -48,13 +49,13 @@ Instance* InstanceManager::GetInstance(LWOMAPID mapID, bool isFriendTransfer, LW
//Start the actual process: //Start the actual process:
#ifdef _WIN32 #ifdef _WIN32
std::string cmd = "start ./WorldServer.exe -zone "; std::string cmd = "start " + (BinaryPathFinder::GetBinaryDir() / "WorldServer.exe").string() + " -zone ";
#else #else
std::string cmd; std::string cmd;
if (std::atoi(Game::config->GetValue("use_sudo_world").c_str())) { if (std::atoi(Game::config->GetValue("use_sudo_world").c_str())) {
cmd = "sudo ./WorldServer -zone "; cmd = "sudo " + (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone ";
} else { } else {
cmd = "./WorldServer -zone "; cmd = (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone ";
} }
#endif #endif
@ -300,10 +301,10 @@ Instance* InstanceManager::CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID clon
instance = new Instance(mExternalIP, port, mapID, ++m_LastInstanceID, cloneID, maxPlayers, maxPlayers, true, password); instance = new Instance(mExternalIP, port, mapID, ++m_LastInstanceID, cloneID, maxPlayers, maxPlayers, true, password);
//Start the actual process: //Start the actual process:
std::string cmd = "start ./WorldServer.exe -zone "; std::string cmd = "start " + (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone ";
#ifndef _WIN32 #ifndef _WIN32
cmd = "./WorldServer -zone "; cmd = (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone ";
#endif #endif
cmd.append(std::to_string(mapID)); cmd.append(std::to_string(mapID));

View File

@ -26,6 +26,7 @@
#include "dLogger.h" #include "dLogger.h"
#include "dServer.h" #include "dServer.h"
#include "AssetManager.h" #include "AssetManager.h"
#include "BinaryPathFinder.h"
//RakNet includes: //RakNet includes:
#include "RakNetDefines.h" #include "RakNetDefines.h"
@ -102,9 +103,14 @@ int main(int argc, char** argv) {
} }
try { try {
std::string client_path = config.GetValue("client_location"); std::string clientPathStr = config.GetValue("client_location");
if (client_path.empty()) client_path = "./res"; if (clientPathStr.empty()) clientPathStr = "./res";
Game::assetManager = new AssetManager(client_path); std::filesystem::path clientPath = std::filesystem::path(clientPathStr);
if (clientPath.is_relative()) {
clientPath = BinaryPathFinder::GetBinaryDir() / clientPath;
}
Game::assetManager = new AssetManager(clientPath);
} catch (std::runtime_error& ex) { } catch (std::runtime_error& ex) {
Game::logger->Log("MasterServer", "Got an error while setting up assets: %s", ex.what()); Game::logger->Log("MasterServer", "Got an error while setting up assets: %s", ex.what());
@ -127,18 +133,16 @@ int main(int argc, char** argv) {
stmt->executeUpdate(); stmt->executeUpdate();
delete stmt; delete stmt;
std::string res = "python3 ../thirdparty/docker-utils/utils/fdb_to_sqlite.py " + (Game::assetManager->GetResPath() / "cdclient.fdb").string(); std::string res = "python3 "
+ (BinaryPathFinder::GetBinaryDir() / "../thirdparty/docker-utils/utils/fdb_to_sqlite.py").string()
+ " --sqlite_path " + (Game::assetManager->GetResPath() / "CDServer.sqlite").string()
+ " " + (Game::assetManager->GetResPath() / "cdclient.fdb").string();
int result = system(res.c_str()); int result = system(res.c_str());
if (result != 0) { if (result != 0) {
Game::logger->Log("MasterServer", "Failed to convert fdb to sqlite"); Game::logger->Log("MasterServer", "Failed to convert fdb to sqlite");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (std::rename("./cdclient.sqlite", (Game::assetManager->GetResPath() / "CDServer.sqlite").string().c_str()) != 0) {
Game::logger->Log("MasterServer", "Failed to move cdclient file.");
return EXIT_FAILURE;
}
} }
//Connect to CDClient //Connect to CDClient
@ -363,7 +367,7 @@ int main(int argc, char** argv) {
dLogger* SetupLogger() { dLogger* SetupLogger() {
std::string logPath = std::string logPath =
"./logs/MasterServer_" + std::to_string(time(nullptr)) + ".log"; (BinaryPathFinder::GetBinaryDir() / ("logs/MasterServer_" + std::to_string(time(nullptr)) + ".log")).string();
bool logToConsole = false; bool logToConsole = false;
bool logDebugStatements = false; bool logDebugStatements = false;
#ifdef _DEBUG #ifdef _DEBUG
@ -738,28 +742,28 @@ void HandlePacket(Packet* packet) {
void StartChatServer() { void StartChatServer() {
#ifdef __APPLE__ #ifdef __APPLE__
//macOS doesn't need sudo to run on ports < 1024 //macOS doesn't need sudo to run on ports < 1024
system("./ChatServer&"); system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
#elif _WIN32 #elif _WIN32
system("start ./ChatServer.exe"); system(("start " + (BinaryPathFinder::GetBinaryDir() / "ChatServer.exe").string()).c_str());
#else #else
if (std::atoi(Game::config->GetValue("use_sudo_chat").c_str())) { if (std::atoi(Game::config->GetValue("use_sudo_chat").c_str())) {
system("sudo ./ChatServer&"); system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
} else { } else {
system("./ChatServer&"); system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
} }
#endif #endif
} }
void StartAuthServer() { void StartAuthServer() {
#ifdef __APPLE__ #ifdef __APPLE__
system("./AuthServer&"); system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
#elif _WIN32 #elif _WIN32
system("start ./AuthServer.exe"); system(("start " + (BinaryPathFinder::GetBinaryDir() / "AuthServer.exe").string()).c_str());
#else #else
if (std::atoi(Game::config->GetValue("use_sudo_auth").c_str())) { if (std::atoi(Game::config->GetValue("use_sudo_auth").c_str())) {
system("sudo ./AuthServer&"); system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
} else { } else {
system("./AuthServer&"); system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
} }
#endif #endif
} }

View File

@ -7,6 +7,7 @@
#include "dPlatforms.h" #include "dPlatforms.h"
#include "NiPoint3.h" #include "NiPoint3.h"
#include "BinaryIO.h" #include "BinaryIO.h"
#include "BinaryPathFinder.h"
#include "dZoneManager.h" #include "dZoneManager.h"
@ -43,7 +44,7 @@ dNavMesh::~dNavMesh() {
void dNavMesh::LoadNavmesh() { void dNavMesh::LoadNavmesh() {
std::string path = "./navmeshes/" + std::to_string(m_ZoneId) + ".bin"; std::string path = (BinaryPathFinder::GetBinaryDir() / "navmeshes/" / (std::to_string(m_ZoneId) + ".bin")).string();
if (!BinaryIO::DoesFileExist(path)) { if (!BinaryIO::DoesFileExist(path)) {
return; return;

View File

@ -17,6 +17,7 @@
#include "Metrics.hpp" #include "Metrics.hpp"
#include "PerformanceManager.h" #include "PerformanceManager.h"
#include "Diagnostics.h" #include "Diagnostics.h"
#include "BinaryPathFinder.h"
//RakNet includes: //RakNet includes:
#include "RakNetDefines.h" #include "RakNetDefines.h"
@ -146,9 +147,13 @@ int main(int argc, char** argv) {
if (config.GetValue("disable_chat") == "1") chatDisabled = true; if (config.GetValue("disable_chat") == "1") chatDisabled = true;
try { try {
std::string client_path = config.GetValue("client_location"); std::string clientPathStr = config.GetValue("client_location");
if (client_path.empty()) client_path = "./res"; if (clientPathStr.empty()) clientPathStr = "./res";
Game::assetManager = new AssetManager(client_path); std::filesystem::path clientPath = std::filesystem::path(clientPathStr);
if (clientPath.is_relative()) {
clientPath = BinaryPathFinder::GetBinaryDir() / clientPath;
}
Game::assetManager = new AssetManager(clientPath);
} catch (std::runtime_error& ex) { } catch (std::runtime_error& ex) {
Game::logger->Log("WorldServer", "Got an error while setting up assets: %s", ex.what()); Game::logger->Log("WorldServer", "Got an error while setting up assets: %s", ex.what());
@ -489,7 +494,7 @@ int main(int argc, char** argv) {
} }
dLogger* SetupLogger(int zoneID, int instanceID) { dLogger* SetupLogger(int zoneID, int instanceID) {
std::string logPath = "./logs/WorldServer_" + std::to_string(zoneID) + "_" + std::to_string(instanceID) + "_" + std::to_string(time(nullptr)) + ".log"; std::string logPath = (BinaryPathFinder::GetBinaryDir() / ("logs/WorldServer_" + std::to_string(zoneID) + "_" + std::to_string(instanceID) + "_" + std::to_string(time(nullptr)) + ".log")).string();
bool logToConsole = false; bool logToConsole = false;
bool logDebugStatements = false; bool logDebugStatements = false;
#ifdef _DEBUG #ifdef _DEBUG