Compare commits

..

No commits in common. "100b4605ecbac660373587342b8880c100a436d7" and "eb8e54c1f0ef3165c49a7b864e49b9c4c48a8823" have entirely different histories.

10 changed files with 97 additions and 152 deletions

View File

@ -93,7 +93,7 @@ static void emailReceiveItemSingle(CNSocket* sock, CNPacketData* data) {
auto pkt = (sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM*)data->buf; auto pkt = (sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM*)data->buf;
Player* plr = PlayerManager::getPlayer(sock); Player* plr = PlayerManager::getPlayer(sock);
if (pkt->iSlotNum < 0 || pkt->iSlotNum >= AINVEN_COUNT || pkt->iEmailItemSlot < 1 || pkt->iEmailItemSlot > 4) if (pkt->iSlotNum < 0 || pkt->iSlotNum >= AINVEN_COUNT || pkt->iSlotNum < 1 || pkt->iSlotNum > 4)
return; // sanity check return; // sanity check
// get email item from db and delete it // get email item from db and delete it
@ -276,7 +276,7 @@ static void emailSend(CNSocket* sock, CNPacketData* data) {
0 // DeleteTime (unimplemented) 0 // DeleteTime (unimplemented)
}; };
if (!Database::sendEmail(&email, attachments, plr)) { if (!Database::sendEmail(&email, attachments)) {
plr->money += cost; // give money back plr->money += cost; // give money back
// give items back // give items back
while (!attachments.empty()) { while (!attachments.empty()) {

View File

@ -28,12 +28,17 @@ using namespace PlayerManager;
std::map<CNSocket*, Player*> PlayerManager::players; std::map<CNSocket*, Player*> PlayerManager::players;
static void addPlayer(CNSocket* key, Player *plr) { static void addPlayer(CNSocket* key, Player& plr) {
players[key] = plr; Player *p = new Player();
plr->chunkPos = Chunking::INVALID_CHUNK;
plr->lastHeartbeat = 0;
std::cout << getPlayerName(plr) << " has joined!" << std::endl; // copy object into heap memory
*p = plr;
players[key] = p;
p->chunkPos = Chunking::INVALID_CHUNK;
p->lastHeartbeat = 0;
std::cout << getPlayerName(p) << " has joined!" << std::endl;
std::cout << players.size() << " players" << std::endl; std::cout << players.size() << " players" << std::endl;
} }
@ -219,73 +224,69 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
return; return;
} }
Player *plr = new Player(); // for convenience
Database::getPlayer(plr, lm->playerId); Player& plr = lm->plr;
plr.groupCnt = 1;
plr.iIDGroup = plr.groupIDs[0] = plr.iID;
// check if account is already in use // check if account is already in use
if (isAccountInUse(plr->accountId)) { if (isAccountInUse(plr.accountId)) {
// kick the other player // kick the other player
exitDuplicate(plr->accountId); exitDuplicate(plr.accountId);
// re-read the player from disk, in case it was just flushed
*plr = {};
Database::getPlayer(plr, lm->playerId);
} }
plr->groupCnt = 1; response.iID = plr.iID;
plr->iIDGroup = plr->groupIDs[0] = plr->iID;
response.iID = plr->iID;
response.uiSvrTime = getTime(); response.uiSvrTime = getTime();
response.PCLoadData2CL.iUserLevel = plr->accountLevel; response.PCLoadData2CL.iUserLevel = plr.accountLevel;
response.PCLoadData2CL.iHP = plr->HP; response.PCLoadData2CL.iHP = plr.HP;
response.PCLoadData2CL.iLevel = plr->level; response.PCLoadData2CL.iLevel = plr.level;
response.PCLoadData2CL.iCandy = plr->money; response.PCLoadData2CL.iCandy = plr.money;
response.PCLoadData2CL.iFusionMatter = plr->fusionmatter; response.PCLoadData2CL.iFusionMatter = plr.fusionmatter;
response.PCLoadData2CL.iMentor = plr->mentor; response.PCLoadData2CL.iMentor = plr.mentor;
response.PCLoadData2CL.iMentorCount = 1; // how many guides the player has had response.PCLoadData2CL.iMentorCount = 1; // how many guides the player has had
response.PCLoadData2CL.iX = plr->x; response.PCLoadData2CL.iX = plr.x;
response.PCLoadData2CL.iY = plr->y; response.PCLoadData2CL.iY = plr.y;
response.PCLoadData2CL.iZ = plr->z; response.PCLoadData2CL.iZ = plr.z;
response.PCLoadData2CL.iAngle = plr->angle; response.PCLoadData2CL.iAngle = plr.angle;
response.PCLoadData2CL.iBatteryN = plr->batteryN; response.PCLoadData2CL.iBatteryN = plr.batteryN;
response.PCLoadData2CL.iBatteryW = plr->batteryW; response.PCLoadData2CL.iBatteryW = plr.batteryW;
response.PCLoadData2CL.iBuddyWarpTime = 60; // sets 60s warp cooldown on login response.PCLoadData2CL.iBuddyWarpTime = 60; // sets 60s warp cooldown on login
response.PCLoadData2CL.iWarpLocationFlag = plr->iWarpLocationFlag; response.PCLoadData2CL.iWarpLocationFlag = plr.iWarpLocationFlag;
response.PCLoadData2CL.aWyvernLocationFlag[0] = plr->aSkywayLocationFlag[0]; response.PCLoadData2CL.aWyvernLocationFlag[0] = plr.aSkywayLocationFlag[0];
response.PCLoadData2CL.aWyvernLocationFlag[1] = plr->aSkywayLocationFlag[1]; response.PCLoadData2CL.aWyvernLocationFlag[1] = plr.aSkywayLocationFlag[1];
response.PCLoadData2CL.iActiveNanoSlotNum = -1; response.PCLoadData2CL.iActiveNanoSlotNum = -1;
response.PCLoadData2CL.iFatigue = 50; response.PCLoadData2CL.iFatigue = 50;
response.PCLoadData2CL.PCStyle = plr->PCStyle; response.PCLoadData2CL.PCStyle = plr.PCStyle;
// client doesnt read this, it gets it from charinfo // client doesnt read this, it gets it from charinfo
// response.PCLoadData2CL.PCStyle2 = plr->PCStyle2; // response.PCLoadData2CL.PCStyle2 = plr.PCStyle2;
// inventory // inventory
for (int i = 0; i < AEQUIP_COUNT; i++) for (int i = 0; i < AEQUIP_COUNT; i++)
response.PCLoadData2CL.aEquip[i] = plr->Equip[i]; response.PCLoadData2CL.aEquip[i] = plr.Equip[i];
for (int i = 0; i < AINVEN_COUNT; i++) for (int i = 0; i < AINVEN_COUNT; i++)
response.PCLoadData2CL.aInven[i] = plr->Inven[i]; response.PCLoadData2CL.aInven[i] = plr.Inven[i];
// quest inventory // quest inventory
for (int i = 0; i < AQINVEN_COUNT; i++) for (int i = 0; i < AQINVEN_COUNT; i++)
response.PCLoadData2CL.aQInven[i] = plr->QInven[i]; response.PCLoadData2CL.aQInven[i] = plr.QInven[i];
// nanos // nanos
for (int i = 1; i < SIZEOF_NANO_BANK_SLOT; i++) { for (int i = 1; i < SIZEOF_NANO_BANK_SLOT; i++) {
response.PCLoadData2CL.aNanoBank[i] = plr->Nanos[i]; response.PCLoadData2CL.aNanoBank[i] = plr.Nanos[i];
} }
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
response.PCLoadData2CL.aNanoSlots[i] = plr->equippedNanos[i]; response.PCLoadData2CL.aNanoSlots[i] = plr.equippedNanos[i];
} }
// missions in progress // missions in progress
for (int i = 0; i < ACTIVE_MISSION_COUNT; i++) { for (int i = 0; i < ACTIVE_MISSION_COUNT; i++) {
if (plr->tasks[i] == 0) if (plr.tasks[i] == 0)
break; break;
response.PCLoadData2CL.aRunningQuest[i].m_aCurrTaskID = plr->tasks[i]; response.PCLoadData2CL.aRunningQuest[i].m_aCurrTaskID = plr.tasks[i];
TaskData &task = *Missions::Tasks[plr->tasks[i]]; TaskData &task = *Missions::Tasks[plr.tasks[i]];
for (int j = 0; j < 3; j++) { for (int j = 0; j < 3; j++) {
response.PCLoadData2CL.aRunningQuest[i].m_aKillNPCID[j] = (int)task["m_iCSUEnemyID"][j]; response.PCLoadData2CL.aRunningQuest[i].m_aKillNPCID[j] = (int)task["m_iCSUEnemyID"][j];
response.PCLoadData2CL.aRunningQuest[i].m_aKillNPCCount[j] = plr->RemainingNPCCount[i][j]; response.PCLoadData2CL.aRunningQuest[i].m_aKillNPCCount[j] = plr.RemainingNPCCount[i][j];
/* /*
* client doesn't care about NeededItem ID and Count, * client doesn't care about NeededItem ID and Count,
* it gets Count from Quest Inventory * it gets Count from Quest Inventory
@ -295,12 +296,12 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
*/ */
} }
} }
response.PCLoadData2CL.iCurrentMissionID = plr->CurrentMissionID; response.PCLoadData2CL.iCurrentMissionID = plr.CurrentMissionID;
// completed missions // completed missions
// the packet requires 32 items, but the client only checks the first 16 (shrug) // the packet requires 32 items, but the client only checks the first 16 (shrug)
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
response.PCLoadData2CL.aQuestFlag[i] = plr->aQuestFlag[i]; response.PCLoadData2CL.aQuestFlag[i] = plr.aQuestFlag[i];
} }
// Computress tips // Computress tips
@ -309,11 +310,11 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
response.PCLoadData2CL.iFirstUseFlag2 = UINT64_MAX; response.PCLoadData2CL.iFirstUseFlag2 = UINT64_MAX;
} }
else { else {
response.PCLoadData2CL.iFirstUseFlag1 = plr->iFirstUseFlag[0]; response.PCLoadData2CL.iFirstUseFlag1 = plr.iFirstUseFlag[0];
response.PCLoadData2CL.iFirstUseFlag2 = plr->iFirstUseFlag[1]; response.PCLoadData2CL.iFirstUseFlag2 = plr.iFirstUseFlag[1];
} }
plr->instanceID = INSTANCE_OVERWORLD; // the player should never be in an instance on enter plr.instanceID = INSTANCE_OVERWORLD; // the player should never be in an instance on enter
sock->setEKey(CNSocketEncryption::createNewKey(response.uiSvrTime, response.iID + 1, response.PCLoadData2CL.iFusionMatter + 1)); sock->setEKey(CNSocketEncryption::createNewKey(response.uiSvrTime, response.iID + 1, response.PCLoadData2CL.iFusionMatter + 1));
sock->setFEKey(lm->FEKey); sock->setFEKey(lm->FEKey);
@ -324,14 +325,14 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
// transmit MOTD after entering the game, so the client hopefully changes modes on time // transmit MOTD after entering the game, so the client hopefully changes modes on time
Chat::sendServerMessage(sock, settings::MOTDSTRING); Chat::sendServerMessage(sock, settings::MOTDSTRING);
// transfer ownership of Player object into the shard (still valid in this function though) // copy Player object into the shard
addPlayer(sock, plr); addPlayer(sock, plr);
// check if there is an expiring vehicle // check if there is an expiring vehicle
Items::checkItemExpire(sock, plr); Items::checkItemExpire(sock, getPlayer(sock));
// set player equip stats // set player equip stats
Items::setItemStats(plr); Items::setItemStats(getPlayer(sock));
Missions::failInstancedMissions(sock); Missions::failInstancedMissions(sock);
@ -342,9 +343,9 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
for (auto& pair : players) for (auto& pair : players)
if (pair.second->notify) if (pair.second->notify)
Chat::sendServerMessage(pair.first, "[ADMIN]" + getPlayerName(plr) + " has joined."); Chat::sendServerMessage(pair.first, "[ADMIN]" + getPlayerName(&plr) + " has joined.");
// deallocate lm // deallocate lm (and therefore the plr object)
delete lm; delete lm;
} }

