mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 21:01:29 +00:00
* initial work, need to clean up gm commands still * cleaned up command, works without kicking char select now * remove thj-specific methods * add script hooks * actually clear flag * rework questmgr::rename * remove unnecessary logging * revert * added missing binding to perl api and updated some text * don't return a value * Fix some bad argument types. * adjust case * alpha order * refactor some old string stuff * don't quote integers, bob --------- Co-authored-by: Zimp <zimp@zenryo.xyz> Co-authored-by: Chris Miles <akkadius1@gmail.com>
2259 lines
48 KiB
C++
2259 lines
48 KiB
C++
/* EQEMu: Everquest Server Emulator
|
|
Copyright (C) 2001-2015 EQEMu Development Team (http://eqemulator.net)
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
|
are required to give you total support for your newly bought product;
|
|
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "../common/global_define.h"
|
|
#include "../common/rulesys.h"
|
|
|
|
#include <ctype.h>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <algorithm>
|
|
#include <mysqld_error.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "../common/repositories/account_repository.h"
|
|
#include "../common/repositories/adventure_stats_repository.h"
|
|
#include "../common/repositories/character_bind_repository.h"
|
|
#include "../common/repositories/character_data_repository.h"
|
|
#include "../common/repositories/character_languages_repository.h"
|
|
#include "../common/repositories/character_leadership_abilities_repository.h"
|
|
#include "../common/repositories/character_parcels_repository.h"
|
|
#include "../common/repositories/character_skills_repository.h"
|
|
#include "../common/repositories/data_buckets_repository.h"
|
|
#include "../common/repositories/group_id_repository.h"
|
|
#include "../common/repositories/group_leaders_repository.h"
|
|
#include "../common/repositories/guild_members_repository.h"
|
|
#include "../common/repositories/instance_list_repository.h"
|
|
#include "../common/repositories/inventory_snapshots_repository.h"
|
|
#include "../common/repositories/ip_exemptions_repository.h"
|
|
#include "../common/repositories/name_filter_repository.h"
|
|
#include "../common/repositories/npc_types_repository.h"
|
|
#include "../common/repositories/raid_details_repository.h"
|
|
#include "../common/repositories/raid_members_repository.h"
|
|
#include "../common/repositories/reports_repository.h"
|
|
#include "../common/repositories/variables_repository.h"
|
|
#include "../common/repositories/character_pet_name_repository.h"
|
|
#include "../common/events/player_event_logs.h"
|
|
|
|
// Disgrace: for windows compile
|
|
#ifdef _WINDOWS
|
|
#include <windows.h>
|
|
#define snprintf _snprintf
|
|
#define strncasecmp _strnicmp
|
|
#define strcasecmp _stricmp
|
|
#else
|
|
#include "unix.h"
|
|
#include <netinet/in.h>
|
|
#include <sys/time.h>
|
|
|
|
#endif
|
|
|
|
#include "database.h"
|
|
#include "data_verification.h"
|
|
#include "eq_packet_structs.h"
|
|
#include "extprofile.h"
|
|
#include "strings.h"
|
|
#include "database_schema.h"
|
|
#include "http/httplib.h"
|
|
#include "http/uri.h"
|
|
|
|
#include "repositories/zone_repository.h"
|
|
#include "zone_store.h"
|
|
#include "repositories/merchantlist_temp_repository.h"
|
|
#include "repositories/bot_data_repository.h"
|
|
#include "repositories/trader_repository.h"
|
|
#include "repositories/buyer_repository.h"
|
|
|
|
extern Client client;
|
|
|
|
Database::Database() { }
|
|
|
|
Database::~Database() { }
|
|
|
|
Database::Database(
|
|
const std::string& host,
|
|
const std::string& user,
|
|
const std::string& password,
|
|
const std::string& database,
|
|
uint32 port
|
|
)
|
|
{
|
|
Connect(host, user, password, database, port);
|
|
}
|
|
|
|
bool Database::Connect(
|
|
const std::string& host,
|
|
const std::string& user,
|
|
const std::string& password,
|
|
const std::string& database,
|
|
uint32 port,
|
|
std::string connection_label
|
|
)
|
|
{
|
|
uint32 error_number = 0;
|
|
char error_buffer[MYSQL_ERRMSG_SIZE];
|
|
|
|
if (!Open(host.c_str(), user.c_str(), password.c_str(), database.c_str(), port, &error_number, error_buffer)) {
|
|
LogError("Connection [{}] Failed to connect to database Error [{}]", connection_label, error_buffer);
|
|
return false;
|
|
} else {
|
|
LogInfo("Connected to database [{}] [{}] @ [{}:{}]", connection_label, database, host, port);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
uint32 Database::CheckLogin(
|
|
const std::string& name,
|
|
const std::string& password,
|
|
const std::string& loginserver,
|
|
int16* status
|
|
)
|
|
{
|
|
if (name.length() >= 50 || password.length() >= 50) {
|
|
return 0;
|
|
}
|
|
|
|
const auto& l = AccountRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}' AND `ls_id` = '{}' AND `password` IS NOT NULL AND LENGTH(`password`) > 0 AND (`password` = '{}' OR `password` = MD5('{}'))",
|
|
Strings::Escape(name),
|
|
Strings::Escape(loginserver),
|
|
Strings::Escape(password),
|
|
Strings::Escape(password)
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return 0;
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
if (status) {
|
|
*status = e.status;
|
|
}
|
|
|
|
return e.id;
|
|
}
|
|
|
|
bool Database::CheckBannedIPs(const std::string& login_ip)
|
|
{
|
|
const std::string& query = fmt::format(
|
|
"SELECT `ip_address` FROM `banned_ips` WHERE `ip_address` = '{}'",
|
|
Strings::Escape(login_ip)
|
|
);
|
|
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success() || results.RowCount()) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Database::AddBannedIP(const std::string& banned_ip, const std::string& notes)
|
|
{
|
|
auto query = fmt::format(
|
|
"INSERT INTO banned_ips SET `ip_address` = '{}', `notes` = '{}'",
|
|
Strings::Escape(banned_ip),
|
|
Strings::Escape(notes)
|
|
);
|
|
|
|
auto results = QueryDatabase(query);
|
|
|
|
return results.Success();
|
|
}
|
|
|
|
bool Database::CheckGMIPs(const std::string& login_ip, uint32 account_id)
|
|
{
|
|
auto query = fmt::format(
|
|
"SELECT `name`, `account_id`, `ip_address` FROM `gm_ips` WHERE `ip_address` = '{}' AND `account_id` = {}",
|
|
Strings::Escape(login_ip),
|
|
account_id
|
|
);
|
|
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success()) {
|
|
return false;
|
|
}
|
|
|
|
return results.RowCount();
|
|
}
|
|
|
|
void Database::LoginIP(uint32 account_id, const std::string& login_ip)
|
|
{
|
|
auto query = fmt::format(
|
|
"INSERT INTO account_ip SET `accid` = {}, `ip` = '{}' ON DUPLICATE KEY UPDATE count = (count + 1), lastused = NOW()",
|
|
account_id,
|
|
Strings::Escape(login_ip)
|
|
);
|
|
|
|
QueryDatabase(query);
|
|
}
|
|
|
|
int16 Database::GetAccountStatus(uint32 account_id)
|
|
{
|
|
auto e = AccountRepository::FindOne(*this, account_id);
|
|
|
|
if (e.suspendeduntil > 0 && e.suspendeduntil < std::time(nullptr)) {
|
|
e.status = 0;
|
|
e.suspendeduntil = 0;
|
|
e.suspend_reason = "";
|
|
|
|
AccountRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
return e.status;
|
|
}
|
|
|
|
uint32 Database::CreateAccount(
|
|
const std::string& name,
|
|
const std::string& password,
|
|
int16 status,
|
|
const std::string& loginserver,
|
|
uint32 lsaccount_id
|
|
)
|
|
{
|
|
auto e = AccountRepository::NewEntity();
|
|
|
|
e.name = name;
|
|
e.status = status;
|
|
e.ls_id = loginserver;
|
|
e.lsaccount_id = lsaccount_id;
|
|
e.time_creation = std::time(nullptr);
|
|
|
|
if (!password.empty()) {
|
|
e.password = password;
|
|
}
|
|
|
|
LogInfo("Account attempting to be created loginserver [{}] name [{}] status [{}]", loginserver, name, status);
|
|
|
|
e = AccountRepository::InsertOne(*this, e);
|
|
|
|
if (!e.id) {
|
|
return 0;
|
|
}
|
|
|
|
return e.id;
|
|
}
|
|
|
|
bool Database::SetLocalPassword(uint32 account_id, const std::string& password)
|
|
{
|
|
return AccountRepository::UpdatePassword(*this, account_id, password);
|
|
}
|
|
|
|
bool Database::SetAccountStatus(const std::string& account_name, int16 status)
|
|
{
|
|
LogInfo("Account [{}] is attempting to be set to status [{}]", account_name, status);
|
|
|
|
auto l = AccountRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}'",
|
|
Strings::Escape(account_name)
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return false;
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
e.status = status;
|
|
|
|
return AccountRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
bool Database::ReserveName(uint32 account_id, const std::string& name)
|
|
{
|
|
const std::string& where_filter = fmt::format(
|
|
"`name` = '{}'",
|
|
Strings::Escape(name)
|
|
);
|
|
|
|
if (RuleB(Bots, Enabled)) {
|
|
const auto& b = BotDataRepository::GetWhere(*this, where_filter);
|
|
|
|
if (!b.empty()) {
|
|
LogInfo("Account [{}] requested name [{}] but name is already taken by a bot", account_id, name);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const auto& c = CharacterDataRepository::GetWhere(*this, where_filter);
|
|
|
|
if (!c.empty()) {
|
|
LogInfo("Account [{}] requested name [{}] but name is already taken by a character", account_id, name);
|
|
return false;
|
|
}
|
|
|
|
const auto& n = NpcTypesRepository::GetWhere(*this, where_filter);
|
|
|
|
if (!n.empty()) {
|
|
LogInfo("Account [{}] requested name [{}] but name is already taken by an NPC", account_id, name);
|
|
return false;
|
|
}
|
|
|
|
const auto& p = CharacterPetNameRepository::GetWhere(*this, where_filter);
|
|
if (!p.empty()) {
|
|
LogInfo("Account [{}] requested name [{}] but name is already taken by an Pet", account_id, name);
|
|
return false;
|
|
}
|
|
|
|
auto e = CharacterDataRepository::NewEntity();
|
|
|
|
e.account_id = account_id;
|
|
e.name = name;
|
|
|
|
e = CharacterDataRepository::InsertOne(*this, e);
|
|
|
|
if (!e.id) {
|
|
return false;
|
|
}
|
|
|
|
const uint32 guild_id = RuleI(Character, DefaultGuild);
|
|
const uint8 guild_rank = EQ::Clamp(RuleI(Character, DefaultGuildRank), 0, 8);
|
|
if (guild_id != 0) {
|
|
if (e.id) {
|
|
auto g = GuildMembersRepository::NewEntity();
|
|
|
|
g.char_id = e.id;
|
|
g.guild_id = guild_id;
|
|
g.rank_ = guild_rank;
|
|
|
|
GuildMembersRepository::InsertOne(*this, g);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Database::DeleteCharacter(const std::string& name)
|
|
{
|
|
if (name.empty()) {
|
|
LogInfo("Request to delete without a name.");
|
|
return false;
|
|
}
|
|
|
|
const auto& l = CharacterDataRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}'",
|
|
Strings::Escape(name)
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return false;
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
if (!e.id) {
|
|
return false;
|
|
}
|
|
|
|
std::string delete_type = "hard-deleted";
|
|
if (RuleB(Character, SoftDeletes)) {
|
|
delete_type = "soft-deleted";
|
|
|
|
std::string query = fmt::format(
|
|
SQL(
|
|
UPDATE character_data SET name = SUBSTRING(CONCAT(name, '-deleted-', UNIX_TIMESTAMP()), 1, 64), deleted_at = NOW()
|
|
WHERE id = {}
|
|
),
|
|
e.id
|
|
);
|
|
|
|
QueryDatabase(query);
|
|
|
|
if (RuleB(Bots, Enabled)) {
|
|
query = fmt::format(
|
|
SQL(
|
|
UPDATE bot_data SET name = SUBSTRING(CONCAT(name, '-deleted-', UNIX_TIMESTAMP()), 1, 64) WHERE owner_id = {}
|
|
),
|
|
e.id
|
|
);
|
|
|
|
QueryDatabase(query);
|
|
|
|
LogInfo(
|
|
"[DeleteCharacter] character_name [{}] ({}) bots are being [{}]",
|
|
e.name,
|
|
e.id,
|
|
delete_type
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
for (const auto &t : DatabaseSchema::GetCharacterTables()) {
|
|
const std::string& table_name = t.first;
|
|
const std::string& character_id_column_name = t.second;
|
|
|
|
QueryDatabase(
|
|
fmt::format(
|
|
"DELETE FROM {} WHERE {} = {}",
|
|
table_name,
|
|
character_id_column_name,
|
|
e.id
|
|
)
|
|
);
|
|
}
|
|
|
|
LogInfo("character_name [{}] ({}) is being [{}]", e.name, e.id, delete_type);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct *pp)
|
|
{
|
|
auto c = CharacterDataRepository::NewEntity();
|
|
|
|
c.id = character_id;
|
|
c.account_id = account_id;
|
|
c.name = pp->name;
|
|
c.last_name = pp->last_name;
|
|
c.gender = pp->gender;
|
|
c.race = pp->race;
|
|
c.class_ = pp->class_;
|
|
c.level = pp->level;
|
|
c.deity = pp->deity;
|
|
c.birthday = pp->birthday;
|
|
c.last_login = pp->lastlogin;
|
|
c.time_played = pp->timePlayedMin;
|
|
c.pvp_status = pp->pvp;
|
|
c.level2 = pp->level2;
|
|
c.anon = pp->anon;
|
|
c.gm = pp->gm;
|
|
c.intoxication = pp->intoxication;
|
|
c.hair_color = pp->haircolor;
|
|
c.beard_color = pp->beardcolor;
|
|
c.eye_color_1 = pp->eyecolor1;
|
|
c.eye_color_2 = pp->eyecolor2;
|
|
c.hair_style = pp->hairstyle;
|
|
c.beard = pp->beard;
|
|
c.ability_time_seconds = pp->ability_time_seconds;
|
|
c.ability_number = pp->ability_number;
|
|
c.ability_time_minutes = pp->ability_time_minutes;
|
|
c.ability_time_hours = pp->ability_time_hours;
|
|
c.title = pp->title;
|
|
c.suffix = pp->suffix;
|
|
c.exp = pp->exp;
|
|
c.points = pp->points;
|
|
c.mana = pp->mana;
|
|
c.cur_hp = pp->cur_hp;
|
|
c.str = pp->STR;
|
|
c.sta = pp->STA;
|
|
c.cha = pp->CHA;
|
|
c.dex = pp->DEX;
|
|
c.int_ = pp->INT;
|
|
c.agi = pp->AGI;
|
|
c.wis = pp->WIS;
|
|
c.face = pp->face;
|
|
c.y = pp->y;
|
|
c.x = pp->x;
|
|
c.z = pp->z;
|
|
c.heading = pp->heading;
|
|
c.pvp2 = pp->pvp2;
|
|
c.pvp_type = pp->pvptype;
|
|
c.autosplit_enabled = pp->autosplit;
|
|
c.zone_change_count = pp->zone_change_count;
|
|
c.drakkin_heritage = pp->drakkin_heritage;
|
|
c.drakkin_tattoo = pp->drakkin_tattoo;
|
|
c.drakkin_details = pp->drakkin_details;
|
|
c.toxicity = pp->toxicity;
|
|
c.hunger_level = pp->hunger_level;
|
|
c.thirst_level = pp->thirst_level;
|
|
c.ability_up = pp->ability_up;
|
|
c.zone_id = pp->zone_id;
|
|
c.zone_instance = pp->zoneInstance;
|
|
c.leadership_exp_on = pp->leadAAActive;
|
|
c.ldon_points_guk = pp->ldon_points_guk;
|
|
c.ldon_points_mir = pp->ldon_points_mir;
|
|
c.ldon_points_mmc = pp->ldon_points_mmc;
|
|
c.ldon_points_ruj = pp->ldon_points_ruj;
|
|
c.ldon_points_tak = pp->ldon_points_tak;
|
|
c.ldon_points_available = pp->ldon_points_available;
|
|
c.tribute_time_remaining = pp->tribute_time_remaining;
|
|
c.show_helm = pp->showhelm;
|
|
c.career_tribute_points = pp->career_tribute_points;
|
|
c.tribute_points = pp->tribute_points;
|
|
c.tribute_active = pp->tribute_active;
|
|
c.endurance = pp->endurance;
|
|
c.group_leadership_exp = pp->group_leadership_exp;
|
|
c.raid_leadership_exp = pp->raid_leadership_exp;
|
|
c.group_leadership_points = pp->group_leadership_points;
|
|
c.raid_leadership_points = pp->raid_leadership_points;
|
|
c.air_remaining = pp->air_remaining;
|
|
c.pvp_kills = pp->PVPKills;
|
|
c.pvp_deaths = pp->PVPDeaths;
|
|
c.pvp_current_points = pp->PVPCurrentPoints;
|
|
c.pvp_career_points = pp->PVPCareerPoints;
|
|
c.pvp_best_kill_streak = pp->PVPBestKillStreak;
|
|
c.pvp_worst_death_streak = pp->PVPWorstDeathStreak;
|
|
c.pvp_current_kill_streak = pp->PVPCurrentKillStreak;
|
|
c.aa_points_spent = pp->aapoints_spent;
|
|
c.aa_exp = pp->expAA;
|
|
c.aa_points = pp->aapoints;
|
|
c.group_auto_consent = pp->groupAutoconsent;
|
|
c.raid_auto_consent = pp->raidAutoconsent;
|
|
c.guild_auto_consent = pp->guildAutoconsent;
|
|
c.RestTimer = pp->RestTimer;
|
|
|
|
CharacterDataRepository::ReplaceOne(*this, c);
|
|
|
|
std::vector<CharacterBindRepository::CharacterBind> character_binds;
|
|
|
|
character_binds.reserve(5);
|
|
|
|
auto b = CharacterBindRepository::NewEntity();
|
|
|
|
b.id = character_id;
|
|
|
|
for (uint8 slot_id = 0; slot_id < 5; slot_id++) {
|
|
b.zone_id = pp->binds[slot_id].zone_id;
|
|
b.x = pp->binds[slot_id].x;
|
|
b.y = pp->binds[slot_id].y;
|
|
b.z = pp->binds[slot_id].z;
|
|
b.heading = pp->binds[slot_id].heading;
|
|
b.slot = slot_id;
|
|
|
|
character_binds.emplace_back(b);
|
|
}
|
|
|
|
CharacterBindRepository::ReplaceMany(*this, character_binds);
|
|
|
|
if (RuleB(Character, GrantHoTTOnCreate)) {
|
|
CharacterLeadershipAbilitiesRepository::InsertOne(
|
|
*this,
|
|
CharacterLeadershipAbilitiesRepository::CharacterLeadershipAbilities{
|
|
.id = character_id,
|
|
.slot = LeadershipAbilitySlot::HealthOfTargetsTarget,
|
|
.rank_ = 1
|
|
}
|
|
);
|
|
}
|
|
|
|
std::vector<CharacterSkillsRepository::CharacterSkills> character_skills;
|
|
|
|
character_skills.reserve(MAX_PP_SKILL);
|
|
|
|
for (uint16 slot_id = 0; slot_id < MAX_PP_SKILL; slot_id++) {
|
|
character_skills.emplace_back(
|
|
CharacterSkillsRepository::CharacterSkills{
|
|
.id = character_id,
|
|
.skill_id = slot_id,
|
|
.value = static_cast<uint16_t>(pp->skills[slot_id])
|
|
}
|
|
);
|
|
}
|
|
|
|
CharacterSkillsRepository::ReplaceMany(*this, character_skills);
|
|
|
|
std::vector<CharacterLanguagesRepository::CharacterLanguages> character_languages;
|
|
|
|
character_languages.reserve(MAX_PP_LANGUAGE);
|
|
|
|
for (uint16 slot_id = 0; slot_id < MAX_PP_LANGUAGE; slot_id++) {
|
|
character_languages.emplace_back(
|
|
CharacterLanguagesRepository::CharacterLanguages{
|
|
.id = character_id,
|
|
.lang_id = slot_id,
|
|
.value = static_cast<uint16_t>(pp->languages[slot_id])
|
|
}
|
|
);
|
|
}
|
|
|
|
CharacterLanguagesRepository::ReplaceMany(*this, character_languages);
|
|
|
|
return true;
|
|
}
|
|
|
|
uint32 Database::GetCharacterID(const std::string& name)
|
|
{
|
|
const auto& l = CharacterDataRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}'",
|
|
Strings::Escape(name)
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return 0;
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
return e.id;
|
|
}
|
|
|
|
uint32 Database::GetAccountIDByChar(const std::string& name, uint32* character_id)
|
|
{
|
|
const auto& l = CharacterDataRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}'",
|
|
Strings::Escape(name)
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return 0;
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
if (character_id) {
|
|
*character_id = e.id;
|
|
}
|
|
|
|
return e.account_id;
|
|
}
|
|
|
|
uint32 Database::GetAccountIDByChar(uint32 character_id)
|
|
{
|
|
const auto& e = CharacterDataRepository::FindOne(*this, character_id);
|
|
|
|
return e.id ? e.account_id : 0;
|
|
}
|
|
|
|
uint32 Database::GetAccountIDByName(const std::string& account_name, const std::string& loginserver, int16* status, uint32* lsaccount_id)
|
|
{
|
|
if (!isAlphaNumeric(account_name.c_str())) {
|
|
return 0;
|
|
}
|
|
|
|
const auto& l = AccountRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}' AND `ls_id` = '{}' LIMIT 1",
|
|
Strings::Escape(account_name),
|
|
Strings::Escape(loginserver)
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return 0;
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
if (status) {
|
|
*status = e.status;
|
|
}
|
|
|
|
if (lsaccount_id) {
|
|
*lsaccount_id = e.lsaccount_id;
|
|
}
|
|
|
|
return e.id;
|
|
}
|
|
|
|
const std::string Database::GetAccountName(uint32 account_id, uint32* lsaccount_id)
|
|
{
|
|
const auto& e = AccountRepository::FindOne(*this, account_id);
|
|
|
|
if (!e.id) {
|
|
return std::string();
|
|
}
|
|
|
|
if (lsaccount_id) {
|
|
*lsaccount_id = e.lsaccount_id;
|
|
}
|
|
|
|
return e.name;
|
|
}
|
|
|
|
const std::string Database::GetCharName(uint32 character_id)
|
|
{
|
|
const auto& e = CharacterDataRepository::FindOne(*this, character_id);
|
|
|
|
return e.id ? e.name : std::string();
|
|
}
|
|
|
|
const std::string Database::GetCharNameByID(uint32 character_id)
|
|
{
|
|
const auto& e = CharacterDataRepository::FindOne(*this, character_id);
|
|
|
|
return e.id ? e.name : std::string();
|
|
}
|
|
|
|
const std::string Database::GetNPCNameByID(uint32 npc_id)
|
|
{
|
|
const auto& e = NpcTypesRepository::FindOne(*this, npc_id);
|
|
|
|
return e.id ? e.name : std::string();
|
|
}
|
|
|
|
const std::string Database::GetCleanNPCNameByID(uint32 npc_id)
|
|
{
|
|
const auto& e = NpcTypesRepository::FindOne(*this, npc_id);
|
|
|
|
if (!e.id) {
|
|
return std::string();
|
|
}
|
|
|
|
std::string mob_name = e.name;
|
|
std::string result;
|
|
|
|
CleanMobName(mob_name.begin(), mob_name.end(), std::back_inserter(result));
|
|
|
|
return result;
|
|
}
|
|
|
|
bool Database::LoadVariables()
|
|
{
|
|
const auto& l = VariablesRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"UNIX_TIMESTAMP(`ts`) >= {}",
|
|
varcache.last_update
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return true;
|
|
}
|
|
|
|
LockMutex lock(&Mvarcache);
|
|
|
|
for (const auto& e : l) {
|
|
varcache.last_update = std::time(nullptr);
|
|
|
|
varcache.Add(Strings::ToLower(e.varname), e.value);
|
|
}
|
|
|
|
LogInfo(
|
|
"Loaded [{}] Variable{}",
|
|
Strings::Commify(l.size()),
|
|
l.size() != 1 ? "s" : ""
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Database::GetVariable(const std::string& name, std::string& value)
|
|
{
|
|
LockMutex lock(&Mvarcache);
|
|
|
|
if (name.empty()) {
|
|
return false;
|
|
}
|
|
|
|
auto v = varcache.Get(Strings::ToLower(name));
|
|
|
|
if (v) {
|
|
value = *v;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Database::SetVariable(const std::string& name, const std::string& value)
|
|
{
|
|
auto l = VariablesRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`varname` = '{}'",
|
|
Strings::Escape(name)
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return false;
|
|
}
|
|
|
|
auto& variable = l.front();
|
|
|
|
variable.value = value;
|
|
|
|
const int updated = VariablesRepository::UpdateOne(*this, variable);
|
|
|
|
if (updated) {
|
|
LoadVariables();
|
|
|
|
return true;
|
|
}
|
|
|
|
auto e = VariablesRepository::NewEntity();
|
|
|
|
e.varname = name;
|
|
e.value = value;
|
|
|
|
e = VariablesRepository::InsertOne(*this, e);
|
|
|
|
if (!e.id) {
|
|
return false;
|
|
}
|
|
|
|
LoadVariables();
|
|
|
|
return true;
|
|
}
|
|
|
|
void Database::SetAccountCRCField(uint32 account_id, const std::string& field_name, uint64 checksum)
|
|
{
|
|
auto e = AccountRepository::FindOne(*this, account_id);
|
|
|
|
if (Strings::EqualFold(field_name, "crc_basedata")) {
|
|
e.crc_basedata = checksum;
|
|
} else if (Strings::EqualFold(field_name, "crc_eqgame")) {
|
|
e.crc_eqgame = checksum;
|
|
} else if (Strings::EqualFold(field_name, "crc_skillcaps")) {
|
|
e.crc_skillcaps = checksum;
|
|
}
|
|
|
|
AccountRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
uint8 Database::GetPEQZone(uint32 zone_id, uint32 version)
|
|
{
|
|
auto z = GetZoneVersionWithFallback(zone_id, version);
|
|
|
|
return z ? z->peqzone : 0;
|
|
}
|
|
|
|
bool Database::CheckNameFilter(const std::string& name, bool surname)
|
|
{
|
|
// the minimum 4 is enforced by the client too
|
|
if (name.empty() || name.size() < 4) {
|
|
return false;
|
|
}
|
|
|
|
// Given name length is enforced by the client too
|
|
if (!surname && name.size() > 15) {
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < name.size(); i++) {
|
|
if (!isalpha(name[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
char c = '\0';
|
|
uint8 num_c = 0;
|
|
for (size_t x = 0; x < name.size(); ++x) {
|
|
if (name[x] == c) {
|
|
num_c++;
|
|
} else {
|
|
num_c = 1;
|
|
c = name[x];
|
|
}
|
|
|
|
if (num_c > 2) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const auto& l = NameFilterRepository::All(*this);
|
|
|
|
if (l.empty()) {
|
|
return true;
|
|
}
|
|
|
|
for (const auto& e : l) {
|
|
if (Strings::Contains(Strings::ToLower(name), Strings::ToLower(e.name))) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Database::AddToNameFilter(const std::string& name)
|
|
{
|
|
auto e = NameFilterRepository::NewEntity();
|
|
|
|
e.name = name;
|
|
|
|
return NameFilterRepository::InsertOne(*this, e).id;
|
|
}
|
|
|
|
uint32 Database::GetAccountIDFromLSID(
|
|
const std::string& in_loginserver_id,
|
|
uint32 in_loginserver_account_id,
|
|
char* in_account_name,
|
|
int16* in_status
|
|
)
|
|
{
|
|
const auto& l = AccountRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`lsaccount_id` = {} AND `ls_id` = '{}'",
|
|
in_loginserver_account_id,
|
|
in_loginserver_id
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return 0;
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
if (in_account_name) {
|
|
strcpy(in_account_name, e.name.c_str());
|
|
}
|
|
|
|
if (in_status) {
|
|
*in_status = e.status;
|
|
}
|
|
|
|
return e.id;
|
|
}
|
|
|
|
void Database::ClearMerchantTemp()
|
|
{
|
|
MerchantlistTempRepository::ClearTemporaryMerchantLists(*this);
|
|
}
|
|
|
|
bool Database::UpdateName(const std::string& old_name, const std::string& new_name)
|
|
{
|
|
LogInfo("Renaming [{}] to [{}]", old_name, new_name);
|
|
|
|
auto l = CharacterDataRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}'",
|
|
Strings::Escape(old_name)
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return false;
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
e.name = new_name;
|
|
|
|
return CharacterDataRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
bool Database::UpdateNameByID(const int character_id, const std::string& new_name)
|
|
{
|
|
LogInfo("Renaming [{}] to [{}]", character_id, new_name);
|
|
|
|
auto l = CharacterDataRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`id` = {}",
|
|
character_id
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return false;
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
e.name = new_name;
|
|
|
|
return CharacterDataRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
bool Database::IsNameUsed(const std::string& name)
|
|
{
|
|
if (RuleB(Bots, Enabled)) {
|
|
const auto& bot_data = BotDataRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}'",
|
|
Strings::Escape(name)
|
|
)
|
|
);
|
|
|
|
if (!bot_data.empty()) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
const auto& character_data = CharacterDataRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}'",
|
|
Strings::Escape(name)
|
|
)
|
|
);
|
|
|
|
return !character_data.empty();
|
|
}
|
|
|
|
// Players cannot have the same name as a pet vanity name, or memory corruption occurs.
|
|
bool Database::IsPetNameUsed(const std::string& name)
|
|
{
|
|
const auto& pet_name_data = CharacterPetNameRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}'",
|
|
Strings::Escape(name)
|
|
)
|
|
);
|
|
|
|
return !pet_name_data.empty();
|
|
}
|
|
|
|
uint32 Database::GetServerType()
|
|
{
|
|
const auto& l = VariablesRepository::GetWhere(*this, "`varname` = 'ServerType' LIMIT 1");
|
|
|
|
if (l.empty()) {
|
|
return 0;
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
return Strings::ToUnsignedInt(e.value);
|
|
}
|
|
|
|
bool Database::MoveCharacterToZone(uint32 character_id, uint32 zone_id)
|
|
{
|
|
auto e = CharacterDataRepository::FindOne(*this, character_id);
|
|
|
|
e.zone_id = zone_id;
|
|
|
|
e.x = e.y = e.z = -1;
|
|
|
|
return CharacterDataRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
bool Database::MoveCharacterToZone(const std::string& name, uint32 zone_id)
|
|
{
|
|
auto l = CharacterDataRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}'",
|
|
Strings::Escape(name)
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return false;
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
e.zone_id = zone_id;
|
|
|
|
e.x = e.y = e.z = -1;
|
|
|
|
return CharacterDataRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
bool Database::UpdateLiveChar(const std::string& name, uint32 account_id)
|
|
{
|
|
auto e = AccountRepository::FindOne(*this, account_id);
|
|
|
|
if (!e.id) {
|
|
return false;
|
|
}
|
|
|
|
e.charname = name;
|
|
|
|
return AccountRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
const std::string Database::GetLiveChar(uint32 account_id)
|
|
{
|
|
auto e = AccountRepository::FindOne(*this, account_id);
|
|
|
|
return e.id ? e.charname : std::string();
|
|
}
|
|
|
|
void Database::SetLFP(uint32 character_id, bool is_lfp)
|
|
{
|
|
auto e = CharacterDataRepository::FindOne(*this, character_id);
|
|
|
|
e.lfp = is_lfp ? 1 : 0;
|
|
|
|
CharacterDataRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
void Database::SetLoginFlags(uint32 character_id, bool is_lfp, bool is_lfg, uint8 first_logon)
|
|
{
|
|
auto e = CharacterDataRepository::FindOne(*this, character_id);
|
|
|
|
e.firstlogon = first_logon;
|
|
e.lfg = is_lfg ? 1 : 0;
|
|
e.lfp = is_lfp ? 1 : 0;
|
|
|
|
CharacterDataRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
void Database::SetLFG(uint32 character_id, bool is_lfg)
|
|
{
|
|
auto e = CharacterDataRepository::FindOne(*this, character_id);
|
|
|
|
e.lfg = is_lfg ? 1 : 0;
|
|
|
|
CharacterDataRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
void Database::SetFirstLogon(uint32 character_id, uint8 first_logon)
|
|
{
|
|
auto e = CharacterDataRepository::FindOne(*this, character_id);
|
|
|
|
e.firstlogon = first_logon;
|
|
|
|
CharacterDataRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
void Database::AddReport(const std::string& who, const std::string& against, const std::string& lines)
|
|
{
|
|
auto e = ReportsRepository::NewEntity();
|
|
|
|
e.name = Strings::Escape(who);
|
|
e.reported = Strings::Escape(against);
|
|
e.reported_text = Strings::Escape(lines);
|
|
|
|
ReportsRepository::InsertOne(*this, e);
|
|
}
|
|
|
|
void Database::ClearAllGroups()
|
|
{
|
|
GroupIdRepository::ClearAllGroups(*this);
|
|
}
|
|
|
|
void Database::ClearGroup(uint32 group_id) {
|
|
ClearGroupLeader(group_id);
|
|
|
|
if (!group_id) {
|
|
//clear all groups
|
|
ClearAllGroups();
|
|
return;
|
|
}
|
|
|
|
//clear a specific group
|
|
GroupIdRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`group_id` = {}",
|
|
group_id
|
|
)
|
|
);
|
|
}
|
|
|
|
uint32 Database::GetGroupID(const std::string& name)
|
|
{
|
|
const auto& l = GroupIdRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}'",
|
|
Strings::Escape(name)
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return 0;
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
return e.group_id;
|
|
}
|
|
|
|
std::string Database::GetGroupLeaderForLogin(const std::string& character_name)
|
|
{
|
|
const auto& g = GroupIdRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}'",
|
|
Strings::Escape(character_name)
|
|
)
|
|
);
|
|
|
|
if (g.empty()) {
|
|
return std::string();
|
|
}
|
|
|
|
auto& group = g.front();
|
|
|
|
const uint32 group_id = group.group_id;
|
|
|
|
const auto& e = GroupLeadersRepository::FindOne(*this, group_id);
|
|
|
|
return e.gid ? e.leadername : std::string();
|
|
}
|
|
|
|
void Database::SetGroupLeaderName(uint32 group_id, const std::string &name)
|
|
{
|
|
auto e = GroupLeadersRepository::FindOne(*this, group_id);
|
|
|
|
e.leadername = name;
|
|
|
|
if (e.gid) {
|
|
GroupLeadersRepository::UpdateOne(*this, e);
|
|
return;
|
|
}
|
|
|
|
e.gid = group_id;
|
|
e.marknpc = std::string();
|
|
e.leadershipaa = std::string();
|
|
e.maintank = std::string();
|
|
e.assist = std::string();
|
|
e.puller = std::string();
|
|
e.mentoree = std::string();
|
|
e.mentor_percent = 0;
|
|
|
|
GroupLeadersRepository::ReplaceOne(*this, e);
|
|
}
|
|
|
|
std::string Database::GetGroupLeaderName(uint32 group_id)
|
|
{
|
|
const std::string& query = fmt::format(
|
|
"SELECT `leadername` FROM `group_leaders` WHERE `gid` = {}",
|
|
group_id
|
|
);
|
|
|
|
auto results = QueryDatabase(query);
|
|
|
|
if (!results.Success() || !results.RowCount()) {
|
|
return std::string();
|
|
}
|
|
|
|
auto& row = results.begin();
|
|
|
|
return row[0];
|
|
}
|
|
|
|
char* Database::GetGroupLeadershipInfo(
|
|
uint32 group_id,
|
|
char* leaderbuf,
|
|
char* maintank,
|
|
char* assist,
|
|
char* puller,
|
|
char* marknpc,
|
|
char* mentoree,
|
|
int* mentor_percent,
|
|
GroupLeadershipAA_Struct* GLAA
|
|
)
|
|
{
|
|
auto e = GroupLeadersRepository::FindOne(*this, group_id);
|
|
|
|
if (!e.gid) {
|
|
if (leaderbuf) {
|
|
strcpy(leaderbuf, "UNKNOWN");
|
|
}
|
|
|
|
if (maintank) {
|
|
maintank[0] = '\0';
|
|
}
|
|
|
|
if (assist) {
|
|
assist[0] = '\0';
|
|
}
|
|
|
|
if (puller) {
|
|
puller[0] = '\0';
|
|
}
|
|
|
|
if (marknpc) {
|
|
marknpc[0] = '\0';
|
|
}
|
|
|
|
if (mentoree) {
|
|
mentoree[0] = '\0';
|
|
}
|
|
|
|
if (mentor_percent) {
|
|
*mentor_percent = 0;
|
|
}
|
|
|
|
return leaderbuf;
|
|
}
|
|
|
|
if (leaderbuf) {
|
|
strcpy(leaderbuf, e.leadername.c_str());
|
|
}
|
|
|
|
if (maintank) {
|
|
strcpy(maintank, e.maintank.c_str());
|
|
}
|
|
|
|
if (assist) {
|
|
strcpy(assist, e.assist.c_str());
|
|
}
|
|
|
|
if (puller) {
|
|
strcpy(puller, e.puller.c_str());
|
|
}
|
|
|
|
if (marknpc) {
|
|
strcpy(marknpc, e.marknpc.c_str());
|
|
}
|
|
|
|
if (mentoree) {
|
|
strcpy(mentoree, e.mentoree.c_str());
|
|
}
|
|
|
|
if (mentor_percent) {
|
|
*mentor_percent = e.mentor_percent;
|
|
}
|
|
if(GLAA && e.leadershipaa.length() == sizeof(GroupLeadershipAA_Struct)) {
|
|
Decode(e.leadershipaa);
|
|
memcpy(GLAA, e.leadershipaa.data(), sizeof(GroupLeadershipAA_Struct));
|
|
}
|
|
|
|
return leaderbuf;
|
|
}
|
|
|
|
void Database::ClearAllGroupLeaders()
|
|
{
|
|
GroupLeadersRepository::ClearAllGroupLeaders(*this);
|
|
}
|
|
|
|
void Database::ClearGroupLeader(uint32 group_id)
|
|
{
|
|
if (!group_id) {
|
|
ClearAllGroupLeaders();
|
|
return;
|
|
}
|
|
|
|
GroupLeadersRepository::DeleteOne(*this, group_id);
|
|
}
|
|
|
|
uint8 Database::GetAgreementFlag(uint32 account_id)
|
|
{
|
|
const auto& e = AccountRepository::FindOne(*this, account_id);
|
|
|
|
return !e.id ? 0 : e.rulesflag;
|
|
}
|
|
|
|
void Database::SetAgreementFlag(uint32 account_id)
|
|
{
|
|
auto e = AccountRepository::FindOne(*this, account_id);
|
|
|
|
e.rulesflag = 1;
|
|
|
|
AccountRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
void Database::ClearRaid(uint32 raid_id)
|
|
{
|
|
if (!raid_id) {
|
|
ClearAllRaids();
|
|
return;
|
|
}
|
|
|
|
RaidMembersRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`raidid` = {}",
|
|
raid_id
|
|
)
|
|
);
|
|
}
|
|
|
|
void Database::ClearAllRaids()
|
|
{
|
|
RaidMembersRepository::ClearAllRaids(*this);
|
|
}
|
|
|
|
void Database::ClearAllRaidDetails()
|
|
{
|
|
RaidDetailsRepository::ClearAllRaidDetails(*this);
|
|
}
|
|
|
|
void Database::ClearRaidDetails(uint32 raid_id) {
|
|
|
|
if (!raid_id) {
|
|
ClearAllRaidDetails();
|
|
return;
|
|
}
|
|
|
|
RaidDetailsRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`raidid` = {}",
|
|
raid_id
|
|
)
|
|
);
|
|
}
|
|
|
|
void Database::PurgeAllDeletedDataBuckets()
|
|
{
|
|
DataBucketsRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"(`expires` < {} AND `expires` > 0)",
|
|
std::time(nullptr)
|
|
)
|
|
);
|
|
}
|
|
|
|
uint32 Database::GetRaidID(const std::string& name)
|
|
{
|
|
const auto& l = RaidMembersRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}'",
|
|
Strings::Escape(name)
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return 0;
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
return e.raidid;
|
|
}
|
|
|
|
const std::string Database::GetRaidLeaderName(uint32 raid_id)
|
|
{
|
|
const auto& l = RaidMembersRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`raidid` = {} AND `israidleader` = 1",
|
|
raid_id
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
LogDebug("Unable to get Raid Leader Name for Raid ID [{}]", raid_id);
|
|
return "UNKNOWN";
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
return e.name;
|
|
}
|
|
|
|
void Database::GetGroupLeadershipInfo(
|
|
uint32 group_id,
|
|
uint32 raid_id,
|
|
char* maintank,
|
|
char* assist,
|
|
char* puller,
|
|
char* marknpc,
|
|
char* mentoree,
|
|
int* mentor_percent,
|
|
GroupLeadershipAA_Struct* GLAA
|
|
)
|
|
{
|
|
const std::string& query = fmt::format(
|
|
SQL(
|
|
SELECT `maintank`, `assist`, `puller`, `marknpc`, `mentoree`, `mentor_percent`, `leadershipaa`
|
|
FROM `raid_leaders`
|
|
WHERE `gid` = {} AND `rid` = {}
|
|
),
|
|
group_id,
|
|
raid_id
|
|
);
|
|
|
|
auto results = QueryDatabase(query);
|
|
|
|
if (!results.Success() || !results.RowCount()) {
|
|
if (maintank) {
|
|
maintank[0] = '\0';
|
|
}
|
|
|
|
if (assist) {
|
|
assist[0] = '\0';
|
|
}
|
|
|
|
if (puller) {
|
|
puller[0] = '\0';
|
|
}
|
|
|
|
if (marknpc) {
|
|
marknpc[0] = '\0';
|
|
}
|
|
|
|
if (mentoree) {
|
|
mentoree[0] = '\0';
|
|
}
|
|
|
|
if (mentor_percent) {
|
|
*mentor_percent = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
auto& row = results.begin();
|
|
|
|
if (maintank) {
|
|
strcpy(maintank, row[0]);
|
|
}
|
|
|
|
if (assist) {
|
|
strcpy(assist, row[1]);
|
|
}
|
|
|
|
if (puller) {
|
|
strcpy(puller, row[2]);
|
|
}
|
|
|
|
if (marknpc) {
|
|
strcpy(marknpc, row[3]);
|
|
}
|
|
|
|
if (mentoree) {
|
|
strcpy(mentoree, row[4]);
|
|
}
|
|
|
|
if (mentor_percent) {
|
|
*mentor_percent = Strings::ToInt(row[5]);
|
|
}
|
|
|
|
if (GLAA && results.LengthOfColumn(6) == sizeof(GroupLeadershipAA_Struct)) {
|
|
memcpy(GLAA, row[6], sizeof(GroupLeadershipAA_Struct));
|
|
}
|
|
}
|
|
|
|
void Database::GetRaidLeadershipInfo(
|
|
uint32 raid_id,
|
|
char* maintank,
|
|
char* assist,
|
|
char* puller,
|
|
char* marknpc,
|
|
RaidLeadershipAA_Struct* RLAA
|
|
)
|
|
{
|
|
std::string query = fmt::format(
|
|
SQL(
|
|
SELECT `maintank`, `assist`, `puller`, `marknpc`, `leadershipaa`
|
|
FROM `raid_leaders`
|
|
WHERE `gid` = {} AND `rid` = {}
|
|
),
|
|
std::numeric_limits<uint32>::max(),
|
|
raid_id
|
|
);
|
|
|
|
auto results = QueryDatabase(query);
|
|
|
|
if (!results.Success() || !results.RowCount()) {
|
|
if (maintank) {
|
|
maintank[0] = '\0';
|
|
}
|
|
|
|
if (assist) {
|
|
assist[0] = '\0';
|
|
}
|
|
|
|
if (puller) {
|
|
puller[0] = '\0';
|
|
}
|
|
|
|
if (marknpc) {
|
|
marknpc[0] = '\0';
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
auto& row = results.begin();
|
|
|
|
if (maintank) {
|
|
strcpy(maintank, row[0]);
|
|
}
|
|
|
|
if (assist) {
|
|
strcpy(assist, row[1]);
|
|
}
|
|
|
|
if (puller) {
|
|
strcpy(puller, row[2]);
|
|
}
|
|
|
|
if (marknpc) {
|
|
strcpy(marknpc, row[3]);
|
|
}
|
|
|
|
if (RLAA && results.LengthOfColumn(4) == sizeof(RaidLeadershipAA_Struct)) {
|
|
memcpy(RLAA, row[4], sizeof(RaidLeadershipAA_Struct));
|
|
}
|
|
}
|
|
|
|
void Database::SetRaidGroupLeaderInfo(uint32 group_id, uint32 raid_id)
|
|
{
|
|
std::string query = fmt::format(
|
|
"UPDATE `raid_leaders` SET `leadershipaa` = '' WHERE `gid` = {} AND `rid` = {}",
|
|
group_id,
|
|
raid_id
|
|
);
|
|
|
|
auto results = QueryDatabase(query);
|
|
|
|
if (results.RowsAffected() != 0) {
|
|
return;
|
|
}
|
|
|
|
query = fmt::format(
|
|
SQL(
|
|
REPLACE INTO `raid_leaders` (`gid`, `rid`, `marknpc`, `leadershipaa`, `maintank`, `assist`, `puller`, `mentoree`, `mentor_percent`) VALUES
|
|
({}, {}, '', '', '', '', '', '', 0)
|
|
),
|
|
group_id,
|
|
raid_id
|
|
);
|
|
|
|
QueryDatabase(query);
|
|
}
|
|
|
|
void Database::ClearAllRaidLeaders()
|
|
{
|
|
QueryDatabase("DELETE FROM `raid_leaders`");
|
|
}
|
|
|
|
void Database::ClearRaidLeader(uint32 group_id, uint32 raid_id)
|
|
{
|
|
if (!raid_id) {
|
|
ClearAllRaidLeaders();
|
|
return;
|
|
}
|
|
|
|
const std::string& query = fmt::format(
|
|
"DELETE FROM `raid_leaders` WHERE `gid` = {} AND `rid` = {}",
|
|
group_id,
|
|
raid_id
|
|
);
|
|
|
|
QueryDatabase(query);
|
|
}
|
|
|
|
void Database::UpdateAdventureStatsEntry(uint32 character_id, uint8 theme_id, bool is_win, bool is_remove)
|
|
{
|
|
AdventureStatsRepository::UpdateAdventureStatsEntry(*this, character_id, theme_id, is_win, is_remove);
|
|
}
|
|
|
|
bool Database::GetAdventureStats(uint32 character_id, AdventureStats_Struct* as)
|
|
{
|
|
const auto& e = AdventureStatsRepository::FindOne(*this, character_id);
|
|
|
|
if (!e.player_id) {
|
|
return false;
|
|
}
|
|
|
|
as->success.guk = e.guk_wins;
|
|
as->failure.guk = e.guk_losses;
|
|
as->success.mir = e.mir_wins;
|
|
as->failure.mir = e.mir_losses;
|
|
as->success.mmc = e.mmc_wins;
|
|
as->failure.mmc = e.mmc_losses;
|
|
as->success.ruj = e.ruj_wins;
|
|
as->failure.ruj = e.ruj_losses;
|
|
as->success.tak = e.tak_wins;
|
|
as->failure.tak = e.tak_losses;
|
|
|
|
as->failure.total = (
|
|
as->failure.guk +
|
|
as->failure.mir +
|
|
as->failure.mmc +
|
|
as->failure.ruj +
|
|
as->failure.tak
|
|
);
|
|
|
|
as->success.total = (
|
|
as->success.guk +
|
|
as->success.mir +
|
|
as->success.mmc +
|
|
as->success.ruj +
|
|
as->success.tak
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
uint32 Database::GetGuildIDByCharID(uint32 character_id)
|
|
{
|
|
const auto& e = GuildMembersRepository::FindOne(*this, character_id);
|
|
|
|
return e.char_id ? e.guild_id : 0;
|
|
}
|
|
|
|
uint32 Database::GetGroupIDByCharID(uint32 character_id)
|
|
{
|
|
const auto& e = GroupIdRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`character_id` = {}",
|
|
character_id
|
|
)
|
|
);
|
|
|
|
return e.size() == 1 ? e.front().group_id : 0;
|
|
}
|
|
|
|
uint32 Database::GetRaidIDByCharID(uint32 character_id)
|
|
{
|
|
|
|
const auto& e = RaidMembersRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`charid` = {}",
|
|
character_id
|
|
)
|
|
);
|
|
|
|
return e.size() == 1 ? e.front().raidid : 0;
|
|
}
|
|
|
|
int64 Database::CountInvSnapshots()
|
|
{
|
|
return InventorySnapshotsRepository::CountInventorySnapshots(*this);
|
|
}
|
|
|
|
void Database::ClearInvSnapshots(bool from_now)
|
|
{
|
|
uint32 delete_time = time(nullptr);
|
|
|
|
if (!from_now) {
|
|
delete_time -= RuleI(Character, InvSnapshotHistoryD) * 86400;
|
|
}
|
|
|
|
InventorySnapshotsRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`time_index` <= {}",
|
|
delete_time
|
|
)
|
|
);
|
|
}
|
|
|
|
struct TimeOfDay_Struct Database::LoadTime(time_t& realtime)
|
|
{
|
|
TimeOfDay_Struct t{ };
|
|
|
|
const std::string& query = "SELECT `minute`, `hour`, `day`, `month`, `year`, `realtime` FROM `eqtime` LIMIT 1";
|
|
auto results = QueryDatabase(query);
|
|
|
|
if (!results.Success() || !results.RowCount()) {
|
|
LogInfo("Loading EQ time of day failed. Using defaults");
|
|
t.minute = 0;
|
|
t.hour = 9;
|
|
t.day = 1;
|
|
t.month = 1;
|
|
t.year = 3100;
|
|
realtime = time(nullptr);
|
|
return t;
|
|
}
|
|
|
|
auto& row = results.begin();
|
|
|
|
uint8 hour = Strings::ToUnsignedInt(row[1]);
|
|
time_t realtime_ = Strings::ToBigInt(row[5]);
|
|
if (RuleI(World, BootHour) > 0 && RuleI(World, BootHour) <= 24) {
|
|
hour = RuleI(World, BootHour);
|
|
realtime_ = time(nullptr);
|
|
}
|
|
|
|
t.minute = Strings::ToUnsignedInt(row[0]);
|
|
t.hour = hour;
|
|
t.day = Strings::ToUnsignedInt(row[2]);
|
|
t.month = Strings::ToUnsignedInt(row[3]);
|
|
t.year = Strings::ToUnsignedInt(row[4]);
|
|
realtime = realtime_;
|
|
|
|
LogEqTime("Setting hour to [{}]", hour);
|
|
|
|
return t;
|
|
}
|
|
|
|
bool Database::SaveTime(int8 minute, int8 hour, int8 day, int8 month, int16 year)
|
|
{
|
|
const std::string& query = fmt::format(
|
|
"UPDATE `eqtime` SET `minute` = {}, `hour` = {}, `day` = {}, `month` = {}, `year` = {}, `realtime` = {} LIMIT 1",
|
|
minute,
|
|
hour,
|
|
day,
|
|
month,
|
|
year,
|
|
std::time(nullptr)
|
|
);
|
|
|
|
auto results = QueryDatabase(query);
|
|
|
|
return results.Success();
|
|
}
|
|
|
|
int Database::GetIPExemption(const std::string& account_ip)
|
|
{
|
|
const auto& l = IpExemptionsRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`exemption_ip` = '{}'",
|
|
Strings::Escape(account_ip)
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return RuleI(World, MaxClientsPerIP);
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
return e.exemption_amount;
|
|
}
|
|
|
|
void Database::SetIPExemption(const std::string& account_ip, int exemption_amount)
|
|
{
|
|
auto l = IpExemptionsRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`exemption_ip` = '{}'",
|
|
Strings::Escape(account_ip)
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
auto e = IpExemptionsRepository::NewEntity();
|
|
|
|
e.exemption_ip = account_ip;
|
|
e.exemption_amount = exemption_amount;
|
|
|
|
IpExemptionsRepository::InsertOne(*this, e);
|
|
|
|
return;
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
e.exemption_amount = exemption_amount;
|
|
|
|
IpExemptionsRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
int Database::GetInstanceID(uint32 character_id, uint32 zone_id)
|
|
{
|
|
const auto& l = InstanceListRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`zone` = {} AND `id` IN (SELECT `id` FROM `instance_list_player` WHERE `charid` = {})",
|
|
zone_id,
|
|
character_id
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return 0;
|
|
}
|
|
|
|
auto& e = l.front();
|
|
|
|
return e.id;
|
|
}
|
|
|
|
bool Database::CopyCharacter(
|
|
const std::string& source_character_name,
|
|
const std::string& destination_character_name,
|
|
const std::string& destination_account_name
|
|
)
|
|
{
|
|
const auto& characters = CharacterDataRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}' AND `deleted_at` IS NULL LIMIT 1",
|
|
Strings::Escape(source_character_name)
|
|
)
|
|
);
|
|
|
|
if (characters.empty()) {
|
|
LogError("No character found with name [{}]", source_character_name);
|
|
return false;
|
|
}
|
|
|
|
auto& character = characters.front();
|
|
|
|
const uint32 source_character_id = character.id;
|
|
|
|
const auto& accounts = AccountRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}' LIMIT 1",
|
|
Strings::Escape(destination_account_name)
|
|
)
|
|
);
|
|
|
|
if (accounts.empty()) {
|
|
LogError("No account found with name [{}]", destination_account_name);
|
|
return false;
|
|
}
|
|
|
|
auto& account = accounts.front();
|
|
|
|
const int destination_account_id = account.id;
|
|
|
|
const int64 new_character_id = (CharacterDataRepository::GetMaxId(*this) + 1);
|
|
|
|
// validate destination name doesn't exist already
|
|
const auto& destination_characters = CharacterDataRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`name` = '{}' AND `deleted_at` IS NULL LIMIT 1",
|
|
Strings::Escape(destination_character_name)
|
|
)
|
|
);
|
|
if (!destination_characters.empty()) {
|
|
LogError("Character [{}] already exists", destination_character_name);
|
|
return false;
|
|
}
|
|
|
|
std::vector<std::string> tables_to_zero_id = {
|
|
"keyring",
|
|
"data_buckets",
|
|
"character_instance_safereturns",
|
|
"character_expedition_lockouts",
|
|
"character_instance_lockouts",
|
|
"character_parcels",
|
|
"character_tribute",
|
|
"player_titlesets",
|
|
};
|
|
|
|
std::vector<std::string> ignore_tables = {
|
|
"guilds",
|
|
};
|
|
|
|
size_t total_rows_copied = 0;
|
|
|
|
TransactionBegin();
|
|
|
|
for (const auto &t : DatabaseSchema::GetCharacterTables()) {
|
|
const std::string& table_name = t.first;
|
|
const std::string& character_id_column_name = t.second;
|
|
|
|
if (Strings::Contains(ignore_tables, table_name)) {
|
|
continue;
|
|
}
|
|
|
|
auto results = QueryDatabase(
|
|
fmt::format(
|
|
"SHOW COLUMNS FROM {}",
|
|
table_name
|
|
)
|
|
);
|
|
|
|
std::vector<std::string> columns = {};
|
|
int column_count = 0;
|
|
|
|
for (auto row : results) {
|
|
columns.emplace_back(row[0]);
|
|
column_count++;
|
|
}
|
|
|
|
results = QueryDatabase(
|
|
fmt::format(
|
|
"SELECT {} FROM {} WHERE {} = {}",
|
|
Strings::Implode(",", Strings::Wrap(columns, "`")),
|
|
table_name,
|
|
character_id_column_name,
|
|
source_character_id
|
|
)
|
|
);
|
|
|
|
std::vector<std::vector<std::string>> new_rows;
|
|
|
|
for (auto row : results) {
|
|
std::vector<std::string> new_values = {};
|
|
|
|
for (int column_index = 0; column_index < column_count; column_index++) {
|
|
const std::string& column = columns[column_index];
|
|
std::string value = row[column_index] ? row[column_index] : "null";
|
|
|
|
if (column == "id" && Strings::Contains(tables_to_zero_id, table_name)) {
|
|
value = "0";
|
|
}
|
|
|
|
if (column == character_id_column_name) {
|
|
value = std::to_string(new_character_id);
|
|
}
|
|
|
|
if (column == "name" && table_name == "character_data") {
|
|
value = destination_character_name;
|
|
}
|
|
|
|
if (column == "account_id" && table_name == "character_data") {
|
|
value = std::to_string(destination_account_id);
|
|
}
|
|
|
|
if (!Strings::IsNumber(value)) {
|
|
value = Strings::Escape(value);
|
|
}
|
|
|
|
new_values.emplace_back(value);
|
|
}
|
|
|
|
new_rows.emplace_back(new_values);
|
|
}
|
|
|
|
std::vector<std::string> insert_rows;
|
|
|
|
for (auto &r : new_rows) {
|
|
const std::string& insert_row = "(" + Strings::Implode(",", Strings::Wrap(r, "'")) + ")";
|
|
insert_rows.emplace_back(insert_row);
|
|
}
|
|
|
|
if (!insert_rows.empty()) {
|
|
QueryDatabase(
|
|
fmt::format(
|
|
"DELETE FROM {} WHERE {} = {}",
|
|
table_name,
|
|
character_id_column_name,
|
|
new_character_id
|
|
)
|
|
);
|
|
|
|
auto insert = QueryDatabase(
|
|
fmt::format(
|
|
"INSERT INTO {} ({}) VALUES {}",
|
|
table_name,
|
|
Strings::Implode(",", Strings::Wrap(columns, "`")),
|
|
Strings::Implode(",", insert_rows)
|
|
)
|
|
);
|
|
|
|
size_t rows_copied = insert_rows.size(); // Rows copied for this table
|
|
total_rows_copied += rows_copied; // Increment grand total
|
|
|
|
LogInfo("Copying table [{}] rows [{}]", table_name, Strings::Commify(rows_copied));
|
|
|
|
if (!insert.ErrorMessage().empty()) {
|
|
TransactionRollback();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
TransactionCommit();
|
|
|
|
LogInfo(
|
|
"Character [{}] copied to [{}] total rows [{}]",
|
|
source_character_name,
|
|
destination_character_name,
|
|
Strings::Commify(total_rows_copied)
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Database::SourceDatabaseTableFromUrl(const std::string& table_name, const std::string& url)
|
|
{
|
|
try {
|
|
uri request_uri(url);
|
|
|
|
LogHTTP(
|
|
"parsing url [{}] path [{}] host [{}] query_string [{}] protocol [{}] port [{}]",
|
|
url,
|
|
request_uri.get_path(),
|
|
request_uri.get_host(),
|
|
request_uri.get_query(),
|
|
request_uri.get_scheme(),
|
|
request_uri.get_port()
|
|
);
|
|
|
|
if (!DoesTableExist(table_name)) {
|
|
LogMySQLQuery("Table [{}] does not exist. Downloading and installing...", table_name);
|
|
|
|
// http get request
|
|
httplib::Client cli(
|
|
fmt::format(
|
|
"{}://{}",
|
|
request_uri.get_scheme(),
|
|
request_uri.get_host()
|
|
)
|
|
);
|
|
|
|
cli.set_connection_timeout(0, 60000000); // 60 sec
|
|
cli.set_read_timeout(60, 0); // 60 seconds
|
|
cli.set_write_timeout(60, 0); // 60 seconds
|
|
|
|
int sourced_queries = 0;
|
|
|
|
if (auto res = cli.Get(request_uri.get_path())) {
|
|
if (res->status == 200) {
|
|
for (auto& s : Strings::Split(res->body, ';')) {
|
|
if (!Strings::Trim(s).empty()) {
|
|
auto results = QueryDatabase(s);
|
|
if (!results.ErrorMessage().empty()) {
|
|
LogError("Error sourcing SQL [{}]", results.ErrorMessage());
|
|
return;
|
|
}
|
|
|
|
sourced_queries++;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
LogError("Error retrieving URL [{}]", url);
|
|
}
|
|
|
|
LogMySQLQuery(
|
|
"Table [{}] installed. Sourced [{}] queries",
|
|
table_name,
|
|
sourced_queries
|
|
);
|
|
}
|
|
} catch (std::invalid_argument iae) {
|
|
LogError("URI parser error [{}]", iae.what());
|
|
}
|
|
}
|
|
|
|
void Database::SourceSqlFromUrl(const std::string& url)
|
|
{
|
|
try {
|
|
uri request_uri(url);
|
|
|
|
LogHTTPDetail(
|
|
"parsing url [{}] path [{}] host [{}] query_string [{}] protocol [{}] port [{}]",
|
|
url,
|
|
request_uri.get_path(),
|
|
request_uri.get_host(),
|
|
request_uri.get_query(),
|
|
request_uri.get_scheme(),
|
|
request_uri.get_port()
|
|
);
|
|
|
|
LogInfo("Downloading and installing from [{}]", url);
|
|
|
|
// http get request
|
|
httplib::Client cli(
|
|
fmt::format(
|
|
"{}://{}",
|
|
request_uri.get_scheme(),
|
|
request_uri.get_host()
|
|
)
|
|
);
|
|
|
|
cli.set_connection_timeout(0, 60000000); // 60 sec
|
|
cli.set_read_timeout(60, 0); // 60 seconds
|
|
cli.set_write_timeout(60, 0); // 60 seconds
|
|
|
|
if (auto res = cli.Get(request_uri.get_path())) {
|
|
if (res->status == 200) {
|
|
auto results = QueryDatabaseMulti(res->body);
|
|
if (!results.ErrorMessage().empty()) {
|
|
LogError("Error sourcing SQL [{}]", results.ErrorMessage());
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (res->status == 404) {
|
|
LogError("Error retrieving URL [{}]", url);
|
|
}
|
|
} else {
|
|
LogError("Error retrieving URL [{}]", url);
|
|
}
|
|
} catch (std::invalid_argument iae) {
|
|
LogError("URI parser error [{}]", iae.what());
|
|
}
|
|
}
|
|
|
|
void Database::Encode(std::string &in)
|
|
{
|
|
for(int i = 0; i < in.length(); i++) {
|
|
in.at(i) += char('0');
|
|
}
|
|
};
|
|
|
|
void Database::Decode(std::string &in)
|
|
{
|
|
for(int i = 0; i < in.length(); i++) {
|
|
in.at(i) -= char('0');
|
|
}
|
|
};
|
|
|
|
void Database::PurgeCharacterParcels()
|
|
{
|
|
auto filter = fmt::format("sent_date < (NOW() - INTERVAL {} DAY)", RuleI(Parcel, ParcelPruneDelay));
|
|
auto results = CharacterParcelsRepository::GetWhere(*this, filter);
|
|
auto prune = CharacterParcelsRepository::DeleteWhere(*this, filter);
|
|
|
|
PlayerEvent::ParcelDelete pd{};
|
|
PlayerEventLogsRepository::PlayerEventLogs pel{};
|
|
pel.event_type_id = PlayerEvent::PARCEL_DELETE;
|
|
pel.event_type_name = PlayerEvent::EventName[pel.event_type_id];
|
|
std::stringstream ss;
|
|
for (auto const &r: results) {
|
|
pd.from_name = r.from_name;
|
|
pd.item_id = r.item_id;
|
|
pd.augment_1_id = r.aug_slot_1;
|
|
pd.augment_2_id = r.aug_slot_2;
|
|
pd.augment_3_id = r.aug_slot_3;
|
|
pd.augment_4_id = r.aug_slot_4;
|
|
pd.augment_5_id = r.aug_slot_5;
|
|
pd.augment_6_id = r.aug_slot_6;
|
|
pd.note = r.note;
|
|
pd.quantity = r.quantity;
|
|
pd.sent_date = r.sent_date;
|
|
pd.char_id = r.char_id;
|
|
{
|
|
cereal::JSONOutputArchiveSingleLine ar(ss);
|
|
pd.serialize(ar);
|
|
}
|
|
|
|
pel.event_data = ss.str();
|
|
pel.created_at = std::time(nullptr);
|
|
|
|
player_event_logs.AddToQueue(pel);
|
|
|
|
ss.str("");
|
|
ss.clear();
|
|
}
|
|
|
|
LogInfo(
|
|
"Purged <yellow>[{}] parcels that were over <yellow>[{}] days old.",
|
|
results.size(),
|
|
RuleI(Parcel, ParcelPruneDelay)
|
|
);
|
|
}
|
|
|
|
void Database::ClearGuildOnlineStatus()
|
|
{
|
|
GuildMembersRepository::ClearOnlineStatus(*this);
|
|
}
|
|
|
|
void Database::ClearTraderDetails()
|
|
{
|
|
TraderRepository::Truncate(*this);
|
|
}
|
|
|
|
void Database::ClearBuyerDetails()
|
|
{
|
|
BuyerRepository::DeleteBuyer(*this, 0);
|
|
}
|
|
|
|
uint64_t Database::GetNextTableId(const std::string &table_name)
|
|
{
|
|
auto results = QueryDatabase(fmt::format("SHOW TABLE STATUS LIKE '{}'", table_name));
|
|
|
|
for (auto row: results) {
|
|
for (int row_index = 0; row_index < results.ColumnCount(); row_index++) {
|
|
std::string field_name = Strings::ToLower(results.FieldName(row_index));
|
|
if (field_name == "auto_increment") {
|
|
std::string value = row[row_index] ? row[row_index] : "null";
|
|
return Strings::ToUnsignedBigInt(value, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|