On login, load Player from DB in shard thread, not in login thread

This avoids some needless data shuffling and fixes a rare desync.
This commit is contained in:
dongresource 2022-12-06 01:07:21 +01:00
parent d92b407349
commit 3f44f53f97
5 changed files with 38 additions and 14 deletions

View File

@ -224,18 +224,22 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
return; return;
} }
// for convenience Player plr = {};
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);
} }
plr.groupCnt = 1;
plr.iIDGroup = plr.groupIDs[0] = plr.iID;
response.iID = plr.iID; response.iID = plr.iID;
response.uiSvrTime = getTime(); response.uiSvrTime = getTime();
response.PCLoadData2CL.iUserLevel = plr.accountLevel; response.PCLoadData2CL.iUserLevel = plr.accountLevel;
@ -345,7 +349,7 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
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;
} }

View File

@ -18,7 +18,7 @@
struct LoginMetadata { struct LoginMetadata {
uint64_t FEKey; uint64_t FEKey;
Player plr; int32_t playerId;
time_t timestamp; time_t timestamp;
}; };

View File

@ -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);

View File

@ -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);

View File

@ -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) {