View File

@ -1,6 +1,5 @@
#include "Trading.hpp" #include "Trading.hpp"
#include "PlayerManager.hpp" #include "PlayerManager.hpp"
#include "db/Database.hpp"
using namespace Trading; using namespace Trading;
@ -270,8 +269,6 @@ static void tradeConfirm(CNSocket* sock, CNPacketData* data) {
otherSock->sendPacket(msg, P_FE2CL_GM_REP_PC_ANNOUNCE); otherSock->sendPacket(msg, P_FE2CL_GM_REP_PC_ANNOUNCE);
return; return;
} }
Database::commitTrade(plr, plr2);
} }
static void tradeConfirmCancel(CNSocket* sock, CNPacketData* data) { static void tradeConfirmCancel(CNSocket* sock, CNPacketData* data) {

View File

@ -32,7 +32,7 @@ void CNShared::pruneLoginMetadata(CNServer *serv, time_t currTime) {
auto& sk = it->first; auto& sk = it->first;
auto& lm = it->second; auto& lm = it->second;
if (currTime > lm->timestamp + CNSHARED_TIMEOUT) { if (lm->timestamp + CNSHARED_TIMEOUT > currTime) {
std::cout << "[WARN] Pruning hung connection attempt" << std::endl; std::cout << "[WARN] Pruning hung connection attempt" << std::endl;
// deallocate object and remove map entry // deallocate object and remove map entry

View File

@ -11,14 +11,14 @@
#include "Player.hpp" #include "Player.hpp"
/* /*
* Connecions time out after 5 minutes, checked every 30 seconds. * Connecions time out after 15 minutes, checked every 30 seconds.
*/ */
#define CNSHARED_TIMEOUT 300000 #define CNSHARED_TIMEOUT 900000
#define CNSHARED_PERIOD 30000 #define CNSHARED_PERIOD 30000
struct LoginMetadata { struct LoginMetadata {
uint64_t FEKey; uint64_t FEKey;
int32_t playerId; Player plr;
time_t timestamp; time_t timestamp;
}; };

View File

@ -52,8 +52,7 @@ namespace Database {
bool banPlayer(int playerId, std::string& reason); bool banPlayer(int playerId, std::string& reason);
bool unbanPlayer(int playerId); bool unbanPlayer(int playerId);
void updateSelected(int accountId, int slot); void updateSelected(int accountId, int playerId);
void updateSelectedByPlayerId(int accountId, int playerId);
bool validateCharacter(int characterID, int userID); bool validateCharacter(int characterID, int userID);
bool isNameFree(std::string firstName, std::string lastName); bool isNameFree(std::string firstName, std::string lastName);
@ -79,9 +78,7 @@ namespace Database {
// getting players // getting players
void getPlayer(Player* plr, int id); void getPlayer(Player* plr, int id);
bool _updatePlayer(Player *player);
void updatePlayer(Player *player); void updatePlayer(Player *player);
void commitTrade(Player *plr1, Player *plr2);
// buddies // buddies
int getNumBuddies(Player* player); int getNumBuddies(Player* player);
@ -101,7 +98,7 @@ namespace Database {
void deleteEmailAttachments(int playerID, int index, int slot); void deleteEmailAttachments(int playerID, int index, int slot);
void deleteEmails(int playerID, int64_t* indices); void deleteEmails(int playerID, int64_t* indices);
int getNextEmailIndex(int playerID); int getNextEmailIndex(int playerID);
bool sendEmail(EmailData* data, std::vector<sItemBase> attachments, Player *sender); bool sendEmail(EmailData* data, std::vector<sItemBase> attachments);
// racing // racing
RaceRanking getTopRaceRanking(int epID, int playerID); RaceRanking getTopRaceRanking(int epID, int playerID);

View File

@ -152,8 +152,7 @@ void Database::updateEmailContent(EmailData* data) {
sqlite3_step(stmt); sqlite3_step(stmt);
int attachmentsCount = sqlite3_column_int(stmt, 0); int attachmentsCount = sqlite3_column_int(stmt, 0);
// set attachment flag dynamically data->ItemFlag = (data->Taros > 0 || attachmentsCount > 0) ? 1 : 0; // set attachment flag dynamically
data->ItemFlag = (data->Taros > 0 || attachmentsCount > 0) ? 1 : 0;
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
@ -266,7 +265,7 @@ int Database::getNextEmailIndex(int playerID) {
return (index > 0 ? index + 1 : 1); return (index > 0 ? index + 1 : 1);
} }
bool Database::sendEmail(EmailData* data, std::vector<sItemBase> attachments, Player *sender) { bool Database::sendEmail(EmailData* data, std::vector<sItemBase> attachments) {
std::lock_guard<std::mutex> lock(dbCrit); std::lock_guard<std::mutex> lock(dbCrit);
sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL); sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
@ -331,13 +330,6 @@ bool Database::sendEmail(EmailData* data, std::vector<sItemBase> attachments, Pl
sqlite3_reset(stmt); sqlite3_reset(stmt);
} }
sqlite3_finalize(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); sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL);
return true; return true;
} }

View File

@ -79,29 +79,6 @@ void Database::updateSelected(int accountId, int slot) {
std::cout << "[WARN] Database fail on updateSelected(): " << sqlite3_errmsg(db) << std::endl; std::cout << "[WARN] Database fail on updateSelected(): " << sqlite3_errmsg(db) << std::endl;
} }
void Database::updateSelectedByPlayerId(int accountId, int32_t playerId) {
std::lock_guard<std::mutex> lock(dbCrit);
const char* sql = R"(
UPDATE Accounts SET
Selected = p.Slot,
LastLogin = (strftime('%s', 'now'))
FROM (SELECT Slot From Players WHERE PlayerId = ?) AS p
WHERE AccountID = ?;
)";
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, playerId);
sqlite3_bind_int(stmt, 2, accountId);
int rc = sqlite3_step(stmt);
sqlite3_finalize(stmt);
if (rc != SQLITE_DONE)
std::cout << "[WARN] Database fail on updateSelectedByPlayerId(): " << sqlite3_errmsg(db) << std::endl;
}
bool Database::validateCharacter(int characterID, int userID) { bool Database::validateCharacter(int characterID, int userID) {
std::lock_guard<std::mutex> lock(dbCrit); std::lock_guard<std::mutex> lock(dbCrit);

View File

@ -285,13 +285,11 @@ void Database::getPlayer(Player* plr, int id) {
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
} }
/* void Database::updatePlayer(Player *player) {
* Low-level function to save a player to DB. std::lock_guard<std::mutex> lock(dbCrit);
* Must be run in a SQL transaction and with dbCrit locked.
* The caller manages the transacstion, so if this function returns false, sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
* the caller must roll it back.
*/
bool Database::_updatePlayer(Player *player) {
const char* sql = R"( const char* sql = R"(
UPDATE Players UPDATE Players
SET SET
@ -338,8 +336,10 @@ bool Database::_updatePlayer(Player *player) {
sqlite3_bind_int(stmt, 21, player->iID); sqlite3_bind_int(stmt, 21, player->iID);
if (sqlite3_step(stmt) != SQLITE_DONE) { 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_finalize(stmt);
return false; sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
return;
} }
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
@ -375,8 +375,10 @@ bool Database::_updatePlayer(Player *player) {
rc = sqlite3_step(stmt); rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) { 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); sqlite3_finalize(stmt);
return false; return;
} }
sqlite3_reset(stmt); sqlite3_reset(stmt);
} }
@ -393,8 +395,10 @@ bool Database::_updatePlayer(Player *player) {
sqlite3_bind_int(stmt, 6, player->Inven[i].iTimeLimit); sqlite3_bind_int(stmt, 6, player->Inven[i].iTimeLimit);
if (sqlite3_step(stmt) != SQLITE_DONE) { 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); sqlite3_finalize(stmt);
return false; return;
} }
sqlite3_reset(stmt); sqlite3_reset(stmt);
} }
@ -411,8 +415,10 @@ bool Database::_updatePlayer(Player *player) {
sqlite3_bind_int(stmt, 6, player->Bank[i].iTimeLimit); sqlite3_bind_int(stmt, 6, player->Bank[i].iTimeLimit);
if (sqlite3_step(stmt) != SQLITE_DONE) { 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); sqlite3_finalize(stmt);
return false; return;
} }
sqlite3_reset(stmt); sqlite3_reset(stmt);
} }
@ -445,8 +451,10 @@ bool Database::_updatePlayer(Player *player) {
sqlite3_bind_int(stmt, 4, player->QInven[i].iID); sqlite3_bind_int(stmt, 4, player->QInven[i].iID);
if (sqlite3_step(stmt) != SQLITE_DONE) { 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); sqlite3_finalize(stmt);
return false; return;
} }
sqlite3_reset(stmt); sqlite3_reset(stmt);
} }
@ -479,8 +487,10 @@ bool Database::_updatePlayer(Player *player) {
sqlite3_bind_int(stmt, 4, player->Nanos[i].iStamina); sqlite3_bind_int(stmt, 4, player->Nanos[i].iStamina);
if (sqlite3_step(stmt) != SQLITE_DONE) { 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); sqlite3_finalize(stmt);
return false; return;
} }
sqlite3_reset(stmt); sqlite3_reset(stmt);
} }
@ -514,47 +524,14 @@ bool Database::_updatePlayer(Player *player) {
sqlite3_bind_int(stmt, 5, player->RemainingNPCCount[i][2]); sqlite3_bind_int(stmt, 5, player->RemainingNPCCount[i][2]);
if (sqlite3_step(stmt) != SQLITE_DONE) { 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); sqlite3_finalize(stmt);
return false; return;
} }
sqlite3_reset(stmt); sqlite3_reset(stmt);
} }
sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL);
sqlite3_finalize(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);
} }

