gdle/Source/DatabaseIO.cpp
Morosity f1cffb38d4 Fix up client commands for starting/stopping events. Now offers more robust feedback if event string is not found.
Does not errantly declare event already active if not found or not active if event not found.
2020-03-10 01:07:58 +00:00

1219 lines
30 KiB
C++

#include <StdAfx.h>
#include "Database2.h"
#include "DatabaseIO.h"
#include "SHA512.h"
#include "Config.h"
uint64_t g_RandomAdminPassword = 0; // this is used for the admin login
CDatabaseIO::CDatabaseIO()
{
blob_query_buffer = new BYTE[BLOB_QUERY_BUFFER_LENGTH];
}
CDatabaseIO::~CDatabaseIO()
{
SafeDeleteArray (blob_query_buffer);
}
bool CDatabaseIO::VerifyAccount(const char *username, const char *password, AccountInformation_t *pAccountInfo, int *pError)
{
*pError = DBIO_ERROR_NONE;
std::string usernameEscaped = g_pDB2->EscapeString(username);
std::string passwordSalt;
if (g_pDB2->Query("SELECT password_salt FROM accounts WHERE username='%s' LIMIT 1", usernameEscaped.c_str()))
{
CSQLResult *pResult = g_pDB2->GetResult();
if (pResult)
{
SQLResultRow_t ResultRow = pResult->FetchRow();
if (ResultRow)
{
passwordSalt = ResultRow[0];
delete pResult;
}
else
{
delete pResult;
*pError = VERIFYACCOUNT_ERROR_DOESNT_EXIST;
return false;
}
}
else
{
*pError = VERIFYACCOUNT_ERROR_QUERY_FAILED;
return false;
}
}
else
{
*pError = VERIFYACCOUNT_ERROR_QUERY_FAILED;
return false;
}
bool bIsAdmin = false;
if (!stricmp(username, "admin"))
{
if (!strcmp(password, csprintf("%I64u", g_RandomAdminPassword)))
{
bIsAdmin = true;
//*pError = VERIFYACCOUNT_ERROR_BAD_LOGIN;
//return false;
}
//bIsAdmin = true;
}
std::string saltedPassword = std::string(password) + passwordSalt;
std::string hashedSaltedPassword = SHA512(saltedPassword.c_str(), saltedPassword.length()).substr(0, 64);
if (g_pDB2->Query("SELECT id, username, date_created, access, banned FROM accounts WHERE (username!='admin' AND username='%s' AND password='%s') OR (username='admin' AND %d) LIMIT 1", usernameEscaped.c_str(), hashedSaltedPassword.c_str(), bIsAdmin))
{
CSQLResult *pResult = g_pDB2->GetResult();
if (pResult)
{
SQLResultRow_t ResultRow = pResult->FetchRow();
if (ResultRow)
{
pAccountInfo->id = CSQLResult::SafeUInt(ResultRow[0]);
pAccountInfo->username = CSQLResult::SafeString(ResultRow[1]);
pAccountInfo->dateCreated = CSQLResult::SafeUInt(ResultRow[2]);
pAccountInfo->access = CSQLResult::SafeUInt(ResultRow[3]);
pAccountInfo->banned = CSQLResult::SafeInt(ResultRow[4]);
delete pResult;
}
else
{
delete pResult;
*pError = VERIFYACCOUNT_ERROR_BAD_LOGIN;
return false;
}
}
else
{
*pError = VERIFYACCOUNT_ERROR_QUERY_FAILED;
return false;
}
}
else
{
*pError = VERIFYACCOUNT_ERROR_QUERY_FAILED;
return false;
}
return true;
}
bool CDatabaseIO::CreateAccount(const char *username, const char *password, int *pError, const char *ipaddress)
{
unsigned int usernameLength = (unsigned int)strlen(username);
if (usernameLength < MIN_USERNAME_LENGTH || usernameLength > MAX_USERNAME_LENGTH)
{
*pError = CREATEACCOUNT_ERROR_BAD_USERNAME;
return false;
}
// Check if username is valid characters.
for (char &c : std::string(username))
{
if (c >= 'a' && c <= 'z')
continue;
if (c >= 'A' && c <= 'Z')
continue;
if (c >= '0' && c <= '9')
continue;
if (c == '_')
continue;
if (c == '-')
continue;
*pError = CREATEACCOUNT_ERROR_BAD_USERNAME;
return false;
}
unsigned int passwordLength = (unsigned int)strlen(password);
if (passwordLength < MIN_PASSWORD_LENGTH || passwordLength > MAX_PASSWORD_LENGTH)
{
*pError = CREATEACCOUNT_ERROR_BAD_PASSWORD;
return false;
}
*pError = DBIO_ERROR_NONE;
std::string usernameEscaped = g_pDB2->EscapeString(username);
std::string ipaddressEscaped = g_pDB2->EscapeString(ipaddress);
// Create random salt
uint32_t randomValue = Random::GenUInt(0, 100000);
std::string passwordSalt = SHA512(&randomValue, sizeof(randomValue)).substr(0, 16);
// Apply salt
std::string saltedPassword = password + passwordSalt;
std::string hashedSaltedPassword = SHA512(saltedPassword.c_str(), saltedPassword.length()).substr(0, 64);
uint64_t accountID = 0;
if (g_pDB2->Query("INSERT INTO accounts (username, password, password_salt, date_created, access, created_ip_address) VALUES ('%s', '%s', '%s', UNIX_TIMESTAMP(), 1, '%s')", usernameEscaped.c_str(), hashedSaltedPassword.c_str(), passwordSalt.c_str(), ipaddressEscaped.c_str()))
{
accountID = g_pDB2->GetInsertID();
if (!accountID)
{
*pError = CREATEACCOUNT_ERROR_QUERY_FAILED;
return false;
}
}
else
{
*pError = CREATEACCOUNT_ERROR_QUERY_FAILED;
return false;
}
return true;
}
bool CDatabaseIO::UpdateBan(unsigned int account_id, bool ban)
{
return g_pDB2->Query("UPDATE accounts SET banned = %u WHERE id = %u", ban, account_id);
}
std::list<HouseInfo_t> CDatabaseIO::GetHousingData()
{
std::list<HouseInfo_t> results;
if (g_pDB2->Query("SELECT house_id, data, LENGTH(data) FROM houses;"))
{
CSQLResult *pQueryResult = g_pDB2->GetResult();
if (pQueryResult)
{
while (SQLResultRow_t Row = pQueryResult->FetchRow())
{
HouseInfo_t res;
res.house_id = strtoul(Row[0], NULL, 10);
//res.houseBlob = Row[1];
res.houseBlobLen = strtoul(Row[2], NULL, 10);
res.houseBlob = new uint8_t[res.houseBlobLen];
memcpy(res.houseBlob, Row[1], res.houseBlobLen);
results.push_back(res);
}
delete pQueryResult;
}
}
return results;
}
std::list<unsigned int> CDatabaseIO::GetAllCharacterIDs()
{
std::list<unsigned int> results;
if (g_pDB2->Query("SELECT weenie_id FROM characters;"))
{
CSQLResult *pQueryResult = g_pDB2->GetResult();
if (pQueryResult)
{
while (SQLResultRow_t Row = pQueryResult->FetchRow())
{
results.push_back(strtoul(Row[0], NULL, 10));
}
delete pQueryResult;
}
}
return results;
}
std::string CDatabaseIO::GetEmailOfAccount(unsigned int character_id)
{
std::string result;
if (g_pDB2->Query("SELECT ac.email FROM accounts ac JOIN characters cc ON cc.account_id = ac.id WHERE cc.weenie_id = %u", character_id))
{
CSQLResult *pQueryResult = g_pDB2->GetResult();
if (pQueryResult)
{
while (SQLResultRow_t Row = pQueryResult->FetchRow())
{
result = Row[0];
}
delete pQueryResult;
}
}
return result;
}
bool CDatabaseIO::SetEmailOfAccount(std::string email, unsigned int character_id)
{
if (!g_pDB2->Query("UPDATE accounts AS ac JOIN characters as cc on cc.account_id = ac.id SET ac.email = '%s' WHERE cc.weenie_id = %u;", email.c_str(), character_id))
return false;
if (!g_pDB2->Query("UPDATE accounts AS ac JOIN characters as cc on cc.account_id = ac.id SET ac.emailsetused = 1 WHERE cc.weenie_id = %u;", character_id))
SERVER_ERROR << "Failed to set one time email flag to true for character:" << character_id << "using email" << email;
return true;
}
bool CDatabaseIO::IsEmailAlreadySet(unsigned int character_id)
{
bool result = false;
if (g_pDB2->Query("SELECT count(*) FROM accounts ac JOIN characters cc ON cc.account_id = ac.id WHERE ac.emailsetused = 1 AND cc.weenie_id = %u;", character_id))
{
CSQLResult *pQueryResult = g_pDB2->GetResult();
if (pQueryResult)
{
while (SQLResultRow_t Row = pQueryResult->FetchRow())
{
if (strtoul(Row[0], NULL, 10) > 0)
result = true;
}
delete pQueryResult;
}
}
return result;
}
std::list<unsigned int> CDatabaseIO::GetBannedAccountIDs()
{
std::list<unsigned int> results;
if (g_pDB2->Query("SELECT id FROM accounts WHERE banned = 1;"))
{
CSQLResult *pQueryResult = g_pDB2->GetResult();
if (pQueryResult)
{
while (SQLResultRow_t Row = pQueryResult->FetchRow())
{
results.push_back(strtoul(Row[0], NULL, 10));
}
delete pQueryResult;
}
}
return results;
}
std::list<unsigned int> CDatabaseIO::GetBannedCharacterIDs()
{
std::list<unsigned int> results;
if (g_pDB2->Query("SELECT weenie_id FROM characters;"))
{
CSQLResult *pQueryResult = g_pDB2->GetResult();
if (pQueryResult)
{
while (SQLResultRow_t Row = pQueryResult->FetchRow())
{
results.push_back(strtoul(Row[0], NULL, 10));
}
delete pQueryResult;
}
}
return results;
}
std::list<unsigned int> CDatabaseIO::GetWeeniesAt(unsigned int block_id)
{
std::list<unsigned int> results;
if (g_pDB2->Query("SELECT weenie_id FROM blocks WHERE block_id = %u;", block_id))
{
CSQLResult *pQueryResult = g_pDB2->GetResult();
if (pQueryResult)
{
while (SQLResultRow_t Row = pQueryResult->FetchRow())
{
results.push_back(strtoul(Row[0], NULL, 10));
}
delete pQueryResult;
}
}
return results;
}
std::string CDatabaseIO::LoadCharacterTitles(unsigned int character_id)
{
std::string result;
if (g_pDB2->Query("SELECT titles FROM character_titles WHERE character_id = %u;", character_id))
{
CSQLResult *pQueryResult = g_pDB2->GetResult();
if (pQueryResult)
{
while (SQLResultRow_t Row = pQueryResult->FetchRow())
{
result = Row[0];
};
delete pQueryResult;
}
}
return result;
}
std::string CDatabaseIO::LoadCharacterCorpses(unsigned int character_id)
{
std::string result;
if (g_pDB2->Query("SELECT corpses FROM character_corpses WHERE character_id = %u;", character_id))
{
CSQLResult *pQueryResult = g_pDB2->GetResult();
if (pQueryResult)
{
while (SQLResultRow_t Row = pQueryResult->FetchRow())
{
result = Row[0];
};
delete pQueryResult;
}
}
return result;
}
bool CDatabaseIO::SaveCharacterTitles(unsigned int character_id, std::string titles)
{
if (!g_pDB2->Query("REPLACE INTO character_titles (character_id, titles) VALUES (%u, '%s');", character_id, titles.c_str()))
return false;
return true;
}
bool CDatabaseIO::SaveCharacterCorpses(unsigned int character_id, std::string corpselist)
{
if (!g_pDB2->Query("REPLACE INTO character_corpses (character_id, corpses) VALUES (%u, '%s');", character_id, corpselist.c_str()))
return false;
return true;
}
bool CDatabaseIO::DeleteCharacterTitles(unsigned int character_id)
{
if (!g_pDB2->Query("DELETE FROM character_titles WHERE character_id = %u", character_id))
return false;
return true;
}
bool CDatabaseIO::DeleteCharacterInventory(unsigned int character_id)
{
if (!g_pDB2->Query("DELETE FROM weenies WHERE top_level_object_id = %u", character_id))
{
SERVER_ERROR << "CHAR DELETE: Failed to delete inventory for weenie - " << character_id;
return false;
}
return true;
}
bool CDatabaseIO::AddOrUpdateWeenieToBlock(unsigned int weenie_id, unsigned int block_id)
{
return g_pDB2->Query("INSERT INTO blocks (weenie_id, block_id) VALUES (%u, %u) ON DUPLICATE KEY UPDATE block_id = %u;", weenie_id, block_id, block_id);
}
bool CDatabaseIO::RemoveWeenieFromBlock(unsigned int weenie_id)
{
return g_pDB2->Query("DELETE FROM blocks WHERE weenie_id = %u;", weenie_id);
}
std::list<CharacterDesc_t> CDatabaseIO::GetCharacterList(unsigned int account_id)
{
std::list<CharacterDesc_t> results;
std::vector<unsigned int> charsToDelete;
mysql_statement<1> statement = g_pDB2->QueryEx(
"SELECT account_id, weenie_id, name, date_created, instance_ts, ts_deleted FROM characters WHERE account_id = ? ORDER BY ts_login DESC;",
account_id);
if (statement)
{
CharacterDesc_t desc = { 0, 0, "", 0, 0, 0};
desc.name.resize(64);
unsigned long length = 0;
mysql_statement_results<6> row;
row.bind(0, desc.account_id);
row.bind(1, desc.weenie_id);
row.bind(2, desc.name, desc.name.capacity(), &length);
row.bind(3, desc.date_created);
row.bind(4, desc.instance_ts);
row.bind(5, desc.ts_deleted);
if (statement.bindResults(row))
{
while (row.next())
{
int32_t ts_delete = 0;
if (desc.ts_deleted)
{
auto now = std::chrono::system_clock::now();
uint32_t ts = (uint32_t)std::chrono::system_clock::to_time_t(now);
ts_delete = g_pConfig->GetDeleteCharLifespan() - (ts - desc.ts_deleted);
if (ts_delete <= 0)
{
charsToDelete.push_back(desc.weenie_id);
continue;
}
desc.ts_deleted = ts_delete;
}
CharacterDesc_t entry = desc;
entry.name.resize(length);
results.push_back(entry);
}
}
}
for (auto it = charsToDelete.begin(); it != charsToDelete.end(); ++it) {
DeleteCharacter(*it, true);
}
return results;
}
std::list<CharacterSquelch_t> CDatabaseIO::GetCharacterSquelch(unsigned int character_id)
{
std::list<CharacterSquelch_t> results;
if (g_pDB2->Query("SELECT squelched_id, account_id, isip, isspeech, istell, iscombat, ismagic, isemote, isadvancement, isappraisal, isspellcasting, isallegiance, isfellowhip, iscombatenemy, isrecall, iscrafting FROM character_squelch WHERE character_id = %u", character_id))
{
CSQLResult *pQueryResult = g_pDB2->GetResult();
if (pQueryResult)
{
while (SQLResultRow_t Row = pQueryResult->FetchRow())
{
CharacterSquelch_t entry;
entry.squelched_id = strtoul(Row[0], NULL, 10);
entry.account_id = strtoul(Row[1], NULL, 10);
entry.isIp = Row[2];
entry.isSpeech = Row[3];
entry.isTell = Row[4];
entry.isCombat = Row[5];
entry.isMagic = Row[6];
entry.isEmote = Row[7];
entry.isAdvancement = Row[8];
entry.isAppraisal = Row[9];
entry.isSpellcasting = Row[10];
entry.isAllegiance = Row[11];
entry.isFellowship = Row[12];
entry.isCombatEnemy = Row[13];
entry.isRecall = Row[14];
entry.isCrafting = Row[15];
results.push_back(entry);
}
delete pQueryResult;
}
}
return results;
}
bool CDatabaseIO::SaveCharacterSquelch(unsigned int character_id, CharacterSquelch_t data)
{
return g_pDB2->Query("INSERT INTO character_squelch(character_id, squelched_id, account_id, isip, isspeech, istell, iscombat, ismagic, isemote, isadvancement, isappraisal, isspellcasting, isallegiance, isfellowhip, iscombatenemy, isrecall, iscrafting) VALUES"
"(%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u) ON DUPLICATE KEY UPDATE "
"isip = %u, isspeech = %u, istell = %u, iscombat = %u, ismagic = %u, isemote = %u, isadvancement = %u, isappraisal = %u, isspellcasting = %u, isallegiance = %u, isfellowhip = %u, iscombatenemy = %u, isrecall = %u, iscrafting = %u;",
character_id, data.squelched_id, data.account_id, data.isIp, data.isSpeech, data.isTell, data.isCombat, data.isMagic, data.isEmote, data.isAdvancement,
data.isAppraisal, data.isSpellcasting, data.isAllegiance, data.isFellowship, data.isCombatEnemy, data.isRecall, data.isCrafting,
data.isIp, data.isSpeech, data.isTell, data.isCombat, data.isMagic, data.isEmote, data.isAdvancement,
data.isAppraisal, data.isSpellcasting, data.isAllegiance, data.isFellowship, data.isCombatEnemy, data.isRecall, data.isCrafting);
}
bool CDatabaseIO::RemoveCharacterSquelch(unsigned int character_id, CharacterSquelch_t data)
{
return g_pDB2->Query("DELETE FROM character_squelch WHERE character_id = %u AND squelched_id = %u;", character_id, data.squelched_id);
}
uint32_t CDatabaseIO::GetPlayerAccountId(unsigned int character_id)
{
uint32_t accountToSquelch = 0;
if (g_pDB2->Query("SELECT account_id FROM characters WHERE weenie_id = %u;", character_id))
{
CSQLResult *pQueryResult = g_pDB2->GetResult();
if (pQueryResult)
{
SQLResultRow_t Row = pQueryResult->FetchRow();
if (Row)
{
accountToSquelch = strtoul(Row[0], NULL, 10);
}
delete pQueryResult;
}
}
return accountToSquelch;
}
CharacterDesc_t CDatabaseIO::GetCharacterInfo(unsigned int weenie_id)
{
CharacterDesc_t result = { 0,0,"",0,0, 0};
mysql_statement<1> statement = g_pDB2->QueryEx(
"SELECT account_id, weenie_id, name, date_created, instance_ts, ts_deleted FROM characters WHERE weenie_id = ?;",
weenie_id);
if (statement)
{
result.name.resize(64);
unsigned long length = 0;
mysql_statement_results<6> row;
row.bind(0, result.account_id);
row.bind(1, result.weenie_id);
row.bind(2, result.name, result.name.capacity(), &length);
row.bind(3, result.date_created);
row.bind(4, result.instance_ts);
row.bind(5, result.ts_deleted);
if (statement.bindResults(row))
{
if (row.next())
{
result.name.resize(length);
}
}
}
return result;
}
bool CDatabaseIO::CreateCharacter(unsigned int account_id, unsigned int weenie_id, const char *name)
{
return g_pDB2->Query("INSERT INTO characters (account_id, weenie_id, name, date_created, instance_ts) VALUES (%u, %u, '%s', UNIX_TIMESTAMP(), 1)",
account_id, weenie_id, g_pDB2->EscapeString(name).c_str());
}
bool CDatabaseIO::DeleteCharacter(unsigned int weenie_id, bool force)
{
if (force)
{
DeleteCharacterTitles(weenie_id);
DeleteCharacterInventory(weenie_id);
return g_pDB2->Query("DELETE FROM characters WHERE weenie_id = %u", weenie_id);
}
auto now = std::chrono::system_clock::now();
uint32_t ts = (uint32_t)std::chrono::system_clock::to_time_t(now);
if (!g_pDB2->Query("UPDATE characters SET ts_deleted=%u WHERE weenie_id=%u", ts, weenie_id))
{
SERVER_ERROR << "CHAR DELETE: Failed on Weenie ID -" << weenie_id;
return false;
}
return true;
}
bool CDatabaseIO::IsCharacterDeleting(unsigned int weenie_id)
{
mysql_statement<1> statement = g_pDB2->QueryEx(
"SELECT ts_deleted FROM characters WHERE ts_deleted > 0 AND weenie_id = ?",
weenie_id);
if (statement)
{
uint32_t result_id = 0;
mysql_statement_results<1> result;
result.bind(0, result_id);
if (statement.bindResults(result))
{
if (result.next())
{
return true;
}
}
}
return false;
}
bool CDatabaseIO::RestoreCharacter(unsigned int weenie_id)
{
if (!g_pDB2->Query("UPDATE characters SET ts_deleted = 0 WHERE weenie_id = %u;", weenie_id))
return false;
return true;
}
bool CDatabaseIO::SetCharacterInstanceTS(unsigned int weenie_id, unsigned int instance_ts)
{
auto now = std::chrono::system_clock::now();
uint32_t ts = (uint32_t)std::chrono::system_clock::to_time_t(now);
auto statement = g_pDB2->Query("UPDATE characters SET instance_ts=%u, ts_login=%u WHERE weenie_id=%u;", instance_ts, ts, weenie_id);
if (statement)
return true;
SERVER_ERROR << "CHAR LOGIN: Failed to set Instance or Login timestampes for Weenie ID -" << weenie_id;
return false;
}
bool CDatabaseIO::IDRangeTableExistsAndValid()
{
bool retval = false;
mysql_statement<0> statement = g_pDB2->QueryEx("SELECT unused FROM idranges WHERE unused > 2147999999 LIMIT 1");
if (statement)
{
unsigned int current = 0;
mysql_statement_results<1> result;
result.bind(0, current);
if (statement.bindResults(result))
{
if (result.next())
{
retval = true;
}
}
}
return retval;
}
std::list<unsigned int> CDatabaseIO::GetNextIDRange(unsigned int rangeStart, unsigned int count)
{
mysql_statement<2> statement = g_pDB2->QueryEx(
"SELECT unused FROM idranges WHERE unused > ? limit ?;",
rangeStart, count);
std::list< unsigned int> found;
if (statement)
{
unsigned int current = 0;
mysql_statement_results<1> result;
result.bind(0, current);
if (statement.bindResults(result))
{
while (result.next())
{
found.push_back(current);
}
}
}
return found;
}
unsigned int CDatabaseIO::GetHighestWeenieID(unsigned int min_range, unsigned int max_range)
{
uint32_t minId = min_range;
if (g_pDB2->Query("SELECT COALESCE(max(id), %u) FROM weenies WHERE id >= %u AND id < %u;", min_range, min_range, max_range))
{
CSQLResult *pQueryResult = g_pDB2->GetResult();
if (pQueryResult)
{
SQLResultRow_t Row = pQueryResult->FetchRow();
if (Row)
{
minId = strtoul(Row[0], NULL, 10);
}
delete pQueryResult;
}
}
return minId;
}
bool CDatabaseIO::IsCharacterNameOpen(const char *name)
{
std::string tmp(name);
mysql_statement<1> statement = g_pDB2->QueryEx(
"SELECT weenie_id FROM characters WHERE name = ?",
tmp);
if (statement)
{
unsigned int id = 0;
mysql_statement_results<1> result;
result.bind(0, id);
if (statement.bindResults(result))
{
if (result.next())
{
return false;
}
return true;
}
}
SERVER_ERROR << "Error on IsCharacterNameOpen:" << statement.error();
return false;
}
bool CDatabaseIO::IsPlayerCharacter(unsigned int weenie_id)
{
mysql_statement<1> statement = g_pDB2->QueryEx(
"SELECT weenie_id FROM characters WHERE weenie_id = ?",
weenie_id);
if (statement)
{
uint32_t result_id = 0;
mysql_statement_results<1> result;
result.bind(0, result_id);
if (statement.bindResults(result))
{
if (result.next())
{
return true;
}
}
}
return false;
}
uint32_t CDatabaseIO::GetPlayerCharacterId(const char *name)
{
uint32_t id = 0;
mysql_statement<1> statement = g_pDB2->QueryEx(
"SELECT weenie_id FROM characters WHERE name = ?",
name);
if (statement)
{
mysql_statement_results<1> result;
result.bind(0, id);
if (statement.bindResults(result))
{
result.next();
}
}
return (uint32_t)id;
}
std::string CDatabaseIO::GetPlayerCharacterName(uint32_t weenie_id)
{
std::string result;
mysql_statement<1> statement = g_pDB2->QueryEx(
"SELECT name FROM characters WHERE weenie_id = ?",
weenie_id);
if (statement)
{
unsigned long length = 0;
mysql_statement_results<1> results;
results.bind(0, result, 64, &length);
if (statement.bindResults(results))
{
if (results.next())
{
result.resize(length);
}
}
}
return result;
}
/*
bool CDatabaseIO::CreateOrUpdateWeenie(unsigned int weenie_id, unsigned int top_level_object_id, unsigned int block_id, void *data, unsigned int data_length)
{
// synchronous, deprecated
return CreateOrUpdateWeenie((MYSQL *)g_pDB2->GetInternalConnection(), weenie_id, top_level_object_id, block_id, data, data_length);
}
*/
bool CDatabaseIO::CreateOrUpdateWeenie(unsigned int weenie_id, unsigned int top_level_object_id, unsigned int block_id, void *data, unsigned int data_length)
{
IncrementPendingSave(weenie_id);
g_pDB2->QueueAsyncQuery(new CMYSQLSaveWeenieQuery(weenie_id, top_level_object_id, block_id, data, data_length));
return true;
}
bool CDatabaseIO::GetWeenie(unsigned int weenie_id, unsigned int *top_level_object_id, unsigned int *block_id, void **data, unsigned long *data_length)
{
// this is so bad...
while (GetNumPendingSaves(weenie_id))
std::this_thread::yield();
mysql_statement<1> statement = g_pDB2->QueryEx(
"SELECT top_level_object_id, block_id, data FROM weenies WHERE id = ?",
weenie_id);
if (statement)
{
mysql_statement_results<3> result;
result.bind(0, top_level_object_id);
result.bind(1, block_id);
result.bind(2, blob_query_buffer, BLOB_QUERY_BUFFER_LENGTH, data_length);
if (statement.bindResults(result))
{
if (result.next())
{
*data = blob_query_buffer;
return true;
}
}
}
SERVER_ERROR << "Error on GetWeenie:" << statement.error();
return false;
}
bool CDatabaseIO::DeleteWeenie(unsigned int weenie_id)
{
mysql_statement<2> statement = g_pDB2->QueryEx(
"DELETE FROM weenies WHERE id = ?;",
weenie_id, weenie_id);
if (statement)
{
return true;
}
SERVER_ERROR << "Error on DeleteWeenie: " << statement.error();
return false;
}
bool CDatabaseIO::IsWeenieInDatabase(unsigned int weenie_id)
{
mysql_statement<1> statement = g_pDB2->QueryEx(
"SELECT id FROM weenies WHERE id = ?",
weenie_id);
if (statement)
{
uint32_t result_id = 0;
mysql_statement_results<1> result;
result.bind(0, result_id);
if (statement.bindResults(result))
{
if (result.next())
{
return true;
}
}
}
return false;
}
bool CDatabaseIO::CreateOrUpdateGlobalData(DBIOGlobalDataID id, void *data, unsigned long data_length)
{
mysql_statement<2> statement = g_pDB2->CreateQuery<2>("REPLACE INTO globals (id, data) VALUES (?, ?)");
if (statement)
{
uint32_t gid = (uint32_t)id;
statement.bind(0, gid);
statement.bind(1, data, data_length);
if (statement.execute())
return true;
}
SERVER_ERROR << "Error on CreateOrUpdateGlobalData for" << id << ":" << statement.error();
return false;
}
bool CDatabaseIO::GetGlobalData(DBIOGlobalDataID id, void **data, unsigned long *data_length)
{
uint32_t gid = (uint32_t)id;
mysql_statement<1> statement = g_pDB2->QueryEx(
"SELECT data FROM globals WHERE id = ?",
gid);
if (statement)
{
mysql_statement_results<1> result;
result.bind(0, blob_query_buffer, BLOB_QUERY_BUFFER_LENGTH, data_length);
if (statement.bindResults(result))
{
if (result.next())
{
*data = blob_query_buffer;
return true;
}
}
}
SERVER_ERROR << "Error on GetGlobalData:" << statement.error();
return false;
}
bool CDatabaseIO::CreateOrUpdateHouseData(unsigned int house_id, void *data, unsigned int data_length)
{
IncrementPendingSave(house_id);
g_pDB2->QueueAsyncQuery(new CMYSQLSaveHouseQuery(house_id, data, data_length));
return true;
}
bool CDatabaseIO::GetHouseData(unsigned int house_id, void **data, unsigned long *data_length)
{
// this is so bad...
while (GetNumPendingSaves(house_id))
std::this_thread::yield();
mysql_statement<1> statement = g_pDB2->QueryEx(
"SELECT data FROM houses WHERE house_id = ?",
house_id);
if (statement)
{
mysql_statement_results<1> result;
result.bind(0, blob_query_buffer, BLOB_QUERY_BUFFER_LENGTH, data_length);
if (statement.bindResults(result))
{
if (result.next())
{
*data = blob_query_buffer;
return true;
}
}
}
SERVER_ERROR << "Error on GetHouseData:" << statement.error();
return false;
}
// CLockable _pendingSavesLock;
// std::unordered_map<uint32_t, uint32_t> _pendingSaves;
void CDatabaseIO::IncrementPendingSave(uint32_t weenie_id)
{
_pendingSavesLock.Lock();
std::unordered_map<uint32_t, uint32_t>::iterator i = _pendingSaves.find(weenie_id);
if (i != _pendingSaves.end())
{
i->second++;
}
else
{
_pendingSaves.insert(std::pair<uint32_t, uint32_t>(weenie_id, 1));
}
_pendingSavesLock.Unlock();
}
void CDatabaseIO::DecrementPendingSave(uint32_t weenie_id)
{
_pendingSavesLock.Lock();
std::unordered_map<uint32_t, uint32_t>::iterator i = _pendingSaves.find(weenie_id);
if (i != _pendingSaves.end())
{
if (i->second <= 1)
_pendingSaves.erase(i);
else
i->second--;
}
_pendingSavesLock.Unlock();
}
uint32_t CDatabaseIO::GetNumPendingSaves(uint32_t weenie_id)
{
_pendingSavesLock.Lock();
std::unordered_map<uint32_t, uint32_t>::iterator i = _pendingSaves.find(weenie_id);
uint32_t numSaves = 0;
if (i != _pendingSaves.end())
{
numSaves = i->second;
}
_pendingSavesLock.Unlock();
return numSaves;
}
bool CDatabaseIO::IsAlreadyFriend(unsigned int character_id, unsigned int friend_id)
{
mysql_statement<2> statement = g_pDB2->QueryEx(
"SELECT friend_id FROM character_friends WHERE character_id = ? AND friend_id = ? AND friend_type = 1;", character_id, friend_id);
if (statement)
{
unsigned int id = 0;
mysql_statement_results<1> result;
result.bind(0, id);
if (statement.bindResults(result))
{
if (result.next())
{
return true;
}
return false;
}
}
return false;
}
bool CDatabaseIO::AddCharacterFriend(unsigned int character_id, unsigned int friend_id)
{
if (!g_pDB2->Query("REPLACE INTO character_friends (character_id, friend_type, friend_id) VALUES (%u, 1, %u);", character_id, friend_id))
return false;
if (!g_pDB2->Query("REPLACE INTO character_friends (character_id, friend_type, friend_id) VALUES (%u, 2, %u);", friend_id, character_id))
return false;
return true;
}
bool CDatabaseIO::RemoveCharacterFriend(unsigned int character_id, unsigned int frenemy_id)
{
if (!g_pDB2->Query("DELETE FROM character_friends WHERE character_id = %u AND friend_id = %u;", character_id, frenemy_id))
return false;
if (!g_pDB2->Query("DELETE FROM character_friends WHERE friend_id = %u AND character_id = %u;", frenemy_id, character_id))
return false;
return true;
}
void CDatabaseIO::DeleteCharFriends(unsigned int character_id)
{
g_pDB2->Query("DELETE FROM character_friends WHERE character_id = %u;", character_id);
g_pDB2->Query("DELETE FROM character_friends WHERE friend_id = %u;", character_id);
}
bool CDatabaseIO::SaveWindowData(unsigned int character_id, BYTE* data, unsigned long data_length)
{
mysql_statement<3> statement = g_pDB2->CreateQuery<3>("REPLACE INTO character_windowdata (character_id, bloblength, windowblob) VALUES (?,?, ?)");
if (statement)
{
uint32_t gid = (uint32_t)character_id;
statement.bind(0, gid);
statement.bind(1, data_length);
statement.bind(2, data, data_length);
if (statement.execute())
return true;
}
SERVER_ERROR << "Error on saving window data for" << character_id << ":" << statement.error();
return false;
}
int CDatabaseIO::LoadWindowData(unsigned int character_id, BYTE ** data)
{
int retVal = 0;
unsigned long * data_length = 0;
uint32_t gid = (uint32_t)character_id;
mysql_statement<1> statement = g_pDB2->QueryEx(
"SELECT bloblength, windowblob FROM character_windowdata WHERE character_id = ?",
gid);
if (statement)
{
mysql_statement_results<2> result;
result.bind(0, retVal);
result.bind(1, blob_query_buffer, BLOB_QUERY_BUFFER_LENGTH, data_length);
if (statement.bindResults(result))
{
if (result.next())
{
*data = blob_query_buffer;
return retVal;
}
}
}
SERVER_ERROR << "Error on reading window data for" << character_id << ":" << statement.error();
return retVal;
}
void CDatabaseIO::ClearFriendsList(unsigned int character_id)
{
g_pDB2->Query("DELETE FROM character_friends WHERE character_id = %u;", character_id);
}
std::list<CharacterDesc_t> CDatabaseIO::GetFriends(unsigned int character_id)
{
std::list<CharacterDesc_t > results;
mysql_statement<1> statement = g_pDB2->QueryEx(
"SELECT 0, cf.friend_id, c.name, 0, 0 FROM characters c JOIN character_friends cf ON cf.friend_id = c.weenie_id WHERE cf.friend_type = 1 AND cf.character_id = ?",
character_id);
if (statement)
{
CharacterDesc_t desc = { 0, 0, "", 0, 0 };
desc.name.resize(64);
unsigned long length = 0;
mysql_statement_results<5> row;
row.bind(0, desc.account_id);
row.bind(1, desc.weenie_id);
row.bind(2, desc.name, desc.name.capacity(), &length);
row.bind(3, desc.date_created);
row.bind(4, desc.instance_ts);
if (statement.bindResults(row))
{
while (row.next())
{
CharacterDesc_t entry = desc;
entry.name.resize(length);
results.push_back(entry);
}
}
}
return results;
}
std::list<CharacterDesc_t> CDatabaseIO::GetFriendsOf(unsigned int character_id)
{
std::list<CharacterDesc_t > results;
mysql_statement<1> statement = g_pDB2->QueryEx(
"SELECT 0, cf.friend_id, c.name, 0, 0 FROM characters c JOIN character_friends cf ON cf.friend_id = c.weenie_id WHERE cf.friend_type = 2 AND cf.character_id = ?",
character_id);
if (statement)
{
CharacterDesc_t desc = { 0, 0, "", 0, 0 };
desc.name.resize(64);
unsigned long length = 0;
mysql_statement_results<5> row;
row.bind(0, desc.account_id);
row.bind(1, desc.weenie_id);
row.bind(2, desc.name, desc.name.capacity(), &length);
row.bind(3, desc.date_created);
row.bind(4, desc.instance_ts);
if (statement.bindResults(row))
{
while (row.next())
{
CharacterDesc_t entry = desc;
entry.name.resize(length);
results.push_back(entry);
}
}
}
return results;
}