fix: use generated bcrypt password for internal master connections (#1720)

* add password hashing for master server

* use define
This commit is contained in:
David Markowitz 2025-01-10 01:45:20 -08:00 committed by GitHub
parent 136133dde2
commit 8abc545bd1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 73 additions and 32 deletions

View File

@ -70,12 +70,15 @@ int main(int argc, char** argv) {
//Find out the master's IP: //Find out the master's IP:
std::string masterIP; std::string masterIP;
uint32_t masterPort = 1500; uint32_t masterPort = 1500;
std::string masterPassword;
auto masterInfo = Database::Get()->GetMasterInfo(); auto masterInfo = Database::Get()->GetMasterInfo();
if (masterInfo) { if (masterInfo) {
masterIP = masterInfo->ip; masterIP = masterInfo->ip;
masterPort = masterInfo->port; masterPort = masterInfo->port;
masterPassword = masterInfo->password;
} }
LOG("Master is at %s:%d", masterIP.c_str(), masterPort); LOG("Master is at %s:%d", masterIP.c_str(), masterPort);
Game::randomEngine = std::mt19937(time(0)); Game::randomEngine = std::mt19937(time(0));
@ -89,7 +92,7 @@ int main(int argc, char** argv) {
const auto externalIPString = Game::config->GetValue("external_ip"); const auto externalIPString = Game::config->GetValue("external_ip");
if (!externalIPString.empty()) ourIP = externalIPString; if (!externalIPString.empty()) ourIP = externalIPString;
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::lastSignal); Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::lastSignal, masterPassword);
//Run it until server gets a kill message from Master: //Run it until server gets a kill message from Master:
auto t = std::chrono::high_resolution_clock::now(); auto t = std::chrono::high_resolution_clock::now();

View File

@ -92,10 +92,12 @@ int main(int argc, char** argv) {
//Find out the master's IP: //Find out the master's IP:
std::string masterIP; std::string masterIP;
uint32_t masterPort = 1000; uint32_t masterPort = 1000;
std::string masterPassword;
auto masterInfo = Database::Get()->GetMasterInfo(); auto masterInfo = Database::Get()->GetMasterInfo();
if (masterInfo) { if (masterInfo) {
masterIP = masterInfo->ip; masterIP = masterInfo->ip;
masterPort = masterInfo->port; masterPort = masterInfo->port;
masterPassword = masterInfo->password;
} }
//It's safe to pass 'localhost' here, as the IP is only used as the external IP. //It's safe to pass 'localhost' here, as the IP is only used as the external IP.
std::string ourIP = "localhost"; std::string ourIP = "localhost";
@ -104,7 +106,7 @@ int main(int argc, char** argv) {
const auto externalIPString = Game::config->GetValue("external_ip"); const auto externalIPString = Game::config->GetValue("external_ip");
if (!externalIPString.empty()) ourIP = externalIPString; if (!externalIPString.empty()) ourIP = externalIPString;
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal); Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal, masterPassword);
const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false); const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF); Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);

View File

@ -9,10 +9,11 @@ public:
struct MasterInfo { struct MasterInfo {
std::string ip; std::string ip;
uint32_t port{}; uint32_t port{};
std::string password{};
}; };
// Set the master server ip and port. // Set the master server ip and port.
virtual void SetMasterIp(const std::string_view ip, const uint32_t port) = 0; virtual void SetMasterInfo(const MasterInfo& info) = 0;
// Get the master server info. // Get the master server info.
virtual std::optional<MasterInfo> GetMasterInfo() = 0; virtual std::optional<MasterInfo> GetMasterInfo() = 0;

View File

@ -96,7 +96,7 @@ public:
void UpdateAccountBan(const uint32_t accountId, const bool banned) override; void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override; void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override; void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
void SetMasterIp(const std::string_view ip, const uint32_t port) override; void SetMasterInfo(const IServers::MasterInfo& info) override;
std::optional<uint32_t> GetCurrentPersistentId() override; std::optional<uint32_t> GetCurrentPersistentId() override;
void InsertDefaultPersistentId() override; void InsertDefaultPersistentId() override;
void UpdatePersistentId(const uint32_t id) override; void UpdatePersistentId(const uint32_t id) override;

