561 lines
18 KiB
C++
561 lines
18 KiB
C++
#include "db/internal.hpp"
|
|
|
|
// Loading and saving players to/from the DB
|
|
|
|
static void removeExpiredVehicles(Player* player) {
|
|
int32_t currentTime = getTimestamp();
|
|
|
|
// if there are expired vehicles in bank just remove them silently
|
|
for (int i = 0; i < ABANK_COUNT; i++) {
|
|
if (player->Bank[i].iType == 10 && player->Bank[i].iTimeLimit < currentTime && player->Bank[i].iTimeLimit != 0) {
|
|
memset(&player->Bank[i], 0, sizeof(sItemBase));
|
|
}
|
|
}
|
|
|
|
// we want to leave only 1 expired vehicle on player to delete it with the client packet
|
|
std::vector<sItemBase*> toRemove;
|
|
|
|
// equipped vehicle
|
|
if (player->Equip[8].iOpt > 0 && player->Equip[8].iTimeLimit < currentTime && player->Equip[8].iTimeLimit != 0) {
|
|
toRemove.push_back(&player->Equip[8]);
|
|
player->toRemoveVehicle.eIL = 0;
|
|
player->toRemoveVehicle.iSlotNum = 8;
|
|
}
|
|
// inventory
|
|
for (int i = 0; i < AINVEN_COUNT; i++) {
|
|
if (player->Inven[i].iType == 10 && player->Inven[i].iTimeLimit < currentTime && player->Inven[i].iTimeLimit != 0) {
|
|
toRemove.push_back(&player->Inven[i]);
|
|
player->toRemoveVehicle.eIL = 1;
|
|
player->toRemoveVehicle.iSlotNum = i;
|
|
}
|
|
}
|
|
|
|
// delete all but one vehicles, leave last one for ceremonial deletion
|
|
for (int i = 0; i < (int)toRemove.size()-1; i++) {
|
|
memset(toRemove[i], 0, sizeof(sItemBase));
|
|
}
|
|
}
|
|
|
|
void Database::getPlayer(Player* plr, int id) {
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
const char* sql = R"(
|
|
SELECT
|
|
p.AccountID, p.Slot, p.FirstName, p.LastName,
|
|
p.Level, p.Nano1, p.Nano2, p.Nano3,
|
|
p.AppearanceFlag, p.TutorialFlag, p.PayZoneFlag,
|
|
p.XCoordinate, p.YCoordinate, p.ZCoordinate, p.NameCheck,
|
|
p.Angle, p.HP, acc.AccountLevel, p.FusionMatter, p.Taros, p.Quests,
|
|
p.BatteryW, p.BatteryN, p.Mentor, p.WarpLocationFlag,
|
|
p.SkywayLocationFlag, p.CurrentMissionID, p.FirstUseFlag,
|
|
a.Body, a.EyeColor, a.FaceStyle, a.Gender, a.HairColor, a.HairStyle, a.Height, a.SkinColor
|
|
FROM Players as p
|
|
INNER JOIN Appearances as a ON p.PlayerID = a.PlayerID
|
|
INNER JOIN Accounts as acc ON p.AccountID = acc.AccountID
|
|
WHERE p.PlayerID = ?;
|
|
)";
|
|
sqlite3_stmt* stmt;
|
|
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
sqlite3_bind_int(stmt, 1, id);
|
|
if (sqlite3_step(stmt) != SQLITE_ROW) {
|
|
sqlite3_finalize(stmt);
|
|
std::cout << "[WARN] Database: Failed to load character [" << id << "]: " << sqlite3_errmsg(db) << std::endl;
|
|
return;
|
|
}
|
|
|
|
plr->iID = id;
|
|
plr->PCStyle.iPC_UID = id;
|
|
|
|
plr->accountId = sqlite3_column_int(stmt, 0);
|
|
plr->slot = sqlite3_column_int(stmt, 1);
|
|
|
|
// parsing const unsigned char* to char16_t
|
|
std::string placeHolder = std::string(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2)));
|
|
U8toU16(placeHolder, plr->PCStyle.szFirstName, sizeof(plr->PCStyle.szFirstName));
|
|
placeHolder = std::string(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3)));
|
|
U8toU16(placeHolder, plr->PCStyle.szLastName, sizeof(plr->PCStyle.szLastName));
|
|
|
|
plr->level = sqlite3_column_int(stmt, 4);
|
|
plr->equippedNanos[0] = sqlite3_column_int(stmt, 5);
|
|
plr->equippedNanos[1] = sqlite3_column_int(stmt, 6);
|
|
plr->equippedNanos[2] = sqlite3_column_int(stmt, 7);
|
|
|
|
plr->PCStyle2.iAppearanceFlag = sqlite3_column_int(stmt, 8);
|
|
plr->PCStyle2.iTutorialFlag = sqlite3_column_int(stmt, 9);
|
|
plr->PCStyle2.iPayzoneFlag = sqlite3_column_int(stmt, 10);
|
|
|
|
plr->x = sqlite3_column_int(stmt, 11);
|
|
plr->y = sqlite3_column_int(stmt, 12);
|
|
plr->z = sqlite3_column_int(stmt, 13);
|
|
plr->PCStyle.iNameCheck = sqlite3_column_int(stmt, 14);
|
|
|
|
plr->angle = sqlite3_column_int(stmt, 15);
|
|
plr->HP = sqlite3_column_int(stmt, 16);
|
|
plr->accountLevel = sqlite3_column_int(stmt, 17);
|
|
plr->fusionmatter = sqlite3_column_int(stmt, 18);
|
|
plr->money = sqlite3_column_int(stmt, 19);
|
|
|
|
memcpy(plr->aQuestFlag, sqlite3_column_blob(stmt, 20), sizeof(plr->aQuestFlag));
|
|
|
|
plr->batteryW = sqlite3_column_int(stmt, 21);
|
|
plr->batteryN = sqlite3_column_int(stmt, 22);
|
|
plr->mentor = sqlite3_column_int(stmt, 23);
|
|
plr->iWarpLocationFlag = sqlite3_column_int(stmt, 24);
|
|
|
|
memcpy(plr->aSkywayLocationFlag, sqlite3_column_blob(stmt, 25), sizeof(plr->aSkywayLocationFlag));
|
|
|
|
plr->CurrentMissionID = sqlite3_column_int(stmt, 26);
|
|
|
|
memcpy(plr->iFirstUseFlag, sqlite3_column_blob(stmt, 27), sizeof(plr->iFirstUseFlag));
|
|
|
|
plr->PCStyle.iBody = sqlite3_column_int(stmt, 28);
|
|
plr->PCStyle.iEyeColor = sqlite3_column_int(stmt, 29);
|
|
plr->PCStyle.iFaceStyle = sqlite3_column_int(stmt, 30);
|
|
plr->PCStyle.iGender = sqlite3_column_int(stmt, 31);
|
|
plr->PCStyle.iHairColor = sqlite3_column_int(stmt, 32);
|
|
plr->PCStyle.iHairStyle = sqlite3_column_int(stmt, 33);
|
|
plr->PCStyle.iHeight = sqlite3_column_int(stmt, 34);
|
|
plr->PCStyle.iSkinColor = sqlite3_column_int(stmt, 35);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
// get inventory
|
|
sql = R"(
|
|
SELECT Slot, Type, ID, Opt, TimeLimit
|
|
FROM Inventory
|
|
WHERE PlayerID = ?;
|
|
)";
|
|
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
|
|
sqlite3_bind_int(stmt, 1, id);
|
|
|
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
|
int slot = sqlite3_column_int(stmt, 0);
|
|
|
|
// for extra safety
|
|
if (slot < 0 || slot > AEQUIP_COUNT + AINVEN_COUNT + ABANK_COUNT) {
|
|
std::cout << "[WARN] Database: Invalid item slot in db?! " << std::endl;
|
|
continue;
|
|
}
|
|
|
|
sItemBase* item;
|
|
if (slot < AEQUIP_COUNT) {
|
|
// equipment
|
|
item = &plr->Equip[slot];
|
|
} else if (slot < (AEQUIP_COUNT + AINVEN_COUNT)) {
|
|
// inventory
|
|
item = &plr->Inven[slot - AEQUIP_COUNT];
|
|
} else {
|
|
// bank
|
|
item = &plr->Bank[slot - AEQUIP_COUNT - AINVEN_COUNT];
|
|
}
|
|
|
|
item->iType = sqlite3_column_int(stmt, 1);
|
|
item->iID = sqlite3_column_int(stmt, 2);
|
|
item->iOpt = sqlite3_column_int(stmt, 3);
|
|
item->iTimeLimit = sqlite3_column_int(stmt, 4);
|
|
}
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
removeExpiredVehicles(plr);
|
|
|
|
// get quest inventory
|
|
sql = R"(
|
|
SELECT Slot, ID, Opt
|
|
FROM QuestItems
|
|
WHERE PlayerID = ?;
|
|
)";
|
|
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
|
|
sqlite3_bind_int(stmt, 1, id);
|
|
|
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
|
int slot = sqlite3_column_int(stmt, 0);
|
|
|
|
// for extra safety
|
|
if (slot < 0)
|
|
continue;
|
|
|
|
sItemBase* item = &plr->QInven[slot];
|
|
item->iType = 8;
|
|
item->iID = sqlite3_column_int(stmt, 1);
|
|
item->iOpt = sqlite3_column_int(stmt, 2);
|
|
}
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
// get nanos
|
|
sql = R"(
|
|
SELECT ID, Skill, Stamina
|
|
FROM Nanos
|
|
WHERE PlayerID = ?;
|
|
)";
|
|
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
sqlite3_bind_int(stmt, 1, id);
|
|
|
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
|
int id = sqlite3_column_int(stmt, 0);
|
|
|
|
// for extra safety
|
|
if (id < 0 || id > NANO_COUNT)
|
|
continue;
|
|
|
|
sNano* nano = &plr->Nanos[id];
|
|
nano->iID = id;
|
|
nano->iSkillID = sqlite3_column_int(stmt, 1);
|
|
nano->iStamina = sqlite3_column_int(stmt, 2);
|
|
}
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
// get active quests
|
|
sql = R"(
|
|
SELECT
|
|
TaskID,
|
|
RemainingNPCCount1,
|
|
RemainingNPCCount2,
|
|
RemainingNPCCount3
|
|
FROM RunningQuests
|
|
WHERE PlayerID = ?;
|
|
)";
|
|
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
sqlite3_bind_int(stmt, 1, id);
|
|
|
|
std::set<int> tasksSet; // used to prevent duplicate tasks from loading in
|
|
for (int i = 0; sqlite3_step(stmt) == SQLITE_ROW && i < ACTIVE_MISSION_COUNT; i++) {
|
|
|
|
int taskID = sqlite3_column_int(stmt, 0);
|
|
if (tasksSet.find(taskID) != tasksSet.end())
|
|
continue;
|
|
|
|
plr->tasks[i] = taskID;
|
|
tasksSet.insert(taskID);
|
|
plr->RemainingNPCCount[i][0] = sqlite3_column_int(stmt, 1);
|
|
plr->RemainingNPCCount[i][1] = sqlite3_column_int(stmt, 2);
|
|
plr->RemainingNPCCount[i][2] = sqlite3_column_int(stmt, 3);
|
|
}
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
// get buddies
|
|
sql = R"(
|
|
SELECT PlayerAID, PlayerBID
|
|
FROM Buddyships
|
|
WHERE PlayerAID = ? OR PlayerBID = ?;
|
|
)";
|
|
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
sqlite3_bind_int(stmt, 1, id);
|
|
sqlite3_bind_int(stmt, 2, id);
|
|
|
|
int i = 0;
|
|
while (sqlite3_step(stmt) == SQLITE_ROW && i < 50) {
|
|
int PlayerAId = sqlite3_column_int(stmt, 0);
|
|
int PlayerBId = sqlite3_column_int(stmt, 1);
|
|
|
|
plr->buddyIDs[i] = id == PlayerAId ? PlayerBId : PlayerAId;
|
|
plr->isBuddyBlocked[i] = false;
|
|
i++;
|
|
}
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
// get blocked players
|
|
sql = R"(
|
|
SELECT BlockedPlayerID FROM Blocks
|
|
WHERE PlayerID = ?;
|
|
)";
|
|
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
sqlite3_bind_int(stmt, 1, id);
|
|
|
|
// i retains its value from after the loop over Buddyships
|
|
while (sqlite3_step(stmt) == SQLITE_ROW && i < 50) {
|
|
plr->buddyIDs[i] = sqlite3_column_int(stmt, 0);
|
|
plr->isBuddyBlocked[i] = true;
|
|
i++;
|
|
}
|
|
|
|
sqlite3_finalize(stmt);
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
Level = ? , Nano1 = ?, Nano2 = ?, Nano3 = ?,
|
|
XCoordinate = ?, YCoordinate = ?, ZCoordinate = ?,
|
|
Angle = ?, HP = ?, FusionMatter = ?, Taros = ?, Quests = ?,
|
|
BatteryW = ?, BatteryN = ?, WarplocationFlag = ?,
|
|
SkywayLocationFlag = ?, CurrentMissionID = ?,
|
|
PayZoneFlag = ?, FirstUseFlag = ?, Mentor = ?
|
|
WHERE PlayerID = ?;
|
|
)";
|
|
sqlite3_stmt* stmt;
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
sqlite3_bind_int(stmt, 1, player->level);
|
|
sqlite3_bind_int(stmt, 2, player->equippedNanos[0]);
|
|
sqlite3_bind_int(stmt, 3, player->equippedNanos[1]);
|
|
sqlite3_bind_int(stmt, 4, player->equippedNanos[2]);
|
|
|
|
if (player->instanceID == 0 && !player->onMonkey) {
|
|
sqlite3_bind_int(stmt, 5, player->x);
|
|
sqlite3_bind_int(stmt, 6, player->y);
|
|
sqlite3_bind_int(stmt, 7, player->z);
|
|
sqlite3_bind_int(stmt, 8, player->angle);
|
|
}
|
|
else {
|
|
sqlite3_bind_int(stmt, 5, player->lastX);
|
|
sqlite3_bind_int(stmt, 6, player->lastY);
|
|
sqlite3_bind_int(stmt, 7, player->lastZ);
|
|
sqlite3_bind_int(stmt, 8, player->lastAngle);
|
|
}
|
|
|
|
sqlite3_bind_int(stmt, 9, player->HP);
|
|
sqlite3_bind_int(stmt, 10, player->fusionmatter);
|
|
sqlite3_bind_int(stmt, 11, player->money);
|
|
sqlite3_bind_blob(stmt, 12, player->aQuestFlag, sizeof(player->aQuestFlag), NULL);
|
|
sqlite3_bind_int(stmt, 13, player->batteryW);
|
|
sqlite3_bind_int(stmt, 14, player->batteryN);
|
|
sqlite3_bind_int(stmt, 15, player->iWarpLocationFlag);
|
|
sqlite3_bind_blob(stmt, 16, player->aSkywayLocationFlag, sizeof(player->aSkywayLocationFlag), NULL);
|
|
sqlite3_bind_int(stmt, 17, player->CurrentMissionID);
|
|
sqlite3_bind_int(stmt, 18, player->PCStyle2.iPayzoneFlag);
|
|
sqlite3_bind_blob(stmt, 19, player->iFirstUseFlag, sizeof(player->iFirstUseFlag), NULL);
|
|
sqlite3_bind_int(stmt, 20, player->mentor);
|
|
sqlite3_bind_int(stmt, 21, player->iID);
|
|
|
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
|
sqlite3_finalize(stmt);
|
|
return false;
|
|
}
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
// update inventory
|
|
sql = R"(
|
|
DELETE FROM Inventory WHERE PlayerID = ?;
|
|
)";
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
sqlite3_bind_int(stmt, 1, player->iID);
|
|
int rc = sqlite3_step(stmt);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
sql = R"(
|
|
INSERT INTO Inventory
|
|
(PlayerID, Slot, Type, Opt, ID, Timelimit)
|
|
VALUES (?, ?, ?, ?, ?, ?);
|
|
)";
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
|
|
for (int i = 0; i < AEQUIP_COUNT; i++) {
|
|
if (player->Equip[i].iID == 0)
|
|
continue;
|
|
|
|
sqlite3_bind_int(stmt, 1, player->iID);
|
|
sqlite3_bind_int(stmt, 2, i);
|
|
sqlite3_bind_int(stmt, 3, player->Equip[i].iType);
|
|
sqlite3_bind_int(stmt, 4, player->Equip[i].iOpt);
|
|
sqlite3_bind_int(stmt, 5, player->Equip[i].iID);
|
|
sqlite3_bind_int(stmt, 6, player->Equip[i].iTimeLimit);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
|
|
if (rc != SQLITE_DONE) {
|
|
sqlite3_finalize(stmt);
|
|
return false;
|
|
}
|
|
sqlite3_reset(stmt);
|
|
}
|
|
|
|
for (int i = 0; i < AINVEN_COUNT; i++) {
|
|
if (player->Inven[i].iID == 0)
|
|
continue;
|
|
|
|
sqlite3_bind_int(stmt, 1, player->iID);
|
|
sqlite3_bind_int(stmt, 2, i + AEQUIP_COUNT);
|
|
sqlite3_bind_int(stmt, 3, player->Inven[i].iType);
|
|
sqlite3_bind_int(stmt, 4, player->Inven[i].iOpt);
|
|
sqlite3_bind_int(stmt, 5, player->Inven[i].iID);
|
|
sqlite3_bind_int(stmt, 6, player->Inven[i].iTimeLimit);
|
|
|
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
|
sqlite3_finalize(stmt);
|
|
return false;
|
|
}
|
|
sqlite3_reset(stmt);
|
|
}
|
|
|
|
for (int i = 0; i < ABANK_COUNT; i++) {
|
|
if (player->Bank[i].iID == 0)
|
|
continue;
|
|
|
|
sqlite3_bind_int(stmt, 1, player->iID);
|
|
sqlite3_bind_int(stmt, 2, i + AEQUIP_COUNT + AINVEN_COUNT);
|
|
sqlite3_bind_int(stmt, 3, player->Bank[i].iType);
|
|
sqlite3_bind_int(stmt, 4, player->Bank[i].iOpt);
|
|
sqlite3_bind_int(stmt, 5, player->Bank[i].iID);
|
|
sqlite3_bind_int(stmt, 6, player->Bank[i].iTimeLimit);
|
|
|
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
|
sqlite3_finalize(stmt);
|
|
return false;
|
|
}
|
|
sqlite3_reset(stmt);
|
|
}
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
// Update Quest Inventory
|
|
sql = R"(
|
|
DELETE FROM QuestItems WHERE PlayerID = ?;
|
|
)";
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
sqlite3_bind_int(stmt, 1, player->iID);
|
|
sqlite3_step(stmt);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
sql = R"(
|
|
INSERT INTO QuestItems (PlayerID, Slot, Opt, ID)
|
|
VALUES (?, ?, ?, ?);
|
|
)";
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
|
|
for (int i = 0; i < AQINVEN_COUNT; i++) {
|
|
if (player->QInven[i].iID == 0)
|
|
continue;
|
|
|
|
sqlite3_bind_int(stmt, 1, player->iID);
|
|
sqlite3_bind_int(stmt, 2, i);
|
|
sqlite3_bind_int(stmt, 3, player->QInven[i].iOpt);
|
|
sqlite3_bind_int(stmt, 4, player->QInven[i].iID);
|
|
|
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
|
sqlite3_finalize(stmt);
|
|
return false;
|
|
}
|
|
sqlite3_reset(stmt);
|
|
}
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
// Update Nanos
|
|
sql = R"(
|
|
DELETE FROM Nanos WHERE PlayerID = ?;
|
|
)";
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
sqlite3_bind_int(stmt, 1, player->iID);
|
|
sqlite3_step(stmt);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
sql = R"(
|
|
INSERT INTO Nanos (PlayerID, ID, SKill, Stamina)
|
|
VALUES (?, ?, ?, ?);
|
|
)";
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
|
|
for (int i = 0; i < NANO_COUNT; i++) {
|
|
if (player->Nanos[i].iID == 0)
|
|
continue;
|
|
|
|
sqlite3_bind_int(stmt, 1, player->iID);
|
|
sqlite3_bind_int(stmt, 2, player->Nanos[i].iID);
|
|
sqlite3_bind_int(stmt, 3, player->Nanos[i].iSkillID);
|
|
sqlite3_bind_int(stmt, 4, player->Nanos[i].iStamina);
|
|
|
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
|
sqlite3_finalize(stmt);
|
|
return false;
|
|
}
|
|
sqlite3_reset(stmt);
|
|
}
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
// Update Running Quests
|
|
sql = R"(
|
|
DELETE FROM RunningQuests WHERE PlayerID = ?;
|
|
)";
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
sqlite3_bind_int(stmt, 1, player->iID);
|
|
sqlite3_step(stmt);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
sql = R"(
|
|
INSERT INTO RunningQuests
|
|
(PlayerID, TaskID, RemainingNPCCount1, RemainingNPCCount2, RemainingNPCCount3)
|
|
VALUES (?, ?, ?, ?, ?);
|
|
)";
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
|
|
for (int i = 0; i < ACTIVE_MISSION_COUNT; i++) {
|
|
if (player->tasks[i] == 0)
|
|
continue;
|
|
sqlite3_bind_int(stmt, 1, player->iID);
|
|
sqlite3_bind_int(stmt, 2, player->tasks[i]);
|
|
sqlite3_bind_int(stmt, 3, player->RemainingNPCCount[i][0]);
|
|
sqlite3_bind_int(stmt, 4, player->RemainingNPCCount[i][1]);
|
|
sqlite3_bind_int(stmt, 5, player->RemainingNPCCount[i][2]);
|
|
|
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
|
sqlite3_finalize(stmt);
|
|
return false;
|
|
}
|
|
sqlite3_reset(stmt);
|
|
}
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Database::updatePlayer(Player *player) {
|
|
std::lock_guard<std::mutex> 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<std::mutex> 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);
|
|
}
|