mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2024-12-25 15:03:34 +00:00
Start on replacing MySQL
This commit is contained in:
parent
c6087ce77a
commit
8edfdd48a1
@ -55,34 +55,10 @@ int main(int argc, char** argv) {
|
|||||||
Game::logger->Log("AuthServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
|
Game::logger->Log("AuthServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
|
||||||
Game::logger->Log("AuthServer", "Compiled on: %s", __TIMESTAMP__);
|
Game::logger->Log("AuthServer", "Compiled on: %s", __TIMESTAMP__);
|
||||||
|
|
||||||
//Connect to the MySQL Database
|
Database::Connect(Game::config);
|
||||||
std::string mysql_host = Game::config->GetValue("mysql_host");
|
|
||||||
std::string mysql_database = Game::config->GetValue("mysql_database");
|
|
||||||
std::string mysql_username = Game::config->GetValue("mysql_username");
|
|
||||||
std::string mysql_password = Game::config->GetValue("mysql_password");
|
|
||||||
|
|
||||||
try {
|
// Get Master server IP and port
|
||||||
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
SocketDescriptor masterSock = Database::Connection->GetMasterServerIP();
|
||||||
} catch (sql::SQLException& ex) {
|
|
||||||
Game::logger->Log("AuthServer", "Got an error while connecting to the database: %s", ex.what());
|
|
||||||
Database::Destroy("AuthServer");
|
|
||||||
delete Game::server;
|
|
||||||
delete Game::logger;
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Find out the master's IP:
|
|
||||||
std::string masterIP;
|
|
||||||
uint32_t masterPort = 1500;
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
|
||||||
auto res = stmt->executeQuery();
|
|
||||||
while (res->next()) {
|
|
||||||
masterIP = res->getString(1).c_str();
|
|
||||||
masterPort = res->getInt(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete res;
|
|
||||||
delete stmt;
|
|
||||||
|
|
||||||
Game::randomEngine = std::mt19937(time(0));
|
Game::randomEngine = std::mt19937(time(0));
|
||||||
|
|
||||||
@ -92,7 +68,7 @@ int main(int argc, char** argv) {
|
|||||||
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
|
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
|
||||||
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
|
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
|
||||||
|
|
||||||
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::shouldShutdown);
|
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterSock.hostAddress, masterSock.port, ServerType::Auth, Game::config, &Game::shouldShutdown);
|
||||||
|
|
||||||
//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();
|
||||||
@ -131,18 +107,7 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
//Every 10 min we ping our sql server to keep it alive hopefully:
|
//Every 10 min we ping our sql server to keep it alive hopefully:
|
||||||
if (framesSinceLastSQLPing >= sqlPingTime) {
|
if (framesSinceLastSQLPing >= sqlPingTime) {
|
||||||
//Find out the master's IP for absolutely no reason:
|
Database::Connection->GetMasterServerIP();
|
||||||
std::string masterIP;
|
|
||||||
uint32_t masterPort;
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
|
||||||
auto res = stmt->executeQuery();
|
|
||||||
while (res->next()) {
|
|
||||||
masterIP = res->getString(1).c_str();
|
|
||||||
masterPort = res->getInt(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete res;
|
|
||||||
delete stmt;
|
|
||||||
|
|
||||||
framesSinceLastSQLPing = 0;
|
framesSinceLastSQLPing = 0;
|
||||||
} else framesSinceLastSQLPing++;
|
} else framesSinceLastSQLPing++;
|
||||||
@ -152,8 +117,6 @@ int main(int argc, char** argv) {
|
|||||||
std::this_thread::sleep_until(t);
|
std::this_thread::sleep_until(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Delete our objects here:
|
|
||||||
Database::Destroy("AuthServer");
|
|
||||||
delete Game::server;
|
delete Game::server;
|
||||||
delete Game::logger;
|
delete Game::logger;
|
||||||
delete Game::config;
|
delete Game::config;
|
||||||
|
@ -32,15 +32,12 @@ dChatFilter::dChatFilter(const std::string& filepath, bool dontGenerateDCF) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read player names that are ok as well:
|
// Read player names that are ok as well:
|
||||||
auto stmt = Database::CreatePreppedStmt("select name from charinfo;");
|
auto names = Database::Connection->GetAllCharacterNames();
|
||||||
auto res = stmt->executeQuery();
|
|
||||||
while (res->next()) {
|
for (auto& name : names) {
|
||||||
std::string line = res->getString(1).c_str();
|
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
|
||||||
std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase
|
m_ApprovedWords.push_back(CalculateHash(name));
|
||||||
m_ApprovedWords.push_back(CalculateHash(line));
|
|
||||||
}
|
}
|
||||||
delete res;
|
|
||||||
delete stmt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dChatFilter::~dChatFilter() {
|
dChatFilter::~dChatFilter() {
|
||||||
|
@ -77,34 +77,10 @@ int main(int argc, char** argv) {
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Connect to the MySQL Database
|
Database::Connect(Game::config);
|
||||||
std::string mysql_host = Game::config->GetValue("mysql_host");
|
|
||||||
std::string mysql_database = Game::config->GetValue("mysql_database");
|
|
||||||
std::string mysql_username = Game::config->GetValue("mysql_username");
|
|
||||||
std::string mysql_password = Game::config->GetValue("mysql_password");
|
|
||||||
|
|
||||||
try {
|
// Get Master server IP and port
|
||||||
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
SocketDescriptor masterSock = Database::Connection->GetMasterServerIP();
|
||||||
} catch (sql::SQLException& ex) {
|
|
||||||
Game::logger->Log("ChatServer", "Got an error while connecting to the database: %s", ex.what());
|
|
||||||
Database::Destroy("ChatServer");
|
|
||||||
delete Game::server;
|
|
||||||
delete Game::logger;
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Find out the master's IP:
|
|
||||||
std::string masterIP;
|
|
||||||
uint32_t masterPort = 1000;
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
|
||||||
auto res = stmt->executeQuery();
|
|
||||||
while (res->next()) {
|
|
||||||
masterIP = res->getString(1).c_str();
|
|
||||||
masterPort = res->getInt(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete res;
|
|
||||||
delete stmt;
|
|
||||||
|
|
||||||
//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.
|
||||||
uint32_t maxClients = 50;
|
uint32_t maxClients = 50;
|
||||||
@ -112,7 +88,7 @@ int main(int argc, char** argv) {
|
|||||||
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
|
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
|
||||||
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
|
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
|
||||||
|
|
||||||
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::shouldShutdown);
|
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterSock.hostAddress, masterSock.port, ServerType::Chat, Game::config, &Game::shouldShutdown);
|
||||||
|
|
||||||
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
|
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
|
||||||
|
|
||||||
@ -155,18 +131,7 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
//Every 10 min we ping our sql server to keep it alive hopefully:
|
//Every 10 min we ping our sql server to keep it alive hopefully:
|
||||||
if (framesSinceLastSQLPing >= sqlPingTime) {
|
if (framesSinceLastSQLPing >= sqlPingTime) {
|
||||||
//Find out the master's IP for absolutely no reason:
|
Database::Connection->GetMasterServerIP();
|
||||||
std::string masterIP;
|
|
||||||
uint32_t masterPort;
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
|
||||||
auto res = stmt->executeQuery();
|
|
||||||
while (res->next()) {
|
|
||||||
masterIP = res->getString(1).c_str();
|
|
||||||
masterPort = res->getInt(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete res;
|
|
||||||
delete stmt;
|
|
||||||
|
|
||||||
framesSinceLastSQLPing = 0;
|
framesSinceLastSQLPing = 0;
|
||||||
} else framesSinceLastSQLPing++;
|
} else framesSinceLastSQLPing++;
|
||||||
@ -177,7 +142,7 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete our objects here:
|
// Delete our objects here:
|
||||||
Database::Destroy("ChatServer");
|
Database::Destroy();
|
||||||
delete Game::server;
|
delete Game::server;
|
||||||
delete Game::logger;
|
delete Game::logger;
|
||||||
delete Game::config;
|
delete Game::config;
|
||||||
|
@ -41,14 +41,7 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
|
|||||||
mPlayers.insert(std::make_pair(data->playerID, data));
|
mPlayers.insert(std::make_pair(data->playerID, data));
|
||||||
Game::logger->Log("PlayerContainer", "Added user: %s (%llu), zone: %i", data->playerName.c_str(), data->playerID, data->zoneID.GetMapID());
|
Game::logger->Log("PlayerContainer", "Added user: %s (%llu), zone: %i", data->playerName.c_str(), data->playerID, data->zoneID.GetMapID());
|
||||||
|
|
||||||
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
|
Database::Connection->InsertIntoActivityLog(data->playerID, 0, time(nullptr), data->zoneID.GetMapID());
|
||||||
|
|
||||||
insertLog->setInt(1, data->playerID);
|
|
||||||
insertLog->setInt(2, 0);
|
|
||||||
insertLog->setUInt64(3, time(nullptr));
|
|
||||||
insertLog->setInt(4, data->zoneID.GetMapID());
|
|
||||||
|
|
||||||
insertLog->executeUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerContainer::RemovePlayer(Packet* packet) {
|
void PlayerContainer::RemovePlayer(Packet* packet) {
|
||||||
@ -85,14 +78,7 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
|
|||||||
Game::logger->Log("PlayerContainer", "Removed user: %llu", playerID);
|
Game::logger->Log("PlayerContainer", "Removed user: %llu", playerID);
|
||||||
mPlayers.erase(playerID);
|
mPlayers.erase(playerID);
|
||||||
|
|
||||||
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
|
Database::Connection->InsertIntoActivityLog(playerID, 1, time(nullptr), player->zoneID.GetMapID());
|
||||||
|
|
||||||
insertLog->setInt(1, playerID);
|
|
||||||
insertLog->setInt(2, 1);
|
|
||||||
insertLog->setUInt64(3, time(nullptr));
|
|
||||||
insertLog->setInt(4, player->zoneID.GetMapID());
|
|
||||||
|
|
||||||
insertLog->executeUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerContainer::MuteUpdate(Packet* packet) {
|
void PlayerContainer::MuteUpdate(Packet* packet) {
|
||||||
|
@ -9,5 +9,11 @@ foreach(file ${DDATABASE_TABLES_SOURCES})
|
|||||||
set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Tables/${file}")
|
set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Tables/${file}")
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
add_subdirectory(Databases)
|
||||||
|
|
||||||
|
foreach (file ${DDATABASE_DATABASES_SOURCES})
|
||||||
|
set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Databases/${file}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
add_library(dDatabase STATIC ${DDATABASE_SOURCES})
|
add_library(dDatabase STATIC ${DDATABASE_SOURCES})
|
||||||
target_link_libraries(dDatabase sqlite3 mariadbConnCpp)
|
target_link_libraries(dDatabase sqlite3 mariadbConnCpp)
|
||||||
|
@ -1,118 +1,26 @@
|
|||||||
#include "Database.h"
|
#include "Database.h"
|
||||||
#include "Game.h"
|
|
||||||
#include "dConfig.h"
|
#include "dConfig.h"
|
||||||
#include "dLogger.h"
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#pragma warning (disable:4251) //Disables SQL warnings
|
#include "Databases/MySQL.h"
|
||||||
|
|
||||||
sql::Driver* Database::driver;
|
void Database::Connect(dConfig* config) {
|
||||||
sql::Connection* Database::con;
|
bool useSqlite = true;
|
||||||
sql::Properties Database::props;
|
if (config->GetValue("mysql_host") != "" && config->GetValue("mysql_database") != "" && config->GetValue("mysql_username") != "" && config->GetValue("mysql_password") != "") {
|
||||||
std::string Database::database;
|
useSqlite = false;
|
||||||
|
}
|
||||||
|
|
||||||
void Database::Connect(const string& host, const string& database, const string& username, const string& password) {
|
if (useSqlite) {
|
||||||
|
|
||||||
//To bypass debug issues:
|
|
||||||
const char* szDatabase = database.c_str();
|
|
||||||
const char* szUsername = username.c_str();
|
|
||||||
const char* szPassword = password.c_str();
|
|
||||||
|
|
||||||
driver = sql::mariadb::get_driver_instance();
|
|
||||||
|
|
||||||
sql::Properties properties;
|
|
||||||
// The mariadb connector is *supposed* to handle unix:// and pipe:// prefixes to hostName, but there are bugs where
|
|
||||||
// 1) it tries to parse a database from the connection string (like in tcp://localhost:3001/darkflame) based on the
|
|
||||||
// presence of a /
|
|
||||||
// 2) even avoiding that, the connector still assumes you're connecting with a tcp socket
|
|
||||||
// So, what we do in the presence of a unix socket or pipe is to set the hostname to the protocol and localhost,
|
|
||||||
// which avoids parsing errors while still ensuring the correct connection type is used, and then setting the appropriate
|
|
||||||
// property manually (which the URL parsing fails to do)
|
|
||||||
const std::string UNIX_PROTO = "unix://";
|
|
||||||
const std::string PIPE_PROTO = "pipe://";
|
|
||||||
if (host.find(UNIX_PROTO) == 0) {
|
|
||||||
properties["hostName"] = "unix://localhost";
|
|
||||||
properties["localSocket"] = host.substr(UNIX_PROTO.length()).c_str();
|
|
||||||
} else if (host.find(PIPE_PROTO) == 0) {
|
|
||||||
properties["hostName"] = "pipe://localhost";
|
|
||||||
properties["pipe"] = host.substr(PIPE_PROTO.length()).c_str();
|
|
||||||
} else {
|
} else {
|
||||||
properties["hostName"] = host.c_str();
|
Database::Connection = new MySQLDatabase(config->GetValue("mysql_host"), config->GetValue("mysql_database"), config->GetValue("mysql_username"), config->GetValue("mysql_password"));
|
||||||
}
|
Database::ConnectionType = eConnectionTypes::MYSQL;
|
||||||
properties["user"] = szUsername;
|
|
||||||
properties["password"] = szPassword;
|
|
||||||
properties["autoReconnect"] = "true";
|
|
||||||
|
|
||||||
Database::props = properties;
|
|
||||||
Database::database = database;
|
|
||||||
|
|
||||||
Database::Connect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::Connect() {
|
Database::Connection->Connect();
|
||||||
// `connect(const Properties& props)` segfaults in windows debug, but
|
|
||||||
// `connect(const SQLString& host, const SQLString& user, const SQLString& pwd)` doesn't handle pipes/unix sockets correctly
|
|
||||||
if (Database::props.find("localSocket") != Database::props.end() || Database::props.find("pipe") != Database::props.end()) {
|
|
||||||
con = driver->connect(Database::props);
|
|
||||||
} else {
|
|
||||||
con = driver->connect(Database::props["hostName"].c_str(), Database::props["user"].c_str(), Database::props["password"].c_str());
|
|
||||||
}
|
|
||||||
con->setSchema(Database::database.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::Destroy(std::string source, bool log) {
|
void Database::Destroy() {
|
||||||
if (!con) return;
|
Database::Connection->Destroy();
|
||||||
|
delete Database::Connection;
|
||||||
if (log) {
|
|
||||||
if (source != "") Game::logger->Log("Database", "Destroying MySQL connection from %s!", source.c_str());
|
|
||||||
else Game::logger->Log("Database", "Destroying MySQL connection!");
|
|
||||||
}
|
|
||||||
|
|
||||||
con->close();
|
|
||||||
delete con;
|
|
||||||
} //Destroy
|
|
||||||
|
|
||||||
sql::Statement* Database::CreateStmt() {
|
|
||||||
sql::Statement* toReturn = con->createStatement();
|
|
||||||
return toReturn;
|
|
||||||
} //CreateStmt
|
|
||||||
|
|
||||||
sql::PreparedStatement* Database::CreatePreppedStmt(const std::string& query) {
|
|
||||||
const char* test = query.c_str();
|
|
||||||
size_t size = query.length();
|
|
||||||
sql::SQLString str(test, size);
|
|
||||||
|
|
||||||
if (!con) {
|
|
||||||
Connect();
|
|
||||||
Game::logger->Log("Database", "Trying to reconnect to MySQL");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!con->isValid() || con->isClosed()) {
|
|
||||||
delete con;
|
|
||||||
|
|
||||||
con = nullptr;
|
|
||||||
|
|
||||||
Connect();
|
|
||||||
Game::logger->Log("Database", "Trying to reconnect to MySQL from invalid or closed connection");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* stmt = con->prepareStatement(str);
|
|
||||||
|
|
||||||
return stmt;
|
|
||||||
} //CreatePreppedStmt
|
|
||||||
|
|
||||||
void Database::Commit() {
|
|
||||||
Database::con->commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Database::GetAutoCommit() {
|
|
||||||
// TODO This should not just access a pointer. A future PR should update this
|
|
||||||
// to check for null and throw an error if the connection is not valid.
|
|
||||||
return con->getAutoCommit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Database::SetAutoCommit(bool value) {
|
|
||||||
// TODO This should not just access a pointer. A future PR should update this
|
|
||||||
// to check for null and throw an error if the connection is not valid.
|
|
||||||
Database::con->setAutoCommit(value);
|
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <conncpp.hpp>
|
|
||||||
|
|
||||||
class MySqlException : public std::runtime_error {
|
#include "Databases/DatabaseBase.h"
|
||||||
public:
|
|
||||||
MySqlException() : std::runtime_error("MySQL error!") {}
|
class dConfig;
|
||||||
MySqlException(const std::string& msg) : std::runtime_error(msg.c_str()) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Database {
|
class Database {
|
||||||
private:
|
|
||||||
static sql::Driver* driver;
|
|
||||||
static sql::Connection* con;
|
|
||||||
static sql::Properties props;
|
|
||||||
static std::string database;
|
|
||||||
public:
|
public:
|
||||||
static void Connect(const std::string& host, const std::string& database, const std::string& username, const std::string& password);
|
static DatabaseBase* Connection;
|
||||||
static void Connect();
|
static eConnectionTypes ConnectionType;
|
||||||
static void Destroy(std::string source = "", bool log = true);
|
|
||||||
|
|
||||||
static sql::Statement* CreateStmt();
|
static void Connect(dConfig* config);
|
||||||
static sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
|
static void Destroy();
|
||||||
static void Commit();
|
|
||||||
static bool GetAutoCommit();
|
|
||||||
static void SetAutoCommit(bool value);
|
|
||||||
|
|
||||||
static std::string GetDatabase() { return database; }
|
|
||||||
static sql::Properties GetProperties() { return props; }
|
|
||||||
};
|
};
|
||||||
|
1
dDatabase/Databases/CMakeLists.txt
Normal file
1
dDatabase/Databases/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
set(DDATABASE_DATABASES_SOURCES "MySQL.cpp")
|
75
dDatabase/Databases/DatabaseBase.h
Normal file
75
dDatabase/Databases/DatabaseBase.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "RakNetTypes.h"
|
||||||
|
|
||||||
|
#include "Structures.h"
|
||||||
|
|
||||||
|
enum eConnectionTypes {
|
||||||
|
NONE,
|
||||||
|
MYSQL,
|
||||||
|
SQLITE
|
||||||
|
};
|
||||||
|
|
||||||
|
class DatabaseBase {
|
||||||
|
public:
|
||||||
|
virtual void Connect() = 0;
|
||||||
|
virtual void Destroy() = 0;
|
||||||
|
|
||||||
|
// Server Get
|
||||||
|
virtual SocketDescriptor GetMasterServerIP() = 0;
|
||||||
|
|
||||||
|
// Server Set
|
||||||
|
virtual void CreateServer(const std::string& name, const std::string& ip, uint16_t port, uint32_t state, uint32_t version) = 0;
|
||||||
|
virtual void SetServerIpAndPortByName(const std::string& name, const std::string& ip, uint16_t port) = 0;
|
||||||
|
|
||||||
|
// Misc
|
||||||
|
virtual void InsertIntoActivityLog(uint32_t playerId, uint32_t activityId, uint32_t timestamp, uint32_t zoneId) = 0;
|
||||||
|
virtual void InsertIntoCommandLog(uint32_t playerId, const std::string& command) = 0;
|
||||||
|
|
||||||
|
// Character Get
|
||||||
|
virtual CharacterInfo GetCharacterInfoByID(uint32_t id) = 0;
|
||||||
|
virtual CharacterInfo GetCharacterInfoByName(const std::string& name) = 0;
|
||||||
|
virtual std::string GetCharacterXMLByID(uint32_t id) = 0;
|
||||||
|
virtual std::vector<std::string> GetAllCharacterNames() = 0;
|
||||||
|
virtual std::vector<CharacterInfo> GetAllCharactersByAccountID(uint32_t accountId) = 0;
|
||||||
|
virtual bool IsCharacterNameAvailable(const std::string& name) = 0;
|
||||||
|
|
||||||
|
// Charater Write
|
||||||
|
virtual void CreateCharacterXML(uint32_t id, const std::string& xml) = 0;
|
||||||
|
virtual void UpdateCharacterXML(uint32_t id, const std::string& xml) = 0;
|
||||||
|
virtual void CreateCharacter(uint32_t id, uint32_t account_id, const std::string& name, const std::string& pending_name, bool needs_rename, uint64_t last_login) = 0;
|
||||||
|
|
||||||
|
// Character Delete
|
||||||
|
virtual void DeleteCharacter(uint32_t id) = 0;
|
||||||
|
|
||||||
|
// Friends Get
|
||||||
|
virtual bool AreBestFriends(uint32_t charId1, uint32_t charId2) = 0;
|
||||||
|
|
||||||
|
// Account Get
|
||||||
|
virtual AccountInfo GetAccountByName(const std::string& name) = 0;
|
||||||
|
virtual AccountInfo GetAccountByID(uint32_t id) = 0;
|
||||||
|
virtual uint32_t GetLatestCharacterOfAccount(uint32_t id) = 0;
|
||||||
|
|
||||||
|
// Pet Write
|
||||||
|
virtual void CreatePetName(uint64_t id, const std::string& name, bool approved) = 0;
|
||||||
|
|
||||||
|
// Pet Delete
|
||||||
|
virtual void DeletePetName(uint64_t id) = 0;
|
||||||
|
|
||||||
|
// Pet Get
|
||||||
|
virtual PetName GetPetName(uint64_t id) = 0;
|
||||||
|
|
||||||
|
// Keys Get
|
||||||
|
virtual bool IsKeyActive(uint32_t id) = 0;
|
||||||
|
|
||||||
|
// Object ID tracker Get
|
||||||
|
virtual uint32_t GetObjectIDTracker() = 0;
|
||||||
|
|
||||||
|
// Object ID tracker Set
|
||||||
|
virtual void SetObjectIDTracker(uint32_t id) = 0;
|
||||||
|
private:
|
||||||
|
|
||||||
|
};
|
477
dDatabase/Databases/MySQL.cpp
Normal file
477
dDatabase/Databases/MySQL.cpp
Normal file
@ -0,0 +1,477 @@
|
|||||||
|
#include "MySQL.h"
|
||||||
|
|
||||||
|
#pragma warning (disable:4251)
|
||||||
|
|
||||||
|
#include "Game.h"
|
||||||
|
#include "dConfig.h"
|
||||||
|
#include "dLogger.h"
|
||||||
|
|
||||||
|
MySQLDatabase::MySQLDatabase(const std::string& host, const std::string& database, const std::string& username, const std::string& password) {
|
||||||
|
this->m_Host = host;
|
||||||
|
this->m_Database = database;
|
||||||
|
this->m_Username = username;
|
||||||
|
this->m_Password = password;
|
||||||
|
|
||||||
|
m_Driver = sql::mariadb::get_driver_instance();
|
||||||
|
|
||||||
|
sql::Properties properties;
|
||||||
|
|
||||||
|
// The mariadb connector is *supposed* to handle unix:// and pipe:// prefixes to hostName, but there are bugs where
|
||||||
|
// 1) it tries to parse a database from the connection string (like in tcp://localhost:3001/darkflame) based on the
|
||||||
|
// presence of a /
|
||||||
|
// 2) even avoiding that, the connector still assumes you're connecting with a tcp socket
|
||||||
|
// So, what we do in the presence of a unix socket or pipe is to set the hostname to the protocol and localhost,
|
||||||
|
// which avoids parsing errors while still ensuring the correct connection type is used, and then setting the appropriate
|
||||||
|
// property manually (which the URL parsing fails to do)
|
||||||
|
const std::string UNIX_PROTO = "unix://";
|
||||||
|
const std::string PIPE_PROTO = "pipe://";
|
||||||
|
if (this->m_Host.find(UNIX_PROTO) == 0) {
|
||||||
|
properties["hostName"] = "unix://localhost";
|
||||||
|
properties["localSocket"] = this->m_Host.substr(UNIX_PROTO.length()).c_str();
|
||||||
|
} else if (this->m_Host.find(PIPE_PROTO) == 0) {
|
||||||
|
properties["hostName"] = "pipe://localhost";
|
||||||
|
properties["pipe"] = this->m_Host.substr(PIPE_PROTO.length()).c_str();
|
||||||
|
} else {
|
||||||
|
properties["hostName"] = this->m_Host.c_str();
|
||||||
|
}
|
||||||
|
properties["user"] = this->m_Username.c_str();
|
||||||
|
properties["password"] = this->m_Password.c_str();
|
||||||
|
properties["autoReconnect"] = "true";
|
||||||
|
|
||||||
|
this->m_Properties = properties;
|
||||||
|
this->m_Database = database;
|
||||||
|
}
|
||||||
|
|
||||||
|
MySQLDatabase::~MySQLDatabase() {
|
||||||
|
this->Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::Connect() {
|
||||||
|
try {
|
||||||
|
this->m_Driver = get_m_Driver_instance();
|
||||||
|
this->m_Properties["hostName"] = this->m_Host;
|
||||||
|
this->m_Properties["userName"] = this->m_Username;
|
||||||
|
this->m_Properties["password"] = this->m_Password;
|
||||||
|
this->m_Properties["OPT_RECONNECT"] = true;
|
||||||
|
|
||||||
|
this->m_Connection = this->m_Driver->connect(this->props);
|
||||||
|
this->m_Connection->setSchema(this->m_Database);
|
||||||
|
|
||||||
|
} catch (sql::SQLException& e) {
|
||||||
|
throw MySqlException(e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->m_Properties.find("localSocket") != this->m_Properties.end() || this->m_Properties.find("pipe") != Database::props.end()) {
|
||||||
|
this->m_Connection = m_Driver->connect(this->m_Properties);
|
||||||
|
} else {
|
||||||
|
this->m_Connection = m_Driver->connect(
|
||||||
|
this->m_Properties["hostName"].c_str(),
|
||||||
|
this->m_Properties["user"].c_str(),
|
||||||
|
this->m_Properties["password"].c_str()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this->m_Connection->setSchema(this->m_Database.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::Destroy() {
|
||||||
|
if (this->m_Connection != nullptr) {
|
||||||
|
this->m_Connection->close();
|
||||||
|
delete this->m_Connection;
|
||||||
|
this->m_Connection = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sql::Statement* MySQLDatabase::CreateStmt() {
|
||||||
|
sql::Statement* toReturn = con->createStatement();
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
sql::PreparedStatement* MySQLDatabase::CreatePreppedStmt(const std::string& query) {
|
||||||
|
const char* test = query.c_str();
|
||||||
|
size_t size = query.length();
|
||||||
|
sql::SQLString str(test, size);
|
||||||
|
|
||||||
|
if (!this->m_Connection) {
|
||||||
|
Connect();
|
||||||
|
Game::logger->Log("Database", "Trying to reconnect to MySQL");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!con->isValid() || con->isClosed()) {
|
||||||
|
delete this->m_Connection;
|
||||||
|
|
||||||
|
this->m_Connection = nullptr;
|
||||||
|
|
||||||
|
Connect();
|
||||||
|
Game::logger->Log("Database", "Trying to reconnect to MySQL from invalid or closed connection");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* stmt = this->m_Connection->prepareStatement(str);
|
||||||
|
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<sql::PreparedStatement> MySQLDatabase::CreatePreppedStmtUnique(const std::string& query) {
|
||||||
|
return CreatePreppedStmt(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<sql::ResultSet> MySQLDatabase::GetResultsOfStatement(sql::Statement* stmt) {
|
||||||
|
std::unique_ptr<sql::ResultSet> result(stmt->executeQuery());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MySQLDatabase::Commit() {
|
||||||
|
Database::con->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MySQLDatabase::GetAutoCommit() {
|
||||||
|
// TODO This should not just access a pointer. A future PR should update this
|
||||||
|
// to check for null and throw an error if the connection is not valid.
|
||||||
|
return m_Connection->getAutoCommit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::SetAutoCommit(bool value) {
|
||||||
|
// TODO This should not just access a pointer. A future PR should update this
|
||||||
|
// to check for null and throw an error if the connection is not valid.
|
||||||
|
m_Connection->setAutoCommit(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
SocketDescriptor MySQLDatabase::GetMasterServerIP() {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("SELECT ip, port FROM servers WHERE name = 'master';");
|
||||||
|
auto res = GetResultsOfStatement(stmt.get());
|
||||||
|
|
||||||
|
while (res->next()) {
|
||||||
|
return SocketDescriptor(res->getInt("port"), res->getString("ip"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return SocketDescriptor(0, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::CreateServer(const std::string& name, const std::string& ip, uint16_t port, uint32_t state, uint32_t version) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("INSERT INTO servers (name, ip, port, state, version) VALUES (?, ?, ?, ?, ?);");
|
||||||
|
stmt->setString(1, name);
|
||||||
|
stmt->setString(2, ip);
|
||||||
|
stmt->setInt(3, port);
|
||||||
|
stmt->setInt(4, state);
|
||||||
|
stmt->setInt(5, version);
|
||||||
|
|
||||||
|
stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::SetServerIpAndPortByName(const std::string& name, const std::string& ip, uint16_t port) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("UPDATE servers SET ip = ?, port = ? WHERE name = ?;");
|
||||||
|
stmt->setString(1, ip);
|
||||||
|
stmt->setInt(2, port);
|
||||||
|
stmt->setString(3, name);
|
||||||
|
|
||||||
|
stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> MySQLDatabase::GetAllCharacterNames() {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("SELECT name FROM charinfo;");
|
||||||
|
auto res = GetResultsOfStatement(stmt.get());
|
||||||
|
|
||||||
|
std::vector<std::string> names;
|
||||||
|
|
||||||
|
while (res->next()) {
|
||||||
|
names.push_back(res->getString("name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MySQLDatabase::IsCharacterNameAvailable(const std::string& name) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("SELECT name FROM charinfo WHERE name = ? OR pending_name = ?;");
|
||||||
|
stmt->setString(1, name);
|
||||||
|
stmt->setString(2, name);
|
||||||
|
|
||||||
|
auto res = GetResultsOfStatement(stmt.get());
|
||||||
|
|
||||||
|
return !res->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::InsertIntoActivityLog(uint32_t playerId, uint32_t activityId, uint32_t timestamp, uint32_t zoneId) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
|
||||||
|
|
||||||
|
stmt->setUInt(1, playerId);
|
||||||
|
stmt->setUInt(2, activityId);
|
||||||
|
stmt->setUInt(3, timestamp);
|
||||||
|
stmt->setUInt(4, zoneId);
|
||||||
|
|
||||||
|
stmt->executeUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::InsertIntoCommandLog(uint32_t playerId, const std::string& command) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("INSERT INTO command_log (character_id, command) VALUES (?, ?);");
|
||||||
|
|
||||||
|
stmt->setUInt(1, playerId);
|
||||||
|
stmt->setString(2, command);
|
||||||
|
|
||||||
|
stmt->executeUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterInfo MySQLDatabase::GetCharacterInfoByID(uint32_t id) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("SELECT account_id, name, pending_name, needs_rename, prop_clone_id, permission_map FROM charinfo WHERE id = ? LIMIT 1;");
|
||||||
|
stmt->setUInt(1, id);
|
||||||
|
|
||||||
|
auto res = GetResultsOfStatement(stmt.get());
|
||||||
|
|
||||||
|
while (res->next()) {
|
||||||
|
CharacterInfo info{};
|
||||||
|
info.AccountID = res->getUInt("account_id");
|
||||||
|
info.ID = id;
|
||||||
|
info.Name = res->getString("name");
|
||||||
|
info.PendingName = res->getString("pending_name");
|
||||||
|
info.NameRejected = res->getBoolean("needs_rename");
|
||||||
|
info.PropertyCloneID = res->getUInt("prop_clone_id");
|
||||||
|
info.PermissionMap = res->getUInt("permission_map");
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CharacterInfo{};
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterInfo MySQLDatabase::GetCharacterInfoByName(const std::string& name) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("SELECT id FROM charinfo WHERE name = ?");
|
||||||
|
stmt->setString(1, name);
|
||||||
|
|
||||||
|
auto res = GetResultsOfStatement(stmt.get());
|
||||||
|
|
||||||
|
while (res->next()) {
|
||||||
|
return GetCharacterByID(res->getUInt("id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return CharacterInfo{};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t MySQLDatabase::GetLatestCharacterOfAccount(uint32_t id) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("SELECT id FROM charinfo WHERE account_id = ? ORDER BY last_login DESC LIMIT 1;");
|
||||||
|
stmt->setUInt(1, id);
|
||||||
|
|
||||||
|
auto res = GetResultsOfStatement(stmt.get());
|
||||||
|
|
||||||
|
while (res->next()) {
|
||||||
|
return res->getUInt("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MySQLDatabase::GetCharacterXMLByID(uint32_t id) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("SELECT xml_data FROM charxml WHERE id = ? LIMIT 1;");
|
||||||
|
stmt->setUInt(1, id);
|
||||||
|
|
||||||
|
auto res = GetResultsOfStatement(stmt.get());
|
||||||
|
|
||||||
|
while (res->next()) {
|
||||||
|
return res->getString("xml_data");
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::CreateCharacterXML(uint32_t id, const std::string& xml) {
|
||||||
|
auto replaceStmt = CreatePreppedStmtUnique("INSERT INTO charxml (id, xml_data) VALUES (?, ?);");
|
||||||
|
replaceStmt->setUInt(1, id);
|
||||||
|
replaceStmt->setString(2, xml);
|
||||||
|
|
||||||
|
stmt->executeUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::UpdateCharacterXML(uint32_t id, const std::string& xml) {
|
||||||
|
auto replaceStmt = CreatePreppedStmtUnique("UPDATE charxml SET xml_data = ? WHERE id = ?;");
|
||||||
|
replaceStmt->setString(1, xml);
|
||||||
|
replaceStmt->setUInt(2, id);
|
||||||
|
|
||||||
|
stmt->executeUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::CreateCharacter(uint32_t id, uint32_t account_id, const std::string& name, const std::string& pending_name, bool needs_rename, uint64_t last_login) {
|
||||||
|
auto replaceStmt = CreatePreppedStmtUnique("INSERT INTO charinfo (id, account_id, name, pending_name, needs_rename, last_login) VALUES (?, ?, ?, ?, ?, ?);");
|
||||||
|
replaceStmt->setUInt(1, id);
|
||||||
|
replaceStmt->setUInt(2, account_id);
|
||||||
|
replaceStmt->setString(3, name);
|
||||||
|
replaceStmt->setString(4, pending_name);
|
||||||
|
replaceStmt->setBoolean(5, needs_rename);
|
||||||
|
replaceStmt->setUInt64(6, last_login);
|
||||||
|
|
||||||
|
replaceStmt->executeUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::DeleteCharacter(uint32_t id) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("DELETE FROM charxml WHERE id = ?;");
|
||||||
|
stmt->setUInt(1, id);
|
||||||
|
stmt->execute();
|
||||||
|
|
||||||
|
stmt = CreatePreppedStmtUnique("DELETE FROM command_log WHERE character_id = ?;");
|
||||||
|
stmt->setUInt(1, id);
|
||||||
|
stmt->execute();
|
||||||
|
|
||||||
|
stmt = CreatePreppedStmtUnique("DELETE FROM friends WHERE player_id = ? OR friend_id = ?;");
|
||||||
|
stmt->setUInt(1, id);
|
||||||
|
stmt->setUInt(2, id);
|
||||||
|
stmt->execute();
|
||||||
|
|
||||||
|
stmt = CreatePreppedStmtUnique("DELETE FROM leaderboard WHERE character_id = ?;");
|
||||||
|
stmt->setUInt(1, id);
|
||||||
|
stmt->execute();
|
||||||
|
|
||||||
|
stmt = CreatePreppedStmtUnique("DELETE FROM properties_contents WHERE property_id IN (SELECT id FROM properties WHERE owner_id = ?)");
|
||||||
|
stmt->setUInt(1, id);
|
||||||
|
stmt->execute();
|
||||||
|
|
||||||
|
stmt = CreatePreppedStmtUnique("DELETE FROM properties WHERE owner_id = ?;");
|
||||||
|
stmt->setUInt(1, id);
|
||||||
|
stmt->execute();
|
||||||
|
|
||||||
|
stmt = CreatePreppedStmtUnique("DELETE FROM ugc WHERE character_id = ?;");
|
||||||
|
stmt->setUInt(1, id);
|
||||||
|
stmt->execute();
|
||||||
|
|
||||||
|
stmt = CreatePreppedStmtUnique("DELETE FROM activity_log WHERE character_id = ?;");
|
||||||
|
stmt->setUInt(1, id);
|
||||||
|
stmt->execute();
|
||||||
|
|
||||||
|
stmt = CreatePreppedStmtUnique("DELETE FROM mail WHERE receiver_id = ?;");
|
||||||
|
stmt->setUInt(1, id);
|
||||||
|
stmt->execute();
|
||||||
|
|
||||||
|
stmt = CreatePreppedStmtUnique("DELETE FROM charinfo WHERE id = ?;");
|
||||||
|
stmt->setUInt(1, id);
|
||||||
|
stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountInfo MySQLDatabase::GetAccountByName(const std::string& name) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("SELECT id FROM accounts WHERE name = ? LIMIT 1;");
|
||||||
|
stmt->setString(1, name);
|
||||||
|
auto res = GetResultsOfStatement(stmt.get());
|
||||||
|
|
||||||
|
while (res->next()) {
|
||||||
|
return GetAccountByID(res->getUInt("id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return AccountInfo{};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MySQLDatabase::AreBestFriends(uint32_t goon1, uint32_t goon2) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;");
|
||||||
|
stmt->setUInt(1, goon1);
|
||||||
|
stmt->setUInt(2, goon2);
|
||||||
|
stmt->setUInt(3, goon2);
|
||||||
|
stmt->setUInt(4, goon1);
|
||||||
|
|
||||||
|
auto res = GetResultsOfStatement(stmt.get());
|
||||||
|
|
||||||
|
while (res->next()) {
|
||||||
|
return res->getInt("best_friend") == 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountInfo MySQLDatabase::GetAccountByID(uint32_t id) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("SELECT name, password, gm_level, locked, banned, play_key_id, created_at, mute_expire FROM accounts WHERE id = ? LIMIT 1;");
|
||||||
|
stmt->setUInt(1, id);
|
||||||
|
|
||||||
|
auto res = GetResultsOfStatement(stmt.get());
|
||||||
|
|
||||||
|
while (res->next()) {
|
||||||
|
AccountInfo info{};
|
||||||
|
info.ID = id;
|
||||||
|
info.Name = res->getString("name");
|
||||||
|
info.Password = res->getString("password");
|
||||||
|
info.GMLevel = res->getUInt("gm_level");
|
||||||
|
info.Locked = res->getBoolean("locked");
|
||||||
|
info.Banned = res->getBoolean("banned");
|
||||||
|
info.PlayKeyID = res->getUInt("play_key_id");
|
||||||
|
info.CreatedAt = res->getUInt64("created_at");
|
||||||
|
info.MuteExpire = res->getUInt64("mute_expire");
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AccountInfo{};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<CharacterInfo> MySQLDatabase::GetAllCharactersByAccountID(uint32_t accountId) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("SELECT id FROM charinfo WHERE account_id = ? LIMIT 4;");
|
||||||
|
stmt->setUInt(1, accountId);
|
||||||
|
|
||||||
|
auto res = GetResultsOfStatement(stmt.get());
|
||||||
|
|
||||||
|
std::vector<CharacterInfo> characters;
|
||||||
|
|
||||||
|
while (res->next()) {
|
||||||
|
characters.push_back(GetCharacterByID(res->getUInt("id")));
|
||||||
|
}
|
||||||
|
|
||||||
|
return characters;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::CreatePetName(uint64_t id, const std::string& name, bool approved) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("INSERT INTO pet_names (id, name, approved) VALUES (?, ?, ?);");
|
||||||
|
stmt->setUInt64(1, id);
|
||||||
|
stmt->setString(2, name);
|
||||||
|
stmt->setBoolean(3, approved);
|
||||||
|
stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::DeletePetName(uint64_t id) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("DELETE FROM pet_names WHERE id = ?;");
|
||||||
|
stmt->setUInt64(1, id);
|
||||||
|
stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
PetName MySQLDatabase::GetPetName(uint64_t id) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("SELECT name, approved FROM pet_names WHERE id = ? LIMIT 1;");
|
||||||
|
stmt->setUInt64(1, id);
|
||||||
|
|
||||||
|
auto res = GetResultsOfStatement(stmt.get());
|
||||||
|
|
||||||
|
while (res->next()) {
|
||||||
|
PetName name{};
|
||||||
|
name.ID = id;
|
||||||
|
name.Name = res->getString("name");
|
||||||
|
name.Approved = res->getBoolean("approved");
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PetName{};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MySQLDatabase::IsKeyActive(uint32_t id) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("SELECT * FROM play_keys WHERE id = ? AND active = 1 LIMIT 1;");
|
||||||
|
stmt->setUInt(1, id);
|
||||||
|
|
||||||
|
auto res = GetResultsOfStatement(stmt.get());
|
||||||
|
|
||||||
|
while (res->next()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t MySQLDatabase::GetObjectIDTracker() {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("SELECT last_object_id FROM object_id_tracker;");
|
||||||
|
auto res = GetResultsOfStatement(stmt.get());
|
||||||
|
|
||||||
|
while (res->next()) {
|
||||||
|
uint32_t id = res->getUInt("last_object_id");
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto stmt = CreatePreppedStmtUnique("INSERT INTO object_id_tracker (last_object_id) VALUES (1);");
|
||||||
|
stmt->execute();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::SetObjectIDTracker(uint32_t id) {
|
||||||
|
auto stmt = CreatePreppedStmtUnique("UPDATE object_id_tracker SET last_object_id = ?;");
|
||||||
|
stmt->setUInt(1, id);
|
||||||
|
stmt->execute();
|
||||||
|
}
|
79
dDatabase/Databases/MySQL.h
Normal file
79
dDatabase/Databases/MySQL.h
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "DatabaseBase.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <conncpp.hpp>
|
||||||
|
|
||||||
|
class MySqlException : public std::runtime_error {
|
||||||
|
public:
|
||||||
|
MySqlException() : std::runtime_error("MySQL error!") {}
|
||||||
|
MySqlException(const std::string& msg) : std::runtime_error(msg.c_str()) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MySQLDatabase : public DatabaseBase {
|
||||||
|
public:
|
||||||
|
MySQLDatabase(const std::string& host, const std::string& database, const std::string& username, const std::string& password);
|
||||||
|
~MySQLDatabase();
|
||||||
|
|
||||||
|
void Connect() override;
|
||||||
|
void Destroy() override;
|
||||||
|
|
||||||
|
sql::Statement* CreateStmt();
|
||||||
|
sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
|
||||||
|
|
||||||
|
std::unique_ptr<sql::PreparedStatement> CreatePreppedStmtUnique(const std::string& query);
|
||||||
|
std::unique_ptr<sql::ResultSet> GetResultsOfStatement(sql::Statement* stmt);
|
||||||
|
|
||||||
|
void Commit() override;
|
||||||
|
bool GetAutoCommit() override;
|
||||||
|
void SetAutoCommit(bool value) override;
|
||||||
|
|
||||||
|
SocketDescriptor GetMasterServerIP() override;
|
||||||
|
|
||||||
|
void CreateServer(const std::string& name, const std::string& ip, uint16_t port, uint32_t state, uint32_t version) override;
|
||||||
|
void SetServerIpAndPortByName(const std::string& name, const std::string& ip, uint16_t port) override;
|
||||||
|
|
||||||
|
void InsertIntoActivityLog(uint32_t playerId, uint32_t activityId, uint32_t timestamp, uint32_t zoneId) override;
|
||||||
|
void InsertIntoCommandLog(uint32_t playerId, const std::string& command) override;
|
||||||
|
|
||||||
|
CharacterInfo GetCharacterInfoByID(uint32_t id) override;
|
||||||
|
CharacterInfo GetCharacterInfoByName(const std::string& name) override;
|
||||||
|
std::string GetCharacterXMLByID(uint32_t id) override;
|
||||||
|
std::vector<std::string> GetAllCharacterNames() override;
|
||||||
|
std::vector<CharacterInfo> GetAllCharactersByAccountID(uint32_t accountId) override;
|
||||||
|
bool IsCharacterNameAvailable(const std::string& name) override;
|
||||||
|
|
||||||
|
void CreateCharacterXML(uint32_t id, const std::string& xml) override;
|
||||||
|
void UpdateCharacterXML(uint32_t id, const std::string& xml) override;
|
||||||
|
void CreateCharacter(uint32_t id, uint32_t account_id, const std::string& name, const std::string& pending_name, bool needs_rename, uint64_t last_login) override;
|
||||||
|
|
||||||
|
void DeleteCharacter(uint32_t id) override;
|
||||||
|
|
||||||
|
bool AreBestFriends(uint32_t charId1, uint32_t charId2) override;
|
||||||
|
|
||||||
|
AccountInfo GetAccountByName(const std::string& name) override;
|
||||||
|
AccountInfo GetAccountByID(uint32_t id) override;
|
||||||
|
uint32_t GetLatestCharacterOfAccount(uint32_t id) override;
|
||||||
|
|
||||||
|
void CreatePetName(uint64_t id, const std::string& name, bool approved) override;
|
||||||
|
|
||||||
|
void DeletePetName(uint64_t id) override;
|
||||||
|
|
||||||
|
PetName GetPetName(uint64_t id) override;
|
||||||
|
|
||||||
|
bool IsKeyActive(uint32_t id) override;
|
||||||
|
|
||||||
|
uint32_t GetObjectIDTracker() override;
|
||||||
|
|
||||||
|
void SetObjectIDTracker(uint32_t id) override;
|
||||||
|
private:
|
||||||
|
std::string m_Host;
|
||||||
|
std::string m_Database;
|
||||||
|
std::string m_Username;
|
||||||
|
std::string m_Password;
|
||||||
|
|
||||||
|
sql::Connection* m_Connection;
|
||||||
|
sql::Driver* m_Driver;
|
||||||
|
sql::Properties m_Properties;
|
||||||
|
};
|
33
dDatabase/Databases/Structures.h
Normal file
33
dDatabase/Databases/Structures.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "ePermissionMap.h"
|
||||||
|
|
||||||
|
struct CharacterInfo {
|
||||||
|
uint32_t AccountID;
|
||||||
|
uint32_t ID;
|
||||||
|
std::string Name;
|
||||||
|
std::string PendingName;
|
||||||
|
bool NameRejected;
|
||||||
|
uint32_t PropertyCloneID;
|
||||||
|
ePermissionMap PermissionMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AccountInfo {
|
||||||
|
uint32_t ID;
|
||||||
|
std::string Name;
|
||||||
|
std::string Password;
|
||||||
|
uint32_t MaxGMLevel;
|
||||||
|
bool Locked;
|
||||||
|
bool Banned;
|
||||||
|
uint32_t PlayKeyID;
|
||||||
|
uint64_t CreatedAt;
|
||||||
|
uint64_t MuteExpire;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PetName {
|
||||||
|
uint64_t ID;
|
||||||
|
std::string Name;
|
||||||
|
bool Approved;
|
||||||
|
};
|
@ -26,39 +26,16 @@ Character::Character(uint32_t id, User* parentUser) {
|
|||||||
//First load the name, etc:
|
//First load the name, etc:
|
||||||
m_ID = id;
|
m_ID = id;
|
||||||
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt(
|
// Load the character
|
||||||
"SELECT name, pending_name, needs_rename, prop_clone_id, permission_map FROM charinfo WHERE id=? LIMIT 1;"
|
auto character = Database::Connection->GetCharacterByID(m_ID);
|
||||||
);
|
m_Name = character.Name;
|
||||||
|
m_UnapprovedName = character.PendingName;
|
||||||
|
m_NameRejected = character.NameRejected;
|
||||||
|
m_PropertyCloneID = character.PropertyCloneID;
|
||||||
|
m_PermissionMap = character.PermissionMap;
|
||||||
|
|
||||||
stmt->setInt64(1, id);
|
// Load the xmlData now
|
||||||
|
m_XMLData = Database::Connection->GetCharacterXMLByID(m_ID);
|
||||||
sql::ResultSet* res = stmt->executeQuery();
|
|
||||||
|
|
||||||
while (res->next()) {
|
|
||||||
m_Name = res->getString(1).c_str();
|
|
||||||
m_UnapprovedName = res->getString(2).c_str();
|
|
||||||
m_NameRejected = res->getBoolean(3);
|
|
||||||
m_PropertyCloneID = res->getUInt(4);
|
|
||||||
m_PermissionMap = static_cast<ePermissionMap>(res->getUInt64(5));
|
|
||||||
}
|
|
||||||
|
|
||||||
delete res;
|
|
||||||
delete stmt;
|
|
||||||
|
|
||||||
//Load the xmlData now:
|
|
||||||
sql::PreparedStatement* xmlStmt = Database::CreatePreppedStmt(
|
|
||||||
"SELECT xml_data FROM charxml WHERE id=? LIMIT 1;"
|
|
||||||
);
|
|
||||||
|
|
||||||
xmlStmt->setInt64(1, id);
|
|
||||||
|
|
||||||
sql::ResultSet* xmlRes = xmlStmt->executeQuery();
|
|
||||||
while (xmlRes->next()) {
|
|
||||||
m_XMLData = xmlRes->getString(1).c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
delete xmlRes;
|
|
||||||
delete xmlStmt;
|
|
||||||
|
|
||||||
m_ZoneID = 0; //TEMP! Set back to 0 when done. This is so we can see loading screen progress for testing.
|
m_ZoneID = 0; //TEMP! Set back to 0 when done. This is so we can see loading screen progress for testing.
|
||||||
m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused.
|
m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused.
|
||||||
@ -85,38 +62,16 @@ Character::~Character() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Character::UpdateFromDatabase() {
|
void Character::UpdateFromDatabase() {
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt(
|
// Load the character
|
||||||
"SELECT name, pending_name, needs_rename, prop_clone_id, permission_map FROM charinfo WHERE id=? LIMIT 1;"
|
auto character = Database::Connection->GetCharacterByID(m_ID);
|
||||||
);
|
m_Name = character.Name;
|
||||||
|
m_UnapprovedName = character.PendingName;
|
||||||
|
m_NameRejected = character.NameRejected;
|
||||||
|
m_PropertyCloneID = character.PropertyCloneID;
|
||||||
|
m_PermissionMap = character.PermissionMap;
|
||||||
|
|
||||||
stmt->setInt64(1, m_ID);
|
// Load the xmlData now
|
||||||
|
m_XMLData = Database::Connection->GetCharacterXMLByID(m_ID);
|
||||||
sql::ResultSet* res = stmt->executeQuery();
|
|
||||||
|
|
||||||
while (res->next()) {
|
|
||||||
m_Name = res->getString(1).c_str();
|
|
||||||
m_UnapprovedName = res->getString(2).c_str();
|
|
||||||
m_NameRejected = res->getBoolean(3);
|
|
||||||
m_PropertyCloneID = res->getUInt(4);
|
|
||||||
m_PermissionMap = static_cast<ePermissionMap>(res->getUInt64(5));
|
|
||||||
}
|
|
||||||
|
|
||||||
delete res;
|
|
||||||
delete stmt;
|
|
||||||
|
|
||||||
//Load the xmlData now:
|
|
||||||
sql::PreparedStatement* xmlStmt = Database::CreatePreppedStmt(
|
|
||||||
"SELECT xml_data FROM charxml WHERE id=? LIMIT 1;"
|
|
||||||
);
|
|
||||||
xmlStmt->setInt64(1, m_ID);
|
|
||||||
|
|
||||||
sql::ResultSet* xmlRes = xmlStmt->executeQuery();
|
|
||||||
while (xmlRes->next()) {
|
|
||||||
m_XMLData = xmlRes->getString(1).c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
delete xmlRes;
|
|
||||||
delete xmlStmt;
|
|
||||||
|
|
||||||
m_ZoneID = 0; //TEMP! Set back to 0 when done. This is so we can see loading screen progress for testing.
|
m_ZoneID = 0; //TEMP! Set back to 0 when done. This is so we can see loading screen progress for testing.
|
||||||
m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused.
|
m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused.
|
||||||
@ -409,13 +364,8 @@ void Character::WriteToDatabase() {
|
|||||||
m_Doc->Print(printer);
|
m_Doc->Print(printer);
|
||||||
m_XMLData = printer->CStr();
|
m_XMLData = printer->CStr();
|
||||||
|
|
||||||
//Finally, save to db:
|
// Save to DB
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charxml SET xml_data=? WHERE id=?");
|
Database::Connection->WriteCharacterXMl(m_ID, m_XMLData);
|
||||||
stmt->setString(1, m_XMLData.c_str());
|
|
||||||
stmt->setUInt(2, m_ID);
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
delete printer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Character::SetPlayerFlag(const uint32_t flagId, const bool value) {
|
void Character::SetPlayerFlag(const uint32_t flagId, const bool value) {
|
||||||
|
@ -27,38 +27,22 @@ User::User(const SystemAddress& sysAddr, const std::string& username, const std:
|
|||||||
//This needs to be re-enabled / updated whenever the mute stuff is moved to another table.
|
//This needs to be re-enabled / updated whenever the mute stuff is moved to another table.
|
||||||
//This was only done because otherwise the website's account page dies and the website is waiting on a migration to wordpress.
|
//This was only done because otherwise the website's account page dies and the website is waiting on a migration to wordpress.
|
||||||
|
|
||||||
//sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id, gmlevel, mute_expire FROM accounts WHERE name=? LIMIT 1;");
|
auto account = Database::Connection->GetAccountByName(username);
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id, gm_level FROM accounts WHERE name=? LIMIT 1;");
|
|
||||||
stmt->setString(1, username.c_str());
|
|
||||||
|
|
||||||
sql::ResultSet* res = stmt->executeQuery();
|
m_AccountID = account.ID;
|
||||||
while (res->next()) {
|
m_MaxGMLevel = static_cast<eGameMasterLevel>(account.MaxGMLevel);
|
||||||
m_AccountID = res->getUInt(1);
|
m_MuteExpire = 0;
|
||||||
m_MaxGMLevel = static_cast<eGameMasterLevel>(res->getInt(2));
|
|
||||||
m_MuteExpire = 0; //res->getUInt64(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete res;
|
|
||||||
delete stmt;
|
|
||||||
|
|
||||||
//If we're loading a zone, we'll load the last used (aka current) character:
|
//If we're loading a zone, we'll load the last used (aka current) character:
|
||||||
if (Game::server->GetZoneID() != 0) {
|
if (Game::server->GetZoneID() != 0) {
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE account_id=? ORDER BY last_login DESC LIMIT 1;");
|
uint32_t characterId = Database::Connection->GetLatestCharacterOfAccount(m_AccountID);
|
||||||
stmt->setUInt(1, m_AccountID);
|
|
||||||
|
|
||||||
sql::ResultSet* res = stmt->executeQuery();
|
if (characterId != 0) {
|
||||||
if (res->rowsCount() > 0) {
|
Character* character = new Character(characterId, this);
|
||||||
while (res->next()) {
|
|
||||||
LWOOBJID objID = res->getUInt64(1);
|
|
||||||
Character* character = new Character(uint32_t(objID), this);
|
|
||||||
m_Characters.push_back(character);
|
m_Characters.push_back(character);
|
||||||
Game::logger->Log("User", "Loaded %llu as it is the last used char", objID);
|
Game::logger->Log("User", "Loaded %u as it is the last used char", characterId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete res;
|
|
||||||
delete stmt;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
User::User(const User& other) {
|
User::User(const User& other) {
|
||||||
|
@ -159,17 +159,7 @@ void UserManager::DeletePendingRemovals() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool UserManager::IsNameAvailable(const std::string& requestedName) {
|
bool UserManager::IsNameAvailable(const std::string& requestedName) {
|
||||||
bool toReturn = false; //To allow for a clean exit
|
return Database::Connection->IsCharacterNameAvailable(requestedName);
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE name=? OR pending_name=? LIMIT 1;");
|
|
||||||
stmt->setString(1, requestedName.c_str());
|
|
||||||
stmt->setString(2, requestedName.c_str());
|
|
||||||
|
|
||||||
sql::ResultSet* res = stmt->executeQuery();
|
|
||||||
if (res->rowsCount() == 0) toReturn = true;
|
|
||||||
|
|
||||||
delete stmt;
|
|
||||||
delete res;
|
|
||||||
return toReturn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string UserManager::GetPredefinedName(uint32_t firstNameIndex, uint32_t middleNameIndex, uint32_t lastNameIndex) {
|
std::string UserManager::GetPredefinedName(uint32_t firstNameIndex, uint32_t middleNameIndex, uint32_t lastNameIndex) {
|
||||||
@ -201,10 +191,8 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) {
|
|||||||
User* u = GetUser(sysAddr);
|
User* u = GetUser(sysAddr);
|
||||||
if (!u) return;
|
if (!u) return;
|
||||||
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE account_id=? ORDER BY last_login DESC LIMIT 4;");
|
auto charInfos = Database::Connection->GetAllCharactersByAccountID(u->GetAccountID());
|
||||||
stmt->setUInt(1, u->GetAccountID());
|
|
||||||
|
|
||||||
sql::ResultSet* res = stmt->executeQuery();
|
|
||||||
std::vector<Character*>& chars = u->GetCharacters();
|
std::vector<Character*>& chars = u->GetCharacters();
|
||||||
|
|
||||||
for (size_t i = 0; i < chars.size(); ++i) {
|
for (size_t i = 0; i < chars.size(); ++i) {
|
||||||
@ -232,16 +220,12 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) {
|
|||||||
|
|
||||||
chars.clear();
|
chars.clear();
|
||||||
|
|
||||||
while (res->next()) {
|
for (const auto& info : charInfos) {
|
||||||
LWOOBJID objID = res->getUInt64(1);
|
Character* character = new Character(info.ID, u);
|
||||||
Character* character = new Character(uint32_t(objID), u);
|
|
||||||
character->SetIsNewLogin();
|
character->SetIsNewLogin();
|
||||||
chars.push_back(character);
|
chars.push_back(character);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete res;
|
|
||||||
delete stmt;
|
|
||||||
|
|
||||||
WorldPackets::SendCharacterList(sysAddr, u);
|
WorldPackets::SendCharacterList(sysAddr, u);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,12 +274,8 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
|||||||
|
|
||||||
//Now that the name is ok, we can get an objectID from Master:
|
//Now that the name is ok, we can get an objectID from Master:
|
||||||
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t objectID) {
|
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t objectID) {
|
||||||
sql::PreparedStatement* overlapStmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE id = ?");
|
auto character = Database::Connection->GetCharacterInfoByID(objectID);
|
||||||
overlapStmt->setUInt(1, objectID);
|
if (character.AccountID != 0) {
|
||||||
|
|
||||||
auto* overlapResult = overlapStmt->executeQuery();
|
|
||||||
|
|
||||||
if (overlapResult->next()) {
|
|
||||||
Game::logger->Log("UserManager", "Character object id unavailable, check objectidtracker!");
|
Game::logger->Log("UserManager", "Character object id unavailable, check objectidtracker!");
|
||||||
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE);
|
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE);
|
||||||
return;
|
return;
|
||||||
@ -333,45 +313,32 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
|||||||
|
|
||||||
xml3 << "</in></items></inv><lvl l=\"1\" cv=\"1\" sb=\"500\"/><flag></flag></obj>";
|
xml3 << "</in></items></inv><lvl l=\"1\" cv=\"1\" sb=\"500\"/><flag></flag></obj>";
|
||||||
|
|
||||||
//Check to see if our name was pre-approved:
|
// Check to see if our name was pre-approved
|
||||||
bool nameOk = IsNamePreapproved(name);
|
bool nameOk = IsNamePreapproved(name);
|
||||||
if (!nameOk && u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true;
|
if (u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true;
|
||||||
|
|
||||||
if (name != "") {
|
if (name != "") {
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`) VALUES (?,?,?,?,?,?)");
|
Database::Connection->CreateCharacter(
|
||||||
stmt->setUInt(1, objectID);
|
objectID,
|
||||||
stmt->setUInt(2, u->GetAccountID());
|
u->GetAccountID(),
|
||||||
stmt->setString(3, predefinedName.c_str());
|
nameOk ? name : predefinedName,
|
||||||
stmt->setString(4, name.c_str());
|
nameOk ? "" : name,
|
||||||
stmt->setBoolean(5, false);
|
false,
|
||||||
stmt->setUInt64(6, time(NULL));
|
time(NULL)
|
||||||
|
);
|
||||||
if (nameOk) {
|
|
||||||
stmt->setString(3, name.c_str());
|
|
||||||
stmt->setString(4, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
} else {
|
} else {
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`) VALUES (?,?,?,?,?,?)");
|
Database::Connection->CreateCharacter(
|
||||||
stmt->setUInt(1, objectID);
|
objectID,
|
||||||
stmt->setUInt(2, u->GetAccountID());
|
u->GetAccountID(),
|
||||||
stmt->setString(3, predefinedName.c_str());
|
predefinedName,
|
||||||
stmt->setString(4, "");
|
"",
|
||||||
stmt->setBoolean(5, false);
|
false,
|
||||||
stmt->setUInt64(6, time(NULL));
|
time(NULL)
|
||||||
|
);
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Now finally insert our character xml:
|
// Now finally insert our character xml
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charxml`(`id`, `xml_data`) VALUES (?,?)");
|
Database::Connection->CreateCharacterXML(objectID, xml3.str());
|
||||||
stmt->setUInt(1, objectID);
|
|
||||||
stmt->setString(2, xml3.str().c_str());
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
|
|
||||||
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS);
|
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS);
|
||||||
UserManager::RequestCharacterList(sysAddr);
|
UserManager::RequestCharacterList(sysAddr);
|
||||||
@ -403,73 +370,13 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
|
|||||||
WorldPackets::SendCharacterDeleteResponse(sysAddr, false);
|
WorldPackets::SendCharacterDeleteResponse(sysAddr, false);
|
||||||
} else {
|
} else {
|
||||||
Game::logger->Log("UserManager", "Deleting character %i", charID);
|
Game::logger->Log("UserManager", "Deleting character %i", charID);
|
||||||
{
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM charxml WHERE id=? LIMIT 1;");
|
Database::Connection->DeleteCharacter(charID);
|
||||||
stmt->setUInt64(1, charID);
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM command_log WHERE character_id=?;");
|
|
||||||
stmt->setUInt64(1, charID);
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM friends WHERE player_id=? OR friend_id=?;");
|
|
||||||
stmt->setUInt(1, charID);
|
|
||||||
stmt->setUInt(2, charID);
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION);
|
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION);
|
||||||
bitStream.Write(objectID);
|
bitStream.Write(objectID);
|
||||||
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
||||||
}
|
|
||||||
{
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM leaderboard WHERE character_id=?;");
|
|
||||||
stmt->setUInt64(1, charID);
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt(
|
|
||||||
"DELETE FROM properties_contents WHERE property_id IN (SELECT id FROM properties WHERE owner_id=?);"
|
|
||||||
);
|
|
||||||
stmt->setUInt64(1, charID);
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM properties WHERE owner_id=?;");
|
|
||||||
stmt->setUInt64(1, charID);
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM ugc WHERE character_id=?;");
|
|
||||||
stmt->setUInt64(1, charID);
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM activity_log WHERE character_id=?;");
|
|
||||||
stmt->setUInt64(1, charID);
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM mail WHERE receiver_id=?;");
|
|
||||||
stmt->setUInt64(1, charID);
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM charinfo WHERE id=? LIMIT 1;");
|
|
||||||
stmt->setUInt64(1, charID);
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
WorldPackets::SendCharacterDeleteResponse(sysAddr, true);
|
WorldPackets::SendCharacterDeleteResponse(sysAddr, true);
|
||||||
}
|
}
|
||||||
|
@ -1085,42 +1085,21 @@ PetComponent::~PetComponent() {
|
|||||||
void PetComponent::SetPetNameForModeration(const std::string& petName) {
|
void PetComponent::SetPetNameForModeration(const std::string& petName) {
|
||||||
int approved = 1; // default, in mod
|
int approved = 1; // default, in mod
|
||||||
|
|
||||||
//Make sure that the name isn't already auto-approved:
|
// Make sure that the name isn't already auto-approved
|
||||||
if (Game::chatFilter->IsSentenceOkay(petName, eGameMasterLevel::CIVILIAN).empty()) {
|
if (Game::chatFilter->IsSentenceOkay(petName, eGameMasterLevel::CIVILIAN).empty()) {
|
||||||
approved = 2; //approved
|
approved = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto deleteStmt = Database::CreatePreppedStmt("DELETE FROM pet_names WHERE id = ? LIMIT 1;");
|
Database::Connection->DeletePetName(m_DatabaseId);
|
||||||
deleteStmt->setUInt64(1, m_DatabaseId);
|
|
||||||
|
|
||||||
deleteStmt->execute();
|
// Save to db
|
||||||
|
Database::Connection->CreatePetName(m_DatabaseId, petName, approved);
|
||||||
delete deleteStmt;
|
|
||||||
|
|
||||||
//Save to db:
|
|
||||||
auto stmt = Database::CreatePreppedStmt("INSERT INTO `pet_names` (`id`, `pet_name`, `approved`) VALUES (?, ?, ?);");
|
|
||||||
stmt->setUInt64(1, m_DatabaseId);
|
|
||||||
stmt->setString(2, petName);
|
|
||||||
stmt->setInt(3, approved);
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PetComponent::LoadPetNameFromModeration() {
|
void PetComponent::LoadPetNameFromModeration() {
|
||||||
auto stmt = Database::CreatePreppedStmt("SELECT pet_name, approved FROM pet_names WHERE id = ? LIMIT 1;");
|
auto petNameInfo = Database::Connection->GetPetName(m_DatabaseId);
|
||||||
stmt->setUInt64(1, m_DatabaseId);
|
m_ModerationStatus = petNameInfo.Approved;
|
||||||
|
m_Name = petNameInfo.Name;
|
||||||
auto res = stmt->executeQuery();
|
|
||||||
while (res->next()) {
|
|
||||||
m_ModerationStatus = res->getInt(2);
|
|
||||||
|
|
||||||
if (m_ModerationStatus == 2) {
|
|
||||||
m_Name = res->getString(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete res;
|
|
||||||
delete stmt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PetComponent::SetPreconditions(std::string& preconditions) {
|
void PetComponent::SetPreconditions(std::string& preconditions) {
|
||||||
|
@ -360,11 +360,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log command to database
|
// Log command to database
|
||||||
auto stmt = Database::CreatePreppedStmt("INSERT INTO command_log (character_id, command) VALUES (?, ?);");
|
Database::Connection->InsertIntoCommandLog(entity->GetCharacter()->GetID(), GeneralUtils::UTF16ToWTF8(command));
|
||||||
stmt->setInt(1, entity->GetCharacter()->GetID());
|
|
||||||
stmt->setString(2, GeneralUtils::UTF16ToWTF8(command).c_str());
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
|
|
||||||
if (chatCommand == "setminifig" && args.size() == 2 && entity->GetGMLevel() >= eGameMasterLevel::FORUM_MODERATOR) { // could break characters so only allow if GM > 0
|
if (chatCommand == "setminifig" && args.size() == 2 && entity->GetGMLevel() >= eGameMasterLevel::FORUM_MODERATOR) { // could break characters so only allow if GM > 0
|
||||||
int32_t minifigItemId;
|
int32_t minifigItemId;
|
||||||
@ -817,17 +813,8 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
|||||||
if (chatCommand == "mailitem" && entity->GetGMLevel() >= eGameMasterLevel::MODERATOR && args.size() >= 2) {
|
if (chatCommand == "mailitem" && entity->GetGMLevel() >= eGameMasterLevel::MODERATOR && args.size() >= 2) {
|
||||||
const auto& playerName = args[0];
|
const auto& playerName = args[0];
|
||||||
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id from charinfo WHERE name=? LIMIT 1;");
|
auto character = Database::Connection->GetCharacterByName(playerName);
|
||||||
stmt->setString(1, playerName);
|
uint32_t receiverID = character.AccountID;
|
||||||
sql::ResultSet* res = stmt->executeQuery();
|
|
||||||
uint32_t receiverID = 0;
|
|
||||||
|
|
||||||
if (res->rowsCount() > 0) {
|
|
||||||
while (res->next()) receiverID = res->getUInt(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete stmt;
|
|
||||||
delete res;
|
|
||||||
|
|
||||||
if (receiverID == 0) {
|
if (receiverID == 0) {
|
||||||
ChatPackets::SendSystemMessage(sysAddr, u"Failed to find that player");
|
ChatPackets::SendSystemMessage(sysAddr, u"Failed to find that player");
|
||||||
@ -1016,26 +1003,15 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
|||||||
LWOOBJID characterId = 0;
|
LWOOBJID characterId = 0;
|
||||||
|
|
||||||
if (player == nullptr) {
|
if (player == nullptr) {
|
||||||
auto* accountQuery = Database::CreatePreppedStmt("SELECT account_id, id FROM charinfo WHERE name=? LIMIT 1;");
|
auto character = Database::Connection->GetCharacterByName(args[0]);
|
||||||
|
|
||||||
accountQuery->setString(1, args[0]);
|
if (accountId != 0) {
|
||||||
|
accountId = character.AccountID;
|
||||||
auto result = accountQuery->executeQuery();
|
characterId = character.ID;
|
||||||
|
|
||||||
if (result->rowsCount() > 0) {
|
|
||||||
while (result->next()) {
|
|
||||||
accountId = result->getUInt(1);
|
|
||||||
characterId = result->getUInt64(2);
|
|
||||||
|
|
||||||
GeneralUtils::SetBit(characterId, eObjectBits::CHARACTER);
|
GeneralUtils::SetBit(characterId, eObjectBits::CHARACTER);
|
||||||
GeneralUtils::SetBit(characterId, eObjectBits::PERSISTENT);
|
GeneralUtils::SetBit(characterId, eObjectBits::PERSISTENT);
|
||||||
}
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
delete accountQuery;
|
|
||||||
delete result;
|
|
||||||
|
|
||||||
if (accountId == 0) {
|
|
||||||
ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + GeneralUtils::UTF8ToUTF16(args[0]));
|
ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + GeneralUtils::UTF8ToUTF16(args[0]));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -1128,20 +1104,10 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
|||||||
uint32_t accountId = 0;
|
uint32_t accountId = 0;
|
||||||
|
|
||||||
if (player == nullptr) {
|
if (player == nullptr) {
|
||||||
auto* accountQuery = Database::CreatePreppedStmt("SELECT account_id FROM charinfo WHERE name=? LIMIT 1;");
|
auto character = Database::Connection->GetCharacterInfoByName(args[0]);
|
||||||
|
accountId = character.AccountID;
|
||||||
|
|
||||||
accountQuery->setString(1, args[0]);
|
if (character.AccountID == 0) {
|
||||||
|
|
||||||
auto result = accountQuery->executeQuery();
|
|
||||||
|
|
||||||
if (result->rowsCount() > 0) {
|
|
||||||
while (result->next()) accountId = result->getUInt(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete accountQuery;
|
|
||||||
delete result;
|
|
||||||
|
|
||||||
if (accountId == 0) {
|
|
||||||
ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + GeneralUtils::UTF8ToUTF16(args[0]));
|
ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + GeneralUtils::UTF8ToUTF16(args[0]));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -129,19 +129,7 @@ int main(int argc, char** argv) {
|
|||||||
Game::logger->Log("MasterServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
|
Game::logger->Log("MasterServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
|
||||||
Game::logger->Log("MasterServer", "Compiled on: %s", __TIMESTAMP__);
|
Game::logger->Log("MasterServer", "Compiled on: %s", __TIMESTAMP__);
|
||||||
|
|
||||||
//Connect to the MySQL Database
|
Database::Connect(Game::config);
|
||||||
std::string mysql_host = Game::config->GetValue("mysql_host");
|
|
||||||
std::string mysql_database = Game::config->GetValue("mysql_database");
|
|
||||||
std::string mysql_username = Game::config->GetValue("mysql_username");
|
|
||||||
std::string mysql_password = Game::config->GetValue("mysql_password");
|
|
||||||
|
|
||||||
try {
|
|
||||||
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
|
||||||
} catch (sql::SQLException& ex) {
|
|
||||||
Game::logger->Log("MasterServer", "Got an error while connecting to the database: %s", ex.what());
|
|
||||||
Game::logger->Log("MigrationRunner", "Migrations not run");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::string clientPathStr = Game::config->GetValue("client_location");
|
std::string clientPathStr = Game::config->GetValue("client_location");
|
||||||
@ -313,8 +301,7 @@ int main(int argc, char** argv) {
|
|||||||
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::shouldShutdown);
|
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::shouldShutdown);
|
||||||
|
|
||||||
//Query for the database for a server labeled "master"
|
//Query for the database for a server labeled "master"
|
||||||
auto* masterLookupStatement = Database::CreatePreppedStmt("SELECT id FROM `servers` WHERE `name` = 'master'");
|
auto masterServerSock = Database::Connection->GetMasterServerIP();
|
||||||
auto* result = masterLookupStatement->executeQuery();
|
|
||||||
|
|
||||||
auto master_server_ip = Game::config->GetValue("master_ip");
|
auto master_server_ip = Game::config->GetValue("master_ip");
|
||||||
|
|
||||||
@ -323,20 +310,11 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//If we found a server, update it's IP and port to the current one.
|
//If we found a server, update it's IP and port to the current one.
|
||||||
if (result->next()) {
|
if (masterServerSock.port != 0) {
|
||||||
auto* updateStatement = Database::CreatePreppedStmt("UPDATE `servers` SET `ip` = ?, `port` = ? WHERE `id` = ?");
|
Database::Connection->SetServerIpAndPortByName("master", master_server_ip, Game::server->GetPort());
|
||||||
updateStatement->setString(1, master_server_ip.c_str());
|
|
||||||
updateStatement->setInt(2, Game::server->GetPort());
|
|
||||||
updateStatement->setInt(3, result->getInt("id"));
|
|
||||||
updateStatement->execute();
|
|
||||||
delete updateStatement;
|
|
||||||
} else {
|
} else {
|
||||||
// If we didn't find a server, create one.
|
// If we didn't find a server, create one.
|
||||||
auto* insertStatement = Database::CreatePreppedStmt("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171023)");
|
Database::Connection->CreateServer("master", master_server_ip, Game::server->GetPort(), 0, 171023);
|
||||||
insertStatement->setString(1, master_server_ip.c_str());
|
|
||||||
insertStatement->setInt(2, Game::server->GetPort());
|
|
||||||
insertStatement->execute();
|
|
||||||
delete insertStatement;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Create additional objects here:
|
//Create additional objects here:
|
||||||
@ -382,18 +360,7 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
//Every 10 min we ping our sql server to keep it alive hopefully:
|
//Every 10 min we ping our sql server to keep it alive hopefully:
|
||||||
if (framesSinceLastSQLPing >= sqlPingTime) {
|
if (framesSinceLastSQLPing >= sqlPingTime) {
|
||||||
//Find out the master's IP for absolutely no reason:
|
Database::Connection->GetMasterServerIP();
|
||||||
std::string masterIP;
|
|
||||||
uint32_t masterPort;
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
|
||||||
auto res = stmt->executeQuery();
|
|
||||||
while (res->next()) {
|
|
||||||
masterIP = res->getString(1).c_str();
|
|
||||||
masterPort = res->getInt(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete res;
|
|
||||||
delete stmt;
|
|
||||||
|
|
||||||
framesSinceLastSQLPing = 0;
|
framesSinceLastSQLPing = 0;
|
||||||
} else
|
} else
|
||||||
@ -973,8 +940,8 @@ void ShutdownSequence(int32_t signal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int32_t FinalizeShutdown(int32_t signal) {
|
int32_t FinalizeShutdown(int32_t signal) {
|
||||||
//Delete our objects here:
|
// Delete our objects here
|
||||||
Database::Destroy("MasterServer");
|
Database::Destroy();
|
||||||
if (Game::config) delete Game::config;
|
if (Game::config) delete Game::config;
|
||||||
if (Game::im) delete Game::im;
|
if (Game::im) delete Game::im;
|
||||||
if (Game::server) delete Game::server;
|
if (Game::server) delete Game::server;
|
||||||
|
@ -10,62 +10,18 @@ ObjectIDManager* ObjectIDManager::m_Address = nullptr;
|
|||||||
//! Initializes the manager
|
//! Initializes the manager
|
||||||
void ObjectIDManager::Initialize(dLogger* logger) {
|
void ObjectIDManager::Initialize(dLogger* logger) {
|
||||||
this->mLogger = logger;
|
this->mLogger = logger;
|
||||||
this->currentPersistentID = 1;
|
this->currentPersistentID = Database::Connection->GetObjectIDTracker();
|
||||||
|
|
||||||
try {
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt(
|
|
||||||
"SELECT last_object_id FROM object_id_tracker");
|
|
||||||
|
|
||||||
sql::ResultSet* result = stmt->executeQuery();
|
|
||||||
auto next = result->next();
|
|
||||||
|
|
||||||
if (!next) {
|
|
||||||
sql::PreparedStatement* insertStmt = Database::CreatePreppedStmt(
|
|
||||||
"INSERT INTO object_id_tracker VALUES (1)");
|
|
||||||
|
|
||||||
insertStmt->execute();
|
|
||||||
|
|
||||||
delete insertStmt;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (next) {
|
|
||||||
this->currentPersistentID =
|
|
||||||
result->getInt(1) > 0 ? result->getInt(1) : 1;
|
|
||||||
next = result->next();
|
|
||||||
}
|
|
||||||
|
|
||||||
delete result;
|
|
||||||
delete stmt;
|
|
||||||
} catch (sql::SQLException& e) {
|
|
||||||
mLogger->Log("ObjectIDManager", "Unable to fetch max persistent object "
|
|
||||||
"ID in use. Defaulting to 1.");
|
|
||||||
mLogger->Log("ObjectIDManager", "SQL error: %s", e.what());
|
|
||||||
this->currentPersistentID = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Generates a new persistent ID
|
//! Generates a new persistent ID
|
||||||
uint32_t ObjectIDManager::GeneratePersistentID(void) {
|
uint32_t ObjectIDManager::GeneratePersistentID(void) {
|
||||||
uint32_t toReturn = ++this->currentPersistentID;
|
uint32_t toReturn = ++this->currentPersistentID;
|
||||||
|
|
||||||
// So we peroidically save our ObjID to the database:
|
Database::Connection->SetObjectIDTracker(toReturn);
|
||||||
// if (toReturn % 25 == 0) { // TEMP: DISABLED FOR DEBUG / DEVELOPMENT!
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt(
|
|
||||||
"UPDATE object_id_tracker SET last_object_id=?");
|
|
||||||
stmt->setUInt(1, toReturn);
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
// }
|
|
||||||
|
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectIDManager::SaveToDatabase() {
|
void ObjectIDManager::SaveToDatabase() {
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt(
|
Database::Connection->SetObjectIDTracker(this->currentPersistentID);
|
||||||
"UPDATE object_id_tracker SET last_object_id=?");
|
|
||||||
stmt->setUInt(1, currentPersistentID);
|
|
||||||
stmt->execute();
|
|
||||||
delete stmt;
|
|
||||||
}
|
}
|
||||||
|
@ -64,122 +64,57 @@ void AuthPackets::HandleLoginRequest(dServer* server, Packet* packet) {
|
|||||||
const char* szUsername = username.c_str();
|
const char* szUsername = username.c_str();
|
||||||
|
|
||||||
// Fetch account details
|
// Fetch account details
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT password, banned, locked, play_key_id, gm_level FROM accounts WHERE name=? LIMIT 1;");
|
auto account = Database::Connection->GetAccountByName(username);
|
||||||
stmt->setString(1, szUsername);
|
|
||||||
|
|
||||||
sql::ResultSet* res = stmt->executeQuery();
|
if (account.ID == 0) {
|
||||||
|
|
||||||
if (res->rowsCount() == 0) {
|
|
||||||
server->GetLogger()->Log("AuthPackets", "No user found!");
|
server->GetLogger()->Log("AuthPackets", "No user found!");
|
||||||
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::INVALID_USER, "", "", 2001, username);
|
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::INVALID_USER, "", "", 2001, username);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string sqlPass = "";
|
|
||||||
bool sqlBanned = false;
|
|
||||||
bool sqlLocked = false;
|
|
||||||
uint32_t sqlPlayKey = 0;
|
|
||||||
uint32_t sqlGmLevel = 0;
|
|
||||||
|
|
||||||
while (res->next()) {
|
|
||||||
sqlPass = res->getString(1).c_str();
|
|
||||||
sqlBanned = res->getBoolean(2);
|
|
||||||
sqlLocked = res->getBoolean(3);
|
|
||||||
sqlPlayKey = res->getInt(4);
|
|
||||||
sqlGmLevel = res->getInt(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete stmt;
|
|
||||||
delete res;
|
|
||||||
|
|
||||||
//If we aren't running in live mode, then only GMs are allowed to enter:
|
//If we aren't running in live mode, then only GMs are allowed to enter:
|
||||||
const auto& closedToNonDevs = Game::config->GetValue("closed_to_non_devs");
|
const auto& closedToNonDevs = Game::config->GetValue("closed_to_non_devs");
|
||||||
if (closedToNonDevs.size() > 0 && bool(std::stoi(closedToNonDevs)) && sqlGmLevel == 0) {
|
if (closedToNonDevs.size() > 0 && bool(std::stoi(closedToNonDevs)) && account.MaxGMLevel == 0) {
|
||||||
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "The server is currently only open to developers.", "", 2001, username);
|
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "The server is currently only open to developers.", "", 2001, username);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Game::config->GetValue("dont_use_keys") != "1") {
|
if (Game::config->GetValue("dont_use_keys") != "1") {
|
||||||
//Check to see if we have a play key:
|
// Check to see if we have a play key
|
||||||
if (sqlPlayKey == 0 && sqlGmLevel == 0) {
|
if (account.PlayKeyID == 0 && account.MaxGMLevel == 0) {
|
||||||
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a play key associated with it!", "", 2001, username);
|
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a play key associated with it!", "", 2001, username);
|
||||||
server->GetLogger()->Log("AuthPackets", "User %s tried to log in, but they don't have a play key.", username.c_str());
|
server->GetLogger()->Log("AuthPackets", "User %s tried to log in, but they don't have a play key.", username.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check if the play key is _valid_:
|
// Check if the play key is _valid_
|
||||||
auto keyCheckStmt = Database::CreatePreppedStmt("SELECT active FROM `play_keys` WHERE id=?");
|
bool isKeyActive = Database::Connection->IsKeyActive(account.PlayKeyID);
|
||||||
keyCheckStmt->setInt(1, sqlPlayKey);
|
|
||||||
auto keyRes = keyCheckStmt->executeQuery();
|
|
||||||
bool isKeyActive = false;
|
|
||||||
|
|
||||||
if (keyRes->rowsCount() == 0 && sqlGmLevel == 0) {
|
if (!isKeyActive && account.MaxGMLevel == 0) {
|
||||||
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a play key associated with it!", "", 2001, username);
|
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a play key associated with it!", "", 2001, username);
|
||||||
return;
|
server->GetLogger()->Log("AuthPackets", "User %s tried to log in, but they lacked a play key", username.c_str());
|
||||||
}
|
|
||||||
|
|
||||||
while (keyRes->next()) {
|
|
||||||
isKeyActive = (bool)keyRes->getInt(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isKeyActive && sqlGmLevel == 0) {
|
|
||||||
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your play key has been disabled.", "", 2001, username);
|
|
||||||
server->GetLogger()->Log("AuthPackets", "User %s tried to log in, but their play key was disabled", username.c_str());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sqlBanned) {
|
if (account.Banned) {
|
||||||
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::BANNED, "", "", 2001, username); return;
|
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::BANNED, "", "", 2001, username); return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sqlLocked) {
|
if (account.Locked) {
|
||||||
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::ACCOUNT_LOCKED, "", "", 2001, username); return;
|
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::ACCOUNT_LOCKED, "", "", 2001, username); return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (!account.Password.empty()) {
|
||||||
* Updated hashing method:
|
if (account.Password[0] != '$') {
|
||||||
* First attempt bcrypt.
|
Game::logger->Log("AuthPackets", "Invalid password being parsed for user %s", username.c_str());
|
||||||
* If that fails, fallback to old method and setup bcrypt for new login.
|
}
|
||||||
*/
|
}
|
||||||
|
|
||||||
bool loginSuccess = true;
|
int32_t bcryptState = ::bcrypt_checkpw(password.c_str(), account.Password.c_str());
|
||||||
|
|
||||||
int32_t bcryptState = ::bcrypt_checkpw(password.c_str(), sqlPass.c_str());
|
|
||||||
|
|
||||||
if (bcryptState != 0) {
|
if (bcryptState != 0) {
|
||||||
// Fallback on old method
|
|
||||||
|
|
||||||
std::string oldPassword = sha512(password + username);
|
|
||||||
|
|
||||||
if (sqlPass != oldPassword) {
|
|
||||||
loginSuccess = false;
|
|
||||||
} else {
|
|
||||||
// Generate new hash for bcrypt
|
|
||||||
|
|
||||||
char salt[BCRYPT_HASHSIZE];
|
|
||||||
char hash[BCRYPT_HASHSIZE];
|
|
||||||
|
|
||||||
bcryptState = ::bcrypt_gensalt(12, salt);
|
|
||||||
|
|
||||||
assert(bcryptState == 0);
|
|
||||||
|
|
||||||
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
|
|
||||||
|
|
||||||
assert(bcryptState == 0);
|
|
||||||
|
|
||||||
sql::PreparedStatement* accountUpdate = Database::CreatePreppedStmt("UPDATE accounts SET password = ? WHERE name = ? LIMIT 1;");
|
|
||||||
|
|
||||||
accountUpdate->setString(1, std::string(hash, BCRYPT_HASHSIZE).c_str());
|
|
||||||
accountUpdate->setString(2, szUsername);
|
|
||||||
|
|
||||||
accountUpdate->executeUpdate();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Login success with bcrypt
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!loginSuccess) {
|
|
||||||
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::WRONG_PASS, "", "", 2001, username);
|
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::WRONG_PASS, "", "", 2001, username);
|
||||||
server->GetLogger()->Log("AuthPackets", "Wrong password used");
|
server->GetLogger()->Log("AuthPackets", "Wrong password used");
|
||||||
} else {
|
} else {
|
||||||
|
@ -351,41 +351,17 @@ void ClientPackets::HandleChatModerationRequest(const SystemAddress& sysAddr, Pa
|
|||||||
// Private chat
|
// Private chat
|
||||||
LWOOBJID idOfReceiver = LWOOBJID_EMPTY;
|
LWOOBJID idOfReceiver = LWOOBJID_EMPTY;
|
||||||
|
|
||||||
{
|
auto receiverInfo = Database::Connection->GetCharacterInfoByName(receiver);
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE name = ?");
|
idOfReceiver = receiverInfo.ID;
|
||||||
stmt->setString(1, receiver);
|
|
||||||
|
|
||||||
sql::ResultSet* res = stmt->executeQuery();
|
|
||||||
|
|
||||||
if (res->next()) {
|
|
||||||
idOfReceiver = res->getInt("id");
|
|
||||||
}
|
|
||||||
|
|
||||||
delete stmt;
|
|
||||||
delete res;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user->GetIsBestFriendMap().find(receiver) == user->GetIsBestFriendMap().end() && idOfReceiver != LWOOBJID_EMPTY) {
|
if (user->GetIsBestFriendMap().find(receiver) == user->GetIsBestFriendMap().end() && idOfReceiver != LWOOBJID_EMPTY) {
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;");
|
isBestFriend = Database::Connection->AreBestFriends(entity->GetObjectID(), idOfReceiver);
|
||||||
stmt->setInt(1, entity->GetObjectID());
|
|
||||||
stmt->setInt(2, idOfReceiver);
|
|
||||||
stmt->setInt(3, idOfReceiver);
|
|
||||||
stmt->setInt(4, entity->GetObjectID());
|
|
||||||
|
|
||||||
sql::ResultSet* res = stmt->executeQuery();
|
|
||||||
|
|
||||||
if (res->next()) {
|
|
||||||
isBestFriend = res->getInt("best_friend") == 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isBestFriend) {
|
if (isBestFriend) {
|
||||||
auto tmpBestFriendMap = user->GetIsBestFriendMap();
|
auto tmpBestFriendMap = user->GetIsBestFriendMap();
|
||||||
tmpBestFriendMap[receiver] = true;
|
tmpBestFriendMap[receiver] = true;
|
||||||
user->SetIsBestFriendMap(tmpBestFriendMap);
|
user->SetIsBestFriendMap(tmpBestFriendMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete res;
|
|
||||||
delete stmt;
|
|
||||||
} else if (user->GetIsBestFriendMap().find(receiver) != user->GetIsBestFriendMap().end()) {
|
} else if (user->GetIsBestFriendMap().find(receiver) != user->GetIsBestFriendMap().end()) {
|
||||||
isBestFriend = true;
|
isBestFriend = true;
|
||||||
}
|
}
|
||||||
|
@ -180,43 +180,21 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
CDClientManager::Instance();
|
CDClientManager::Instance();
|
||||||
|
|
||||||
//Connect to the MySQL Database
|
|
||||||
std::string mysql_host = Game::config->GetValue("mysql_host");
|
|
||||||
std::string mysql_database = Game::config->GetValue("mysql_database");
|
|
||||||
std::string mysql_username = Game::config->GetValue("mysql_username");
|
|
||||||
std::string mysql_password = Game::config->GetValue("mysql_password");
|
|
||||||
|
|
||||||
Diagnostics::SetProduceMemoryDump(Game::config->GetValue("generate_dump") == "1");
|
Diagnostics::SetProduceMemoryDump(Game::config->GetValue("generate_dump") == "1");
|
||||||
|
|
||||||
if (!Game::config->GetValue("dump_folder").empty()) {
|
if (!Game::config->GetValue("dump_folder").empty()) {
|
||||||
Diagnostics::SetOutDirectory(Game::config->GetValue("dump_folder"));
|
Diagnostics::SetOutDirectory(Game::config->GetValue("dump_folder"));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
Database::Connect(Game::config);
|
||||||
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
|
||||||
} catch (sql::SQLException& ex) {
|
|
||||||
Game::logger->Log("WorldServer", "Got an error while connecting to the database: %s", ex.what());
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Find out the master's IP:
|
auto masterSock = Database::Connection->GetMasterServerIP();
|
||||||
std::string masterIP = "localhost";
|
|
||||||
uint32_t masterPort = 1000;
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
|
||||||
auto res = stmt->executeQuery();
|
|
||||||
while (res->next()) {
|
|
||||||
masterIP = res->getString(1).c_str();
|
|
||||||
masterPort = res->getInt(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete res;
|
|
||||||
delete stmt;
|
|
||||||
|
|
||||||
ObjectIDManager::Instance()->Initialize();
|
ObjectIDManager::Instance()->Initialize();
|
||||||
UserManager::Instance()->Initialize();
|
UserManager::Instance()->Initialize();
|
||||||
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
|
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
|
||||||
|
|
||||||
Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, &Game::shouldShutdown, zoneID);
|
Game::server = new dServer(masterSock.hostAddress, ourPort, instanceID, maxClients, false, true, Game::logger, masterSock.hostAddress, masterSock.port, ServerType::World, Game::config, &Game::shouldShutdown, zoneID);
|
||||||
|
|
||||||
//Connect to the chat server:
|
//Connect to the chat server:
|
||||||
uint32_t chatPort = 1501;
|
uint32_t chatPort = 1501;
|
||||||
@ -225,7 +203,7 @@ int main(int argc, char** argv) {
|
|||||||
auto chatSock = SocketDescriptor(uint16_t(ourPort + 2), 0);
|
auto chatSock = SocketDescriptor(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(masterSock.hostAddress, chatPort, "3.25 ND1", 8);
|
||||||
|
|
||||||
//Set up other things:
|
//Set up other things:
|
||||||
Game::randomEngine = std::mt19937(time(0));
|
Game::randomEngine = std::mt19937(time(0));
|
||||||
@ -380,7 +358,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(masterSock.hostAddress, chatPort, "3.25 ND1", 8);
|
||||||
}
|
}
|
||||||
} else framesSinceChatDisconnect = 0;
|
} else framesSinceChatDisconnect = 0;
|
||||||
|
|
||||||
@ -482,18 +460,7 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
//Every 10 min we ping our sql server to keep it alive hopefully:
|
//Every 10 min we ping our sql server to keep it alive hopefully:
|
||||||
if (framesSinceLastSQLPing >= sqlPingTime) {
|
if (framesSinceLastSQLPing >= sqlPingTime) {
|
||||||
//Find out the master's IP for absolutely no reason:
|
Database::Connection->GetMasterServerIP();
|
||||||
std::string masterIP;
|
|
||||||
uint32_t masterPort;
|
|
||||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
|
||||||
auto res = stmt->executeQuery();
|
|
||||||
while (res->next()) {
|
|
||||||
masterIP = res->getString(1).c_str();
|
|
||||||
masterPort = res->getInt(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete res;
|
|
||||||
delete stmt;
|
|
||||||
|
|
||||||
framesSinceLastSQLPing = 0;
|
framesSinceLastSQLPing = 0;
|
||||||
} else framesSinceLastSQLPing++;
|
} else framesSinceLastSQLPing++;
|
||||||
@ -886,20 +853,10 @@ void HandlePacket(Packet* packet) {
|
|||||||
|
|
||||||
// If the check is turned on, validate the client's database checksum.
|
// If the check is turned on, validate the client's database checksum.
|
||||||
if (Game::config->GetValue("check_fdb") == "1" && !databaseChecksum.empty()) {
|
if (Game::config->GetValue("check_fdb") == "1" && !databaseChecksum.empty()) {
|
||||||
uint32_t gmLevel = 0;
|
auto account = Database::Connection->GetAccountByName(username);
|
||||||
auto* stmt = Database::CreatePreppedStmt("SELECT gm_level FROM accounts WHERE name=? LIMIT 1;");
|
|
||||||
stmt->setString(1, username.c_str());
|
|
||||||
|
|
||||||
auto* res = stmt->executeQuery();
|
// Operators may skip this check
|
||||||
while (res->next()) {
|
if (account.MaxGMLevel < 8 && clientDatabaseChecksum != databaseChecksum) {
|
||||||
gmLevel = res->getInt(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete stmt;
|
|
||||||
delete res;
|
|
||||||
|
|
||||||
// Developers may skip this check
|
|
||||||
if (gmLevel < 8 && clientDatabaseChecksum != databaseChecksum) {
|
|
||||||
Game::logger->Log("WorldServer", "Client's database checksum does not match the server's, aborting connection.");
|
Game::logger->Log("WorldServer", "Client's database checksum does not match the server's, aborting connection.");
|
||||||
Game::server->Disconnect(packet->systemAddress, eServerDisconnectIdentifiers::WRONG_GAME_VERSION);
|
Game::server->Disconnect(packet->systemAddress, eServerDisconnectIdentifiers::WRONG_GAME_VERSION);
|
||||||
return;
|
return;
|
||||||
@ -1354,7 +1311,8 @@ void FinalizeShutdown() {
|
|||||||
|
|
||||||
//Delete our objects here:
|
//Delete our objects here:
|
||||||
Metrics::Clear();
|
Metrics::Clear();
|
||||||
Database::Destroy("WorldServer");
|
Database::Destroy();
|
||||||
|
|
||||||
if (Game::chatFilter) delete Game::chatFilter;
|
if (Game::chatFilter) delete Game::chatFilter;
|
||||||
if (Game::zoneManager) delete Game::zoneManager;
|
if (Game::zoneManager) delete Game::zoneManager;
|
||||||
if (Game::server) delete Game::server;
|
if (Game::server) delete Game::server;
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
# MySQL connection info:
|
database_file=darkflame.sqlite
|
||||||
mysql_host=
|
|
||||||
mysql_database=
|
|
||||||
mysql_username=
|
|
||||||
mysql_password=
|
|
||||||
|
|
||||||
# 0 or 1, should log to console
|
# 0 or 1, should log to console
|
||||||
log_to_console=1
|
log_to_console=1
|
||||||
|
Loading…
Reference in New Issue
Block a user