View File

@ -471,7 +471,11 @@ void CNLoginServer::characterSelect(CNSocket* sock, CNPacketData* data) {
LoginMetadata *lm = new LoginMetadata(); LoginMetadata *lm = new LoginMetadata();
lm->FEKey = sock->getFEKey(); lm->FEKey = sock->getFEKey();
lm->timestamp = getTime(); lm->timestamp = getTime();
lm->playerId = selection->iPC_UID;
Database::getPlayer(&lm->plr, selection->iPC_UID);
// this should never happen but for extra safety
if (lm->plr.iID == 0)
return invalidCharacter(sock);
resp.iEnterSerialKey = Rand::cryptoRand(); resp.iEnterSerialKey = Rand::cryptoRand();
@ -481,7 +485,7 @@ void CNLoginServer::characterSelect(CNSocket* sock, CNPacketData* data) {
sock->sendPacket(resp, P_LS2CL_REP_SHARD_SELECT_SUCC); sock->sendPacket(resp, P_LS2CL_REP_SHARD_SELECT_SUCC);
// update current slot in DB // update current slot in DB
Database::updateSelectedByPlayerId(loginSessions[sock].userID, selection->iPC_UID); Database::updateSelected(loginSessions[sock].userID, lm->plr.slot);
} }
void CNLoginServer::finishTutorial(CNSocket* sock, CNPacketData* data) { void CNLoginServer::finishTutorial(CNSocket* sock, CNPacketData* data) {