diff --git a/src/Email.cpp b/src/Email.cpp index 4de3766..bdae822 100644 --- a/src/Email.cpp +++ b/src/Email.cpp @@ -276,7 +276,7 @@ static void emailSend(CNSocket* sock, CNPacketData* data) { 0 // DeleteTime (unimplemented) }; - if (!Database::sendEmail(&email, attachments)) { + if (!Database::sendEmail(&email, attachments, plr)) { plr->money += cost; // give money back // give items back while (!attachments.empty()) { diff --git a/src/Trading.cpp b/src/Trading.cpp index a46abfd..2a258f1 100644 --- a/src/Trading.cpp +++ b/src/Trading.cpp @@ -1,5 +1,6 @@ #include "Trading.hpp" #include "PlayerManager.hpp" +#include "db/Database.hpp" using namespace Trading; @@ -269,6 +270,8 @@ static void tradeConfirm(CNSocket* sock, CNPacketData* data) { otherSock->sendPacket(msg, P_FE2CL_GM_REP_PC_ANNOUNCE); return; } + + Database::commitTrade(plr, plr2); } static void tradeConfirmCancel(CNSocket* sock, CNPacketData* data) { diff --git a/src/db/Database.hpp b/src/db/Database.hpp index 240cd76..347cf27 100644 --- a/src/db/Database.hpp +++ b/src/db/Database.hpp @@ -78,7 +78,9 @@ namespace Database { // getting players void getPlayer(Player* plr, int id); + bool _updatePlayer(Player *player); void updatePlayer(Player *player); + void commitTrade(Player *plr1, Player *plr2); // buddies int getNumBuddies(Player* player); @@ -98,7 +100,7 @@ namespace Database { void deleteEmailAttachments(int playerID, int index, int slot); void deleteEmails(int playerID, int64_t* indices); int getNextEmailIndex(int playerID); - bool sendEmail(EmailData* data, std::vector attachments); + bool sendEmail(EmailData* data, std::vector attachments, Player *sender); // racing RaceRanking getTopRaceRanking(int epID, int playerID); diff --git a/src/db/email.cpp b/src/db/email.cpp index 74ced77..a4c3b69 100644 --- a/src/db/email.cpp +++ b/src/db/email.cpp @@ -152,7 +152,8 @@ void Database::updateEmailContent(EmailData* data) { sqlite3_step(stmt); int attachmentsCount = sqlite3_column_int(stmt, 0); - data->ItemFlag = (data->Taros > 0 || attachmentsCount > 0) ? 1 : 0; // set attachment flag dynamically + // set attachment flag dynamically + data->ItemFlag = (data->Taros > 0 || attachmentsCount > 0) ? 1 : 0; sqlite3_finalize(stmt); @@ -265,7 +266,7 @@ int Database::getNextEmailIndex(int playerID) { return (index > 0 ? index + 1 : 1); } -bool Database::sendEmail(EmailData* data, std::vector attachments) { +bool Database::sendEmail(EmailData* data, std::vector attachments, Player *sender) { std::lock_guard lock(dbCrit); sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL); @@ -330,6 +331,13 @@ bool Database::sendEmail(EmailData* data, std::vector attachments) { sqlite3_reset(stmt); } sqlite3_finalize(stmt); + + if (!_updatePlayer(sender)) { + std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl; + sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL); + return false; + } + sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL); return true; } diff --git a/src/db/player.cpp b/src/db/player.cpp index 77b4310..8396e1a 100644 --- a/src/db/player.cpp +++ b/src/db/player.cpp @@ -285,11 +285,13 @@ void Database::getPlayer(Player* plr, int id) { sqlite3_finalize(stmt); } -void Database::updatePlayer(Player *player) { - std::lock_guard lock(dbCrit); - - sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL); - +/* + * Low-level function to save a player to DB. + * Must be run in a SQL transaction and with dbCrit locked. + * The caller manages the transacstion, so if this function returns false, + * the caller must roll it back. + */ +bool Database::_updatePlayer(Player *player) { const char* sql = R"( UPDATE Players SET @@ -336,10 +338,8 @@ void Database::updatePlayer(Player *player) { sqlite3_bind_int(stmt, 21, player->iID); if (sqlite3_step(stmt) != SQLITE_DONE) { - std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl; sqlite3_finalize(stmt); - sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL); - return; + return false; } sqlite3_finalize(stmt); @@ -375,10 +375,8 @@ void Database::updatePlayer(Player *player) { rc = sqlite3_step(stmt); if (rc != SQLITE_DONE) { - std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl; - sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL); sqlite3_finalize(stmt); - return; + return false; } sqlite3_reset(stmt); } @@ -395,10 +393,8 @@ void Database::updatePlayer(Player *player) { sqlite3_bind_int(stmt, 6, player->Inven[i].iTimeLimit); if (sqlite3_step(stmt) != SQLITE_DONE) { - std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl; - sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL); sqlite3_finalize(stmt); - return; + return false; } sqlite3_reset(stmt); } @@ -415,10 +411,8 @@ void Database::updatePlayer(Player *player) { sqlite3_bind_int(stmt, 6, player->Bank[i].iTimeLimit); if (sqlite3_step(stmt) != SQLITE_DONE) { - std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl; - sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL); sqlite3_finalize(stmt); - return; + return false; } sqlite3_reset(stmt); } @@ -451,10 +445,8 @@ void Database::updatePlayer(Player *player) { sqlite3_bind_int(stmt, 4, player->QInven[i].iID); if (sqlite3_step(stmt) != SQLITE_DONE) { - std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl; - sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL); sqlite3_finalize(stmt); - return; + return false; } sqlite3_reset(stmt); } @@ -487,10 +479,8 @@ void Database::updatePlayer(Player *player) { sqlite3_bind_int(stmt, 4, player->Nanos[i].iStamina); if (sqlite3_step(stmt) != SQLITE_DONE) { - std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl; - sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL); sqlite3_finalize(stmt); - return; + return false; } sqlite3_reset(stmt); } @@ -524,14 +514,47 @@ void Database::updatePlayer(Player *player) { sqlite3_bind_int(stmt, 5, player->RemainingNPCCount[i][2]); if (sqlite3_step(stmt) != SQLITE_DONE) { - std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl; - sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL); sqlite3_finalize(stmt); - return; + return false; } sqlite3_reset(stmt); } - sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL); sqlite3_finalize(stmt); + + return true; +} + +void Database::updatePlayer(Player *player) { + std::lock_guard lock(dbCrit); + + sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL); + + if (!_updatePlayer(player)) { + std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl; + sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL); + return; + } + + sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL); +} + +void Database::commitTrade(Player *plr1, Player *plr2) { + std::lock_guard lock(dbCrit); + + sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL); + + if (!_updatePlayer(plr1)) { + std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl; + sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL); + return; + } + + if (!_updatePlayer(plr2)) { + std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl; + sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL); + return; + } + + sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL); }