mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-22 05:20:05 +00:00
Compare commits
5 Commits
eb8e54c1f0
...
100b4605ec
Author | SHA1 | Date | |
---|---|---|---|
100b4605ec | |||
741b898230 | |||
3f44f53f97 | |||
d92b407349 | |||
9b3e856a05 |
@ -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->iSlotNum < 1 || pkt->iSlotNum > 4)
|
if (pkt->iSlotNum < 0 || pkt->iSlotNum >= AINVEN_COUNT || pkt->iEmailItemSlot < 1 || pkt->iEmailItemSlot > 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)) {
|
if (!Database::sendEmail(&email, attachments, plr)) {
|
||||||
plr->money += cost; // give money back
|
plr->money += cost; // give money back
|
||||||
// give items back
|
// give items back
|
||||||
while (!attachments.empty()) {
|
while (!attachments.empty()) {
|
||||||
|
@ -28,17 +28,12 @@ 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) {
|
||||||
Player *p = new Player();
|
players[key] = plr;
|
||||||
|
plr->chunkPos = Chunking::INVALID_CHUNK;
|
||||||
|
plr->lastHeartbeat = 0;
|
||||||
|
|
||||||
// copy object into heap memory
|
std::cout << getPlayerName(plr) << " has joined!" << std::endl;
|
||||||
*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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,69 +219,73 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// for convenience
|
Player *plr = new Player();
|
||||||
Player& plr = lm->plr;
|
Database::getPlayer(plr, lm->playerId);
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.iID = plr.iID;
|
plr->groupCnt = 1;
|
||||||
|
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
|
||||||
@ -296,12 +295,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
|
||||||
@ -310,11 +309,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);
|
||||||
@ -325,14 +324,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);
|
||||||
|
|
||||||
// copy Player object into the shard
|
// transfer ownership of Player object into the shard (still valid in this function though)
|
||||||
addPlayer(sock, plr);
|
addPlayer(sock, plr);
|
||||||
|
|
||||||
// check if there is an expiring vehicle
|
// check if there is an expiring vehicle
|
||||||
Items::checkItemExpire(sock, getPlayer(sock));
|
Items::checkItemExpire(sock, plr);
|
||||||
|
|
||||||
// set player equip stats
|
// set player equip stats
|
||||||
Items::setItemStats(getPlayer(sock));
|
Items::setItemStats(plr);
|
||||||
|
|
||||||
Missions::failInstancedMissions(sock);
|
Missions::failInstancedMissions(sock);
|
||||||
|
|
||||||
@ -343,9 +342,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 (and therefore the plr object)
|
// deallocate lm
|
||||||
delete lm;
|
delete lm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "Trading.hpp"
|
#include "Trading.hpp"
|
||||||
#include "PlayerManager.hpp"
|
#include "PlayerManager.hpp"
|
||||||
|
#include "db/Database.hpp"
|
||||||
|
|
||||||
using namespace Trading;
|
using namespace Trading;
|
||||||
|
|
||||||
@ -269,6 +270,8 @@ 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) {
|
||||||
|
@ -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 (lm->timestamp + CNSHARED_TIMEOUT > currTime) {
|
if (currTime > lm->timestamp + CNSHARED_TIMEOUT) {
|
||||||
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
|
||||||
|
@ -11,14 +11,14 @@
|
|||||||
#include "Player.hpp"
|
#include "Player.hpp"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Connecions time out after 15 minutes, checked every 30 seconds.
|
* Connecions time out after 5 minutes, checked every 30 seconds.
|
||||||
*/
|
*/
|
||||||
#define CNSHARED_TIMEOUT 900000
|
#define CNSHARED_TIMEOUT 300000
|
||||||
#define CNSHARED_PERIOD 30000
|
#define CNSHARED_PERIOD 30000
|
||||||
|
|
||||||
struct LoginMetadata {
|
struct LoginMetadata {
|
||||||
uint64_t FEKey;
|
uint64_t FEKey;
|
||||||
Player plr;
|
int32_t playerId;
|
||||||
time_t timestamp;
|
time_t timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -52,7 +52,8 @@ 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 playerId);
|
void updateSelected(int accountId, int slot);
|
||||||
|
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);
|
||||||
@ -78,7 +79,9 @@ 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);
|
||||||
@ -98,7 +101,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);
|
bool sendEmail(EmailData* data, std::vector<sItemBase> attachments, Player *sender);
|
||||||
|
|
||||||
// racing
|
// racing
|
||||||
RaceRanking getTopRaceRanking(int epID, int playerID);
|
RaceRanking getTopRaceRanking(int epID, int playerID);
|
||||||
|
@ -152,7 +152,8 @@ 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);
|
||||||
|
|
||||||
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);
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
@ -265,7 +266,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) {
|
bool Database::sendEmail(EmailData* data, std::vector<sItemBase> attachments, Player *sender) {
|
||||||
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);
|
||||||
@ -330,6 +331,13 @@ bool Database::sendEmail(EmailData* data, std::vector<sItemBase> attachments) {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,29 @@ 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);
|
||||||
|
|
||||||
|
@ -285,11 +285,13 @@ void Database::getPlayer(Player* plr, int id) {
|
|||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::updatePlayer(Player *player) {
|
/*
|
||||||
std::lock_guard<std::mutex> lock(dbCrit);
|
* Low-level function to save a player to DB.
|
||||||
|
* Must be run in a SQL transaction and with dbCrit locked.
|
||||||
sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
|
* 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"(
|
const char* sql = R"(
|
||||||
UPDATE Players
|
UPDATE Players
|
||||||
SET
|
SET
|
||||||
@ -336,10 +338,8 @@ void 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);
|
||||||
sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
@ -375,10 +375,8 @@ void 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;
|
return false;
|
||||||
}
|
}
|
||||||
sqlite3_reset(stmt);
|
sqlite3_reset(stmt);
|
||||||
}
|
}
|
||||||
@ -395,10 +393,8 @@ void 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;
|
return false;
|
||||||
}
|
}
|
||||||
sqlite3_reset(stmt);
|
sqlite3_reset(stmt);
|
||||||
}
|
}
|
||||||
@ -415,10 +411,8 @@ void 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;
|
return false;
|
||||||
}
|
}
|
||||||
sqlite3_reset(stmt);
|
sqlite3_reset(stmt);
|
||||||
}
|
}
|
||||||
@ -451,10 +445,8 @@ void 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;
|
return false;
|
||||||
}
|
}
|
||||||
sqlite3_reset(stmt);
|
sqlite3_reset(stmt);
|
||||||
}
|
}
|
||||||
@ -487,10 +479,8 @@ void 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;
|
return false;
|
||||||
}
|
}
|
||||||
sqlite3_reset(stmt);
|
sqlite3_reset(stmt);
|
||||||
}
|
}
|
||||||
@ -524,14 +514,47 @@ void 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;
|
return false;
|
||||||
}
|
}
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
@ -471,11 +471,7 @@ 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();
|
||||||
|
|
||||||
@ -485,7 +481,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::updateSelected(loginSessions[sock].userID, lm->plr.slot);
|
Database::updateSelectedByPlayerId(loginSessions[sock].userID, selection->iPC_UID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CNLoginServer::finishTutorial(CNSocket* sock, CNPacketData* data) {
|
void CNLoginServer::finishTutorial(CNSocket* sock, CNPacketData* data) {
|
||||||
|
Loading…
Reference in New Issue
Block a user