View File

@ -1,14 +1,14 @@
#include "MySQLDatabase.h" #include "MySQLDatabase.h"
void MySQLDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) { void MySQLDatabase::SetMasterInfo(const MasterInfo& info) {
// We only want our 1 entry anyways, so we can just delete all and reinsert the one we want // We only want our 1 entry anyways, so we can just delete all and reinsert the one we want
// since it would be two queries anyways. // since it would be two queries anyways.
ExecuteDelete("TRUNCATE TABLE servers;"); ExecuteDelete("TRUNCATE TABLE servers;");
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171022)", ip, port); ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`, `master_password`) VALUES ('master', ?, ?, 0, 171022, ?)", info.ip, info.port, info.password);
} }
std::optional<IServers::MasterInfo> MySQLDatabase::GetMasterInfo() { std::optional<IServers::MasterInfo> MySQLDatabase::GetMasterInfo() {
auto result = ExecuteSelect("SELECT ip, port FROM servers WHERE name='master' LIMIT 1;"); auto result = ExecuteSelect("SELECT ip, port, master_password FROM servers WHERE name='master' LIMIT 1;");
if (!result->next()) { if (!result->next()) {
return std::nullopt; return std::nullopt;
@ -18,6 +18,7 @@ std::optional<IServers::MasterInfo> MySQLDatabase::GetMasterInfo() {
toReturn.ip = result->getString("ip").c_str(); toReturn.ip = result->getString("ip").c_str();
toReturn.port = result->getInt("port"); toReturn.port = result->getInt("port");
toReturn.password = result->getString("master_password").c_str();
return toReturn; return toReturn;
} }

View File

@ -94,7 +94,7 @@ public:
void UpdateAccountBan(const uint32_t accountId, const bool banned) override; void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override; void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override; void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
void SetMasterIp(const std::string_view ip, const uint32_t port) override; void SetMasterInfo(const IServers::MasterInfo& info) override;
std::optional<uint32_t> GetCurrentPersistentId() override; std::optional<uint32_t> GetCurrentPersistentId() override;
void InsertDefaultPersistentId() override; void InsertDefaultPersistentId() override;
void UpdatePersistentId(const uint32_t id) override; void UpdatePersistentId(const uint32_t id) override;

View File

@ -1,14 +1,14 @@
#include "SQLiteDatabase.h" #include "SQLiteDatabase.h"
void SQLiteDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) { void SQLiteDatabase::SetMasterInfo(const MasterInfo& info) {
// We only want our 1 entry anyways, so we can just delete all and reinsert the one we want // We only want our 1 entry anyways, so we can just delete all and reinsert the one we want
// since it would be two queries anyways. // since it would be two queries anyways.
ExecuteDelete("DELETE FROM servers;"); ExecuteDelete("DELETE FROM servers;");
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171022)", ip, port); ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`, `master_password`) VALUES ('master', ?, ?, 0, 171022 ?)", info.ip, info.port, info.password);
} }
std::optional<IServers::MasterInfo> SQLiteDatabase::GetMasterInfo() { std::optional<IServers::MasterInfo> SQLiteDatabase::GetMasterInfo() {
auto [_, result] = ExecuteSelect("SELECT ip, port FROM servers WHERE name='master' LIMIT 1;"); auto [_, result] = ExecuteSelect("SELECT ip, port, master_password FROM servers WHERE name='master' LIMIT 1;");
if (result.eof()) { if (result.eof()) {
return std::nullopt; return std::nullopt;
@ -18,6 +18,7 @@ std::optional<IServers::MasterInfo> SQLiteDatabase::GetMasterInfo() {
toReturn.ip = result.getStringField("ip"); toReturn.ip = result.getStringField("ip");
toReturn.port = result.getIntField("port"); toReturn.port = result.getIntField("port");
toReturn.password = result.getStringField("master_password");
return toReturn; return toReturn;
} }

View File

@ -236,7 +236,7 @@ void TestSQLDatabase::InsertNewAccount(const std::string_view username, const st
} }
void TestSQLDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) { void TestSQLDatabase::SetMasterInfo(const IServers::MasterInfo& info) {
} }

View File

@ -73,7 +73,7 @@ class TestSQLDatabase : public GameDatabase {
void UpdateAccountBan(const uint32_t accountId, const bool banned) override; void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override; void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override; void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
void SetMasterIp(const std::string_view ip, const uint32_t port) override; void SetMasterInfo(const IServers::MasterInfo& info) override;
std::optional<uint32_t> GetCurrentPersistentId() override; std::optional<uint32_t> GetCurrentPersistentId() override;
void InsertDefaultPersistentId() override; void InsertDefaultPersistentId() override;
void UpdatePersistentId(const uint32_t id) override; void UpdatePersistentId(const uint32_t id) override;

View File

@ -62,6 +62,14 @@ std::map<uint32_t, std::string> activeSessions;
SystemAddress authServerMasterPeerSysAddr; SystemAddress authServerMasterPeerSysAddr;
SystemAddress chatServerMasterPeerSysAddr; SystemAddress chatServerMasterPeerSysAddr;
int GenerateBCryptPassword(const std::string& password, const int workFactor, char salt[BCRYPT_HASHSIZE], char hash[BCRYPT_HASHSIZE]) {
int32_t bcryptState = ::bcrypt_gensalt(workFactor, salt);
assert(bcryptState == 0);
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
assert(bcryptState == 0);
return 0;
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
constexpr uint32_t masterFramerate = mediumFramerate; constexpr uint32_t masterFramerate = mediumFramerate;
constexpr uint32_t masterFrameDelta = mediumFrameDelta; constexpr uint32_t masterFrameDelta = mediumFrameDelta;
@ -94,7 +102,7 @@ int main(int argc, char** argv) {
std::string(folder) + std::string(folder) +
") folder from your download to the binary directory or re-run cmake."; ") folder from your download to the binary directory or re-run cmake.";
LOG("%s", msg.c_str()); LOG("%s", msg.c_str());
// toss an error box up for windows users running the download // toss an error box up for windows users running the download
#ifdef DARKFLAME_PLATFORM_WIN32 #ifdef DARKFLAME_PLATFORM_WIN32
MessageBoxA(nullptr, msg.c_str(), "Missing Folder", MB_OK | MB_ICONERROR); MessageBoxA(nullptr, msg.c_str(), "Missing Folder", MB_OK | MB_ICONERROR);
#endif #endif
@ -238,10 +246,7 @@ int main(int argc, char** argv) {
// Regenerate hash based on new password // Regenerate hash based on new password
char salt[BCRYPT_HASHSIZE]; char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE]; char hash[BCRYPT_HASHSIZE];
int32_t bcryptState = ::bcrypt_gensalt(12, salt); assert(GenerateBCryptPassword(password, 12, salt, hash) == 0);
assert(bcryptState == 0);
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
assert(bcryptState == 0);
Database::Get()->UpdateAccountPassword(accountId->id, std::string(hash, BCRYPT_HASHSIZE)); Database::Get()->UpdateAccountPassword(accountId->id, std::string(hash, BCRYPT_HASHSIZE));
@ -279,10 +284,7 @@ int main(int argc, char** argv) {
//Generate new hash for bcrypt //Generate new hash for bcrypt
char salt[BCRYPT_HASHSIZE]; char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE]; char hash[BCRYPT_HASHSIZE];
int32_t bcryptState = ::bcrypt_gensalt(12, salt); assert(GenerateBCryptPassword(password, 12, salt, hash) == 0);
assert(bcryptState == 0);
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
assert(bcryptState == 0);
//Create account //Create account
try { try {
@ -318,15 +320,24 @@ int main(int argc, char** argv) {
const auto externalIPString = Game::config->GetValue("external_ip"); const auto externalIPString = Game::config->GetValue("external_ip");
if (!externalIPString.empty()) ourIP = externalIPString; if (!externalIPString.empty()) ourIP = externalIPString;
Game::server = new dServer(ourIP, ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::lastSignal); char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE];
const auto& cfgPassword = Game::config->GetValue("master_password");
GenerateBCryptPassword(!cfgPassword.empty() ? cfgPassword : "3.25DARKFLAME1", 13, salt, hash);
Game::server = new dServer(ourIP, ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::lastSignal, hash);
std::string master_server_ip = "localhost"; std::string master_server_ip = "localhost";
const auto masterServerIPString = Game::config->GetValue("master_ip"); const auto masterServerIPString = Game::config->GetValue("master_ip");
if (!masterServerIPString.empty()) master_server_ip = masterServerIPString; if (!masterServerIPString.empty()) master_server_ip = masterServerIPString;
if (master_server_ip == "") master_server_ip = Game::server->GetIP(); if (master_server_ip == "") master_server_ip = Game::server->GetIP();
IServers::MasterInfo info;
info.ip = master_server_ip;
info.port = Game::server->GetPort();
info.password = hash;
Database::Get()->SetMasterIp(master_server_ip, Game::server->GetPort()); Database::Get()->SetMasterInfo(info);
//Create additional objects here: //Create additional objects here:
PersistentIDManager::Initialize(); PersistentIDManager::Initialize();

View File

@ -3,4 +3,3 @@
#include "RakPeer.h" #include "RakPeer.h"
#define NET_PASSWORD_EXTERNAL "3.25 ND1" #define NET_PASSWORD_EXTERNAL "3.25 ND1"
#define NET_PASSWORD_INTERNAL "3.25 DARKFLAME1"

View File

@ -40,7 +40,21 @@ public:
} }
} ReceiveDownloadCompleteCB; } ReceiveDownloadCompleteCB;
dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, Logger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, Game::signal_t* lastSignal, unsigned int zoneID) { dServer::dServer(
const std::string& ip,
int port,
int instanceID,
int maxConnections,
bool isInternal,
bool useEncryption,
Logger* logger,
const std::string masterIP,
int masterPort,
ServerType serverType,
dConfig* config,
Game::signal_t* lastSignal,
const std::string& masterPassword,
unsigned int zoneID) {
mIP = ip; mIP = ip;
mPort = port; mPort = port;
mZoneID = zoneID; mZoneID = zoneID;
@ -56,6 +70,7 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect
mReplicaManager = nullptr; mReplicaManager = nullptr;
mServerType = serverType; mServerType = serverType;
mConfig = config; mConfig = config;
mMasterPassword = masterPassword;
mShouldShutdown = lastSignal; mShouldShutdown = lastSignal;
//Attempt to start our server here: //Attempt to start our server here:
mIsOkay = Startup(); mIsOkay = Startup();
@ -203,11 +218,11 @@ bool dServer::Startup() {
if (!mPeer->Startup(mMaxConnections, 10, &mSocketDescriptor, 1)) return false; if (!mPeer->Startup(mMaxConnections, 10, &mSocketDescriptor, 1)) return false;
if (mIsInternal) { if (mIsInternal) {
mPeer->SetIncomingPassword("3.25 DARKFLAME1", 15); mPeer->SetIncomingPassword(mMasterPassword.c_str(), mMasterPassword.size());
} else { } else {
UpdateBandwidthLimit(); UpdateBandwidthLimit();
UpdateMaximumMtuSize(); UpdateMaximumMtuSize();
mPeer->SetIncomingPassword("3.25 ND1", 8); mPeer->SetIncomingPassword(NET_PASSWORD_EXTERNAL, strnlen(NET_PASSWORD_EXTERNAL, sizeof(NET_PASSWORD_EXTERNAL)));
} }
mPeer->SetMaximumIncomingConnections(mMaxConnections); mPeer->SetMaximumIncomingConnections(mMaxConnections);
@ -257,7 +272,7 @@ void dServer::SetupForMasterConnection() {
bool dServer::ConnectToMaster() { bool dServer::ConnectToMaster() {
//LOG("Connection to Master %s:%d", mMasterIP.c_str(), mMasterPort); //LOG("Connection to Master %s:%d", mMasterIP.c_str(), mMasterPort);
return mMasterPeer->Connect(mMasterIP.c_str(), mMasterPort, "3.25 DARKFLAME1", 15); return mMasterPeer->Connect(mMasterIP.c_str(), mMasterPort, mMasterPassword.c_str(), mMasterPassword.size());
} }
void dServer::UpdateReplica() { void dServer::UpdateReplica() {

View File

@ -46,6 +46,7 @@ public:
ServerType serverType, ServerType serverType,
dConfig* config, dConfig* config,
Game::signal_t* shouldShutdown, Game::signal_t* shouldShutdown,
const std::string& masterPassword,
unsigned int zoneID = 0); unsigned int zoneID = 0);
~dServer(); ~dServer();
@ -121,4 +122,5 @@ protected:
std::string mMasterIP; std::string mMasterIP;
int mMasterPort; int mMasterPort;
std::chrono::steady_clock::time_point mStartTime = std::chrono::steady_clock::now(); std::chrono::steady_clock::time_point mStartTime = std::chrono::steady_clock::now();
std::string mMasterPassword;
}; };

View File

@ -202,11 +202,13 @@ int main(int argc, char** argv) {
//Find out the master's IP: //Find out the master's IP:
std::string masterIP = "localhost"; std::string masterIP = "localhost";
uint32_t masterPort = 1000; uint32_t masterPort = 1000;
std::string masterPassword;
auto masterInfo = Database::Get()->GetMasterInfo(); auto masterInfo = Database::Get()->GetMasterInfo();
if (masterInfo) { if (masterInfo) {
masterIP = masterInfo->ip; masterIP = masterInfo->ip;
masterPort = masterInfo->port; masterPort = masterInfo->port;
masterPassword = masterInfo->password;
} }
UserManager::Instance()->Initialize(); UserManager::Instance()->Initialize();
@ -214,7 +216,7 @@ int main(int argc, char** argv) {
const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false); const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF); Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);
Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, &Game::lastSignal, zoneID); Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, &Game::lastSignal, masterPassword, zoneID);
//Connect to the chat server: //Connect to the chat server:
uint32_t chatPort = 1501; uint32_t chatPort = 1501;
@ -223,7 +225,7 @@ int main(int argc, char** argv) {
auto chatSock = SocketDescriptor(static_cast<uint16_t>(ourPort + 2), 0); auto chatSock = SocketDescriptor(static_cast<uint16_t>(ourPort + 2), 0);
Game::chatServer = RakNetworkFactory::GetRakPeerInterface(); Game::chatServer = RakNetworkFactory::GetRakPeerInterface();
Game::chatServer->Startup(1, 30, &chatSock, 1); Game::chatServer->Startup(1, 30, &chatSock, 1);
Game::chatServer->Connect(masterIP.c_str(), chatPort, "3.25 ND1", 8); Game::chatServer->Connect(masterIP.c_str(), chatPort, NET_PASSWORD_EXTERNAL, strnlen(NET_PASSWORD_EXTERNAL, sizeof(NET_PASSWORD_EXTERNAL)));
//Set up other things: //Set up other things:
Game::randomEngine = std::mt19937(time(0)); Game::randomEngine = std::mt19937(time(0));
@ -371,7 +373,7 @@ int main(int argc, char** argv) {
if (framesSinceChatDisconnect >= chatReconnectionTime) { if (framesSinceChatDisconnect >= chatReconnectionTime) {
framesSinceChatDisconnect = 0; framesSinceChatDisconnect = 0;
Game::chatServer->Connect(masterIP.c_str(), chatPort, "3.25 ND1", 8); Game::chatServer->Connect(masterIP.c_str(), chatPort, NET_PASSWORD_EXTERNAL, strnlen(NET_PASSWORD_EXTERNAL, sizeof(NET_PASSWORD_EXTERNAL)));
} }
} else framesSinceChatDisconnect = 0; } else framesSinceChatDisconnect = 0;

View File

@ -0,0 +1 @@
ALTER TABLE servers ADD COLUMN master_password TEXT NOT NULL DEFAULT ('3.25 DARKFLAME1');

View File

@ -0,0 +1 @@
ALTER TABLE servers ADD COLUMN master_password TEXT NOT NULL DEFAULT ('3.25 DARKFLAME1');

View File

@ -9,3 +9,5 @@ world_port_start=3000
# 0 or 1, should autostart auth, chat, and char servers # 0 or 1, should autostart auth, chat, and char servers
prestart_servers=1 prestart_servers=1
master_password=3.25DARKFLAME1