mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-22 13:30:06 +00:00
Reimplement DB functions 1/2
implemented addAccount implemented updateSelected implemented findAccount validateCharacter isNameFree and isSlotFree implemented getTableSize function implemented functions for name save, character creation and finishing tutorial
This commit is contained in:
parent
34ca36062c
commit
2f5c2a8764
@ -121,24 +121,27 @@ void CNLoginServer::login(CNSocket* sock, CNPacketData* data) {
|
|||||||
if (!CNLoginServer::isLoginDataGood(userLogin, userPassword))
|
if (!CNLoginServer::isLoginDataGood(userLogin, userPassword))
|
||||||
return loginFail(LoginError::LOGIN_ERROR, userLogin, sock);
|
return loginFail(LoginError::LOGIN_ERROR, userLogin, sock);
|
||||||
|
|
||||||
std::unique_ptr<Database::Account> findUser = Database::findAccount(userLogin);
|
Database::Account findUser = {};
|
||||||
if (findUser == nullptr)
|
Database::findAccount(&findUser, userLogin);
|
||||||
|
|
||||||
|
// if 0 was returned, account was not found
|
||||||
|
if (findUser.AccountID == 0)
|
||||||
return newAccount(sock, userLogin, userPassword, login->iClientVerC);
|
return newAccount(sock, userLogin, userPassword, login->iClientVerC);
|
||||||
|
|
||||||
if (!CNLoginServer::isPasswordCorrect(findUser->Password, userPassword))
|
if (!CNLoginServer::isPasswordCorrect(findUser.Password, userPassword))
|
||||||
return loginFail(LoginError::ID_AND_PASSWORD_DO_NOT_MATCH, userLogin, sock);
|
return loginFail(LoginError::ID_AND_PASSWORD_DO_NOT_MATCH, userLogin, sock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* calling this here to timestamp login attempt,
|
* calling this here to timestamp login attempt,
|
||||||
* in order to make duplicate exit sanity check work
|
* in order to make duplicate exit sanity check work
|
||||||
*/
|
*/
|
||||||
Database::updateSelected(findUser->AccountID, findUser->Selected);
|
Database::updateSelected(findUser.AccountID, findUser.Selected);
|
||||||
|
|
||||||
if (CNLoginServer::isAccountInUse(findUser->AccountID))
|
if (CNLoginServer::isAccountInUse(findUser.AccountID))
|
||||||
return loginFail(LoginError::ID_ALREADY_IN_USE, userLogin, sock);
|
return loginFail(LoginError::ID_ALREADY_IN_USE, userLogin, sock);
|
||||||
|
|
||||||
loginSessions[sock] = CNLoginData();
|
loginSessions[sock] = CNLoginData();
|
||||||
loginSessions[sock].userID = findUser->AccountID;
|
loginSessions[sock].userID = findUser.AccountID;
|
||||||
loginSessions[sock].lastHeartbeat = getTime();
|
loginSessions[sock].lastHeartbeat = getTime();
|
||||||
|
|
||||||
std::vector<sP_LS2CL_REP_CHAR_INFO> characters = Database::getCharInfo(loginSessions[sock].userID);
|
std::vector<sP_LS2CL_REP_CHAR_INFO> characters = Database::getCharInfo(loginSessions[sock].userID);
|
||||||
@ -147,7 +150,7 @@ void CNLoginServer::login(CNSocket* sock, CNPacketData* data) {
|
|||||||
memcpy(resp.szID, login->szID, sizeof(login->szID));
|
memcpy(resp.szID, login->szID, sizeof(login->szID));
|
||||||
|
|
||||||
resp.iCharCount = characters.size();
|
resp.iCharCount = characters.size();
|
||||||
resp.iSlotNum = findUser->Selected;
|
resp.iSlotNum = findUser.Selected;
|
||||||
resp.iPaymentFlag = 1;
|
resp.iPaymentFlag = 1;
|
||||||
resp.iOpenBetaFlag = 0;
|
resp.iOpenBetaFlag = 0;
|
||||||
resp.uiSvrTime = getTime();
|
resp.uiSvrTime = getTime();
|
||||||
@ -178,7 +181,7 @@ void CNLoginServer::login(CNSocket* sock, CNPacketData* data) {
|
|||||||
|
|
||||||
void CNLoginServer::newAccount(CNSocket* sock, std::string userLogin, std::string userPassword, int32_t clientVerC) {
|
void CNLoginServer::newAccount(CNSocket* sock, std::string userLogin, std::string userPassword, int32_t clientVerC) {
|
||||||
loginSessions[sock] = CNLoginData();
|
loginSessions[sock] = CNLoginData();
|
||||||
loginSessions[sock].userID = Database::addAccount(userLogin, userPassword);
|
loginSessions[sock].userID = Database::addAccount(userLogin, userPassword); //TODO: Add logic if query fails
|
||||||
loginSessions[sock].lastHeartbeat = getTime();
|
loginSessions[sock].lastHeartbeat = getTime();
|
||||||
|
|
||||||
INITSTRUCT(sP_LS2CL_REP_LOGIN_SUCC, resp);
|
INITSTRUCT(sP_LS2CL_REP_LOGIN_SUCC, resp);
|
||||||
|
563
src/Database.cpp
563
src/Database.cpp
@ -33,8 +33,8 @@ void Database::open() {
|
|||||||
createTables();
|
createTables();
|
||||||
|
|
||||||
std::cout << "[INFO] Database in operation ";
|
std::cout << "[INFO] Database in operation ";
|
||||||
int accounts = getAccountsCount();
|
int accounts = getTableSize("Accounts");
|
||||||
int players = getPlayersCount();
|
int players = getTableSize("Players");
|
||||||
std::string message = "";
|
std::string message = "";
|
||||||
if (accounts > 0) {
|
if (accounts > 0) {
|
||||||
message += ": Found " + std::to_string(accounts) + " Account";
|
message += ": Found " + std::to_string(accounts) + " Account";
|
||||||
@ -59,58 +59,58 @@ void Database::createTables() {
|
|||||||
"AccountID" INTEGER NOT NULL,
|
"AccountID" INTEGER NOT NULL,
|
||||||
"Login" TEXT NOT NULL UNIQUE,
|
"Login" TEXT NOT NULL UNIQUE,
|
||||||
"Password" TEXT NOT NULL,
|
"Password" TEXT NOT NULL,
|
||||||
"Selected" INTEGER NOT NULL,
|
"Selected" INTEGER DEFAULT 1 NOT NULL,
|
||||||
"Created" INTEGER NOT NULL,
|
"Created" INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||||
"LastLogin" INTEGER NOT NULL,
|
"LastLogin" INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||||
PRIMARY KEY("AccountID" AUTOINCREMENT)
|
PRIMARY KEY("AccountID" AUTOINCREMENT)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "Players" (
|
CREATE TABLE IF NOT EXISTS "Players" (
|
||||||
"PlayerID" INTEGER NOT NULL,
|
"PlayerID" INTEGER NOT NULL,
|
||||||
"AccountID" INTEGER NOT NULL,
|
"AccountID" INTEGER NOT NULL,
|
||||||
"Slot" INTEGER NOT NULL,
|
"Slot" INTEGER NOT NULL,
|
||||||
"Firstname" TEXT NOT NULL COLLATE NOCASE,
|
"Firstname" TEXT NOT NULL COLLATE NOCASE,
|
||||||
"LastName" TEXT NOT NULL COLLATE NOCASE,
|
"LastName" TEXT NOT NULL COLLATE NOCASE,
|
||||||
"Created" INTEGER NOT NULL,
|
"Created" INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||||
"LastLogin" INTEGER NOT NULL,
|
"LastLogin" INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||||
"Level" INTEGER NOT NULL,
|
"Level" INTEGER DEFAULT 1 NOT NULL,
|
||||||
"Nano1" INTEGER NOT NULL,
|
"Nano1" INTEGER DEFAULT 0 NOT NULL,
|
||||||
"Nano2" INTEGER NOT NULL,
|
"Nano2" INTEGER DEFAULT 0 NOT NULL,
|
||||||
"Nano3" INTEGER NOT NULL,
|
"Nano3" INTEGER DEFAULT 0 NOT NULL,
|
||||||
"AppearanceFlag" INTEGER NOT NULL,
|
"AppearanceFlag" INTEGER DEFAULT 0 NOT NULL,
|
||||||
"TutorialFlag" INTEGER NOT NULL,
|
"TutorialFlag" INTEGER DEFAULT 0 NOT NULL,
|
||||||
"PayZoneFlag" INTEGER NOT NULL,
|
"PayZoneFlag" INTEGER DEFAULT 0 NOT NULL,
|
||||||
"XCoordinates" INTEGER NOT NULL,
|
"XCoordinates" INTEGER NOT NULL,
|
||||||
"YCoordinates" INTEGER NOT NULL,
|
"YCoordinates" INTEGER NOT NULL,
|
||||||
"ZCoordinates" INTEGER NOT NULL,
|
"ZCoordinates" INTEGER NOT NULL,
|
||||||
|
"Angle" INTEGER NOT NULL,
|
||||||
"HP" INTEGER NOT NULL,
|
"HP" INTEGER NOT NULL,
|
||||||
"NameCheck" INTEGER NOT NULL,
|
"NameCheck" INTEGER NOT NULL,
|
||||||
"AccountLevel" INTEGER NOT NULL,
|
"AccountLevel" INTEGER NOT NULL,
|
||||||
"FusionMatter" INTEGER NOT NULL,
|
"FusionMatter" INTEGER DEFAULT 0 NOT NULL,
|
||||||
"Taros" INTEGER NOT NULL,
|
"Taros" INTEGER DEFAULT 0 NOT NULL,
|
||||||
"Quests" BLOB NOT NULL,
|
"Quests" BLOB NOT NULL,
|
||||||
"BatteryW" INTEGER NOT NULL,
|
"BatteryW" INTEGER DEFAULT 0 NOT NULL,
|
||||||
"BatteryN" INTEGER NOT NULL,
|
"BatteryN" INTEGER DEFAULT 0 NOT NULL,
|
||||||
"Mentor" INTEGER NOT NULL,
|
"Mentor" INTEGER DEFAULT 5 NOT NULL,
|
||||||
"WarpLocationFlag" INTEGER NOT NULL,
|
"WarpLocationFlag" INTEGER DEFAULT 0 NOT NULL,
|
||||||
"SkywayLocationFlag1" INTEGER NOT NULL,
|
"SkywayLocationFlag1" INTEGER DEFAULT 0 NOT NULL,
|
||||||
"SkywayLocationFlag2" INTEGER NOT NULL,
|
"SkywayLocationFlag2" INTEGER DEFAULT 0 NOT NULL,
|
||||||
"CurrentMissionID" INTEGER NOT NULL,
|
"CurrentMissionID" INTEGER DEFAULT 0 NOT NULL,
|
||||||
PRIMARY KEY("PlayerID" AUTOINCREMENT),
|
PRIMARY KEY("PlayerID" AUTOINCREMENT),
|
||||||
FOREIGN KEY("AccountID") REFERENCES "Accounts"("AccountID") ON DELETE CASCADE
|
FOREIGN KEY("AccountID") REFERENCES "Accounts"("AccountID") ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "Appearances" (
|
CREATE TABLE IF NOT EXISTS "Appearances" (
|
||||||
"PlayerID" INTEGER NOT NULL,
|
"PlayerID" INTEGER NOT NULL,
|
||||||
"Angle" INTEGER NOT NULL,
|
"Body" INTEGER DEFAULT 0 NOT NULL,
|
||||||
"Body" INTEGER NOT NULL,
|
"EyeColor" INTEGER DEFAULT 1 NOT NULL,
|
||||||
"EyeColor" INTEGER NOT NULL,
|
"FaceStyle" INTEGER DEFAULT 1 NOT NULL,
|
||||||
"FaceStyle" INTEGER NOT NULL,
|
"Gender" INTEGER DEFAULT 1 NOT NULL,
|
||||||
"Gender" INTEGER NOT NULL,
|
"HairColor" INTEGER DEFAULT 1 NOT NULL,
|
||||||
"HairColor" INTEGER NOT NULL,
|
"HairStyle" INTEGER DEFAULT 1 NOT NULL,
|
||||||
"HairStyle" INTEGER NOT NULL,
|
"Height" INTEGER DEFAULT 0 NOT NULL,
|
||||||
"Height" INTEGER NOT NULL,
|
"SkinColor" INTEGER DEFAULT 1 NOT NULL,
|
||||||
"SkinColor" INTEGER NOT NULL,
|
|
||||||
FOREIGN KEY("PlayerID") REFERENCES "Players"("PlayerID") ON DELETE CASCADE
|
FOREIGN KEY("PlayerID") REFERENCES "Players"("PlayerID") ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ void Database::createTables() {
|
|||||||
"Id" INTEGER NOT NULL,
|
"Id" INTEGER NOT NULL,
|
||||||
"Type" INTEGER NOT NULL,
|
"Type" INTEGER NOT NULL,
|
||||||
"Opt" INTEGER NOT NULL,
|
"Opt" INTEGER NOT NULL,
|
||||||
"TimeLimit" INTEGER NOT NULL,
|
"TimeLimit" INTEGER NOT DEFALUT 0 NULL,
|
||||||
FOREIGN KEY("PlayerID") REFERENCES "Players"("PlayerID") ON DELETE CASCADE
|
FOREIGN KEY("PlayerID") REFERENCES "Players"("PlayerID") ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ void Database::createTables() {
|
|||||||
"PlayerId" INTEGER NOT NULL,
|
"PlayerId" INTEGER NOT NULL,
|
||||||
"Id" INTEGER NOT NULL,
|
"Id" INTEGER NOT NULL,
|
||||||
"Skill" INTEGER NOT NULL,
|
"Skill" INTEGER NOT NULL,
|
||||||
"Stamina" INTEGER NOT NULL,
|
"Stamina" INTEGER DEFAULT = 150 NOT NULL,
|
||||||
FOREIGN KEY("PlayerID") REFERENCES "Players"("PlayerID") ON DELETE CASCADE
|
FOREIGN KEY("PlayerID") REFERENCES "Players"("PlayerID") ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -191,206 +191,397 @@ void Database::createTables() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Database::getTableSize(std::string tableName) {
|
||||||
|
std::lock_guard<std::mutex> lock(dbCrit);
|
||||||
|
|
||||||
|
std::string query = "SELECT COUNT(*) FROM " + tableName + ";";
|
||||||
int Database::getAccountsCount() {
|
const char* sql = query.c_str();
|
||||||
int result;
|
sqlite3_stmt* stmt;
|
||||||
char* sql = "SELECT COUNT(*) FROM Accounts";
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
rc = sqlite3_exec(db, sql, CBAccountsCount, result, &err_msg);
|
sqlite3_step(stmt);
|
||||||
}
|
int result = sqlite3_column_int(stmt, 0);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
static int Database::CBAccountsCount(void* AccountsCount, int count, char** data, char** columns) {
|
return result;
|
||||||
*AccountsCount = (int)data[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int Database::getPlayersCount() {
|
|
||||||
return db.count<DbPlayer>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Database::addAccount(std::string login, std::string password) {
|
int Database::addAccount(std::string login, std::string password) {
|
||||||
std::lock_guard<std::mutex> lock(dbCrit);
|
std::lock_guard<std::mutex> lock(dbCrit);
|
||||||
|
|
||||||
password = BCrypt::generateHash(password);
|
const char* sql = R"(
|
||||||
Account account = {};
|
INSERT INTO "Accounts"
|
||||||
account.Login = login;
|
("Login", "Password")
|
||||||
account.Password = password;
|
VALUES (?, ?);
|
||||||
account.Selected = 1;
|
)";
|
||||||
account.Created = getTimestamp();
|
sqlite3_stmt* stmt;
|
||||||
account.LastLogin = account.Created;
|
|
||||||
return db.insert(account);
|
int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
|
sqlite3_bind_text(stmt, 1, login.c_str(), -1, 0);
|
||||||
|
sqlite3_bind_text(stmt, 2, BCrypt::generateHash(password).c_str(), -1, 0);
|
||||||
|
sqlite3_step(stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
return sqlite3_last_insert_rowid(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::updateSelected(int accountId, int slot) {
|
void Database::updateSelected(int accountId, int slot) {
|
||||||
std::lock_guard<std::mutex> lock(dbCrit);
|
std::lock_guard<std::mutex> lock(dbCrit);
|
||||||
|
|
||||||
Account acc = db.get<Account>(accountId);
|
if (slot < 0 || slot > 4) {
|
||||||
acc.Selected = slot;
|
std::cout << "[WARN] Invalid slot number passed to updateSelected()! " << std::endl;
|
||||||
// timestamp
|
return;
|
||||||
acc.LastLogin = getTimestamp();
|
}
|
||||||
db.update(acc);
|
|
||||||
|
const char* sql = R"(
|
||||||
|
UPDATE "Accounts"
|
||||||
|
SET "Selected" = ?, "LastLogin" = (strftime('%s', 'now'))
|
||||||
|
WHERE "AccountID" = ?;
|
||||||
|
)";
|
||||||
|
|
||||||
|
sqlite3_stmt* stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
|
sqlite3_bind_int(stmt, 1, slot);
|
||||||
|
sqlite3_bind_int(stmt, 2, accountId);
|
||||||
|
int rc = sqlite3_step(stmt);
|
||||||
|
if (rc != SQLITE_DONE)
|
||||||
|
std::cout << "[WARN] Database fail on updateSelected(). Error Code " << rc << std::endl;
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Database::Account> Database::findAccount(std::string login) {
|
void Database::findAccount(Account* account, std::string login) {
|
||||||
std::lock_guard<std::mutex> lock(dbCrit);
|
std::lock_guard<std::mutex> lock(dbCrit);
|
||||||
|
|
||||||
// this is awful, I've tried everything to improve it
|
const char* sql = R"(
|
||||||
auto find = db.get_all<Account>(
|
SELECT "AccountID", "Password", "Selected"
|
||||||
where(c(&Account::Login) == login), limit(1));
|
FROM "Accounts"
|
||||||
if (find.empty())
|
WHERE "Login" = ?
|
||||||
return nullptr;
|
LIMIT 1;
|
||||||
return
|
)";
|
||||||
std::unique_ptr<Account>(new Account(find.front()));
|
sqlite3_stmt* stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
|
sqlite3_bind_text(stmt, 1, login.c_str(), -1, 0);
|
||||||
|
int rc = sqlite3_step(stmt);
|
||||||
|
if (rc == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
account->AccountID = sqlite3_column_int(stmt, 0);
|
||||||
|
account->Password = std::string(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1)));
|
||||||
|
account->Selected = sqlite3_column_int(stmt, 2);
|
||||||
|
}
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Database::validateCharacter(int characterID, int userID) {
|
bool Database::validateCharacter(int characterID, int userID) {
|
||||||
return db.select(&DbPlayer::PlayerID,
|
std::lock_guard<std::mutex> lock(dbCrit);
|
||||||
where((c(&DbPlayer::PlayerID) == characterID) && (c(&DbPlayer::AccountID) == userID)))
|
|
||||||
.size() > 0;
|
// query whatever
|
||||||
|
const char* sql = R"(
|
||||||
|
SELECT "PlayerID"
|
||||||
|
FROM "Players"
|
||||||
|
WHERE "PlayerID" = ? AND "AccountID" = ?
|
||||||
|
LIMIT 1;
|
||||||
|
)";
|
||||||
|
sqlite3_stmt* stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
|
sqlite3_bind_int(stmt, 1, characterID);
|
||||||
|
sqlite3_bind_int(stmt, 2, userID);
|
||||||
|
int rc = sqlite3_step(stmt);
|
||||||
|
// if we got a row back, the character is valid
|
||||||
|
bool result = (rc == SQLITE_ROW);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Database::isNameFree(std::string firstName, std::string lastName) {
|
bool Database::isNameFree(std::string firstName, std::string lastName) {
|
||||||
std::lock_guard<std::mutex> lock(dbCrit);
|
std::lock_guard<std::mutex> lock(dbCrit);
|
||||||
|
|
||||||
return
|
const char* sql = R"(
|
||||||
(db.get_all<DbPlayer>
|
SELECT "PlayerID"
|
||||||
(where((c(&DbPlayer::FirstName) == firstName)
|
FROM "Players"
|
||||||
and (c(&DbPlayer::LastName) == lastName)))
|
WHERE "Firstname" = ? AND "LastName" = ?
|
||||||
.empty());
|
LIMIT 1;
|
||||||
|
)";
|
||||||
|
sqlite3_stmt* stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
|
sqlite3_bind_text(stmt, 1, firstName.c_str(), -1, 0);
|
||||||
|
sqlite3_bind_text(stmt, 2, lastName.c_str(), -1, 0);
|
||||||
|
int rc = sqlite3_step(stmt);
|
||||||
|
|
||||||
|
bool result = (rc != SQLITE_ROW);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Database::isSlotFree(int accountId, int slotNum) {
|
bool Database::isSlotFree(int accountId, int slotNum) {
|
||||||
std::lock_guard<std::mutex> lock(dbCrit);
|
std::lock_guard<std::mutex> lock(dbCrit);
|
||||||
|
|
||||||
return
|
if (slotNum < 0 || slotNum > 4) {
|
||||||
(db.get_all<DbPlayer>
|
std::cout << "[WARN] Invalid slot number passed to isSlotFree()! " << std::endl;
|
||||||
(where((c(&DbPlayer::AccountID) == accountId)
|
return false;
|
||||||
and (c(&DbPlayer::slot) == slotNum)))
|
}
|
||||||
.empty());
|
|
||||||
|
const char* sql = R"(
|
||||||
|
SELECT "PlayerID"
|
||||||
|
FROM "Players"
|
||||||
|
WHERE "AccountID" = ? AND "Slot" = ?
|
||||||
|
LIMIT 1;
|
||||||
|
)";
|
||||||
|
sqlite3_stmt* stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
|
sqlite3_bind_int(stmt, 1, accountId);
|
||||||
|
sqlite3_bind_int(stmt, 2, slotNum);
|
||||||
|
int rc = sqlite3_step(stmt);
|
||||||
|
|
||||||
|
bool result = (rc != SQLITE_ROW);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID) {
|
int Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID) {
|
||||||
std::lock_guard<std::mutex> lock(dbCrit);
|
std::lock_guard<std::mutex> lock(dbCrit);
|
||||||
|
|
||||||
|
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL);
|
||||||
|
|
||||||
// fail if the player already has 4 or more characters
|
const char* sql = R"(
|
||||||
if (db.count<DbPlayer>(where(c(&DbPlayer::AccountID) == AccountID)) >= 4)
|
INSERT INTO "Players"
|
||||||
return -1;
|
("AccountID", "Slot", "Firstname", "LastName", "XCoordinates" , "YCoordinates", "ZCoordinates", "Angle",
|
||||||
|
"HP", "NameCheck", "AccountLevel", "Quests")
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
|
||||||
|
)";
|
||||||
|
sqlite3_stmt* stmt;
|
||||||
|
std::string firstName = U16toU8(save->szFirstName);
|
||||||
|
std::string lastName = U16toU8(save->szLastName);
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
|
sqlite3_bind_int(stmt, 1, AccountID);
|
||||||
|
sqlite3_bind_int(stmt, 2, save->iSlotNum);
|
||||||
|
sqlite3_bind_text(stmt, 3, firstName.c_str(), -1, 0);
|
||||||
|
sqlite3_bind_text(stmt, 4, lastName.c_str(), -1, 0);
|
||||||
|
sqlite3_bind_int(stmt, 5, settings::SPAWN_X);
|
||||||
|
sqlite3_bind_int(stmt, 6, settings::SPAWN_Y);
|
||||||
|
sqlite3_bind_int(stmt, 7, settings::SPAWN_Z);
|
||||||
|
sqlite3_bind_int(stmt, 8, settings::SPAWN_ANGLE);
|
||||||
|
sqlite3_bind_int(stmt, 9, PC_MAXHEALTH(1));
|
||||||
|
|
||||||
DbPlayer create = {};
|
// if FNCode isn't 0, it's a wheel name
|
||||||
|
int nameCheck = (settings::APPROVEALLNAMES || save->iFNCode) ? 1 : 0;
|
||||||
|
sqlite3_bind_int(stmt, 10, nameCheck);
|
||||||
|
sqlite3_bind_int(stmt, 11, settings::ACCLEVEL);
|
||||||
|
|
||||||
// set timestamp
|
// 128 byte blob for completed quests
|
||||||
create.Created = getTimestamp();
|
unsigned char blobBuffer[128];
|
||||||
// save packet data
|
sqlite3_bind_blob(stmt, 12, blobBuffer, sizeof(blobBuffer), 0);
|
||||||
create.FirstName = U16toU8(save->szFirstName);
|
|
||||||
create.LastName = U16toU8(save->szLastName);
|
|
||||||
create.slot = save->iSlotNum;
|
|
||||||
create.AccountID = AccountID;
|
|
||||||
|
|
||||||
// set flags
|
sqlite3_step(stmt);
|
||||||
create.AppearanceFlag = 0;
|
|
||||||
create.TutorialFlag = 0;
|
|
||||||
create.PayZoneFlag = 0;
|
|
||||||
|
|
||||||
// set namecheck based on setting
|
int playerId = sqlite3_last_insert_rowid(db);
|
||||||
if (settings::APPROVEALLNAMES || save->iFNCode)
|
sqlite3_finalize(stmt);
|
||||||
create.NameCheck = 1;
|
|
||||||
else
|
|
||||||
create.NameCheck = 0;
|
|
||||||
|
|
||||||
// create default body character
|
// if something failed
|
||||||
create.Body = 0;
|
if (playerId == 0)
|
||||||
create.Class = 0;
|
return 0;
|
||||||
create.EyeColor = 1;
|
|
||||||
create.FaceStyle = 1;
|
|
||||||
create.Gender = 1;
|
|
||||||
create.Level = 1;
|
|
||||||
create.HP = PC_MAXHEALTH(create.Level);
|
|
||||||
create.HairColor = 1;
|
|
||||||
create.HairStyle = 1;
|
|
||||||
create.Height = 0;
|
|
||||||
create.SkinColor = 1;
|
|
||||||
create.AccountLevel = settings::ACCLEVEL;
|
|
||||||
create.x_coordinates = settings::SPAWN_X;
|
|
||||||
create.y_coordinates = settings::SPAWN_Y;
|
|
||||||
create.z_coordinates = settings::SPAWN_Z;
|
|
||||||
create.angle = settings::SPAWN_ANGLE;
|
|
||||||
// set mentor to computress
|
|
||||||
create.Mentor = 5;
|
|
||||||
|
|
||||||
// initialize the quest blob to 128 0-bytes
|
sql = R"(
|
||||||
create.QuestFlag = std::vector<char>(128, 0);
|
INSERT INTO "Appearances"
|
||||||
|
("PlayerID")
|
||||||
|
VALUES (?);
|
||||||
|
)";
|
||||||
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
|
sqlite3_bind_int(stmt, 1, playerId);
|
||||||
|
|
||||||
return db.insert(create);
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL);
|
||||||
|
return playerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character) {
|
bool Database::finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character) {
|
||||||
std::lock_guard<std::mutex> lock(dbCrit);
|
std::lock_guard<std::mutex> lock(dbCrit);
|
||||||
|
|
||||||
|
const char* sql = R"(
|
||||||
|
SELECT "PlayerID"
|
||||||
|
FROM "Players"
|
||||||
|
WHERE "PlayerID" = ? AND "AppearanceFlag" = 0
|
||||||
|
LIMIT 1;
|
||||||
|
)";
|
||||||
|
sqlite3_stmt* stmt;
|
||||||
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
|
sqlite3_bind_int(stmt, 1, character->PCStyle.iPC_UID);
|
||||||
|
|
||||||
DbPlayer finish = getDbPlayerById(character->PCStyle.iPC_UID);
|
if (sqlite3_step(stmt) != SQLITE_ROW)
|
||||||
finish.AppearanceFlag = 1;
|
{
|
||||||
finish.Body = character->PCStyle.iBody;
|
std::cout << "[WARN] Player tried Character Creation on already existing character?!" << std::endl;
|
||||||
finish.Class = character->PCStyle.iClass;
|
sqlite3_finalize(stmt);
|
||||||
finish.EyeColor = character->PCStyle.iEyeColor;
|
return false;
|
||||||
finish.FaceStyle = character->PCStyle.iFaceStyle;
|
}
|
||||||
finish.Gender = character->PCStyle.iGender;
|
|
||||||
finish.HairColor = character->PCStyle.iHairColor;
|
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL);
|
||||||
finish.HairStyle = character->PCStyle.iHairStyle;
|
|
||||||
finish.Height = character->PCStyle.iHeight;
|
sql = R"(
|
||||||
finish.Level = 1;
|
UPDATE "Players"
|
||||||
finish.SkinColor = character->PCStyle.iSkinColor;
|
SET "AppearanceFlag" = 1
|
||||||
db.update(finish);
|
WHERE "PlayerID" = ?;
|
||||||
// clothes
|
)";
|
||||||
Inventory Foot, LB, UB;
|
|
||||||
Foot.playerId = character->PCStyle.iPC_UID;
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
Foot.id = character->sOn_Item.iEquipFootID;
|
sqlite3_bind_int(stmt, 1, character->PCStyle.iPC_UID);
|
||||||
Foot.Type = 3;
|
if (sqlite3_step(stmt) != SQLITE_DONE)
|
||||||
Foot.slot = 3;
|
{
|
||||||
Foot.Opt = 1;
|
sqlite3_finalize(stmt);
|
||||||
Foot.TimeLimit = 0;
|
return false;
|
||||||
db.insert(Foot);
|
}
|
||||||
LB.playerId = character->PCStyle.iPC_UID;
|
|
||||||
LB.id = character->sOn_Item.iEquipLBID;
|
sql = R"(
|
||||||
LB.Type = 2;
|
UPDATE "Appearances"
|
||||||
LB.slot = 2;
|
SET
|
||||||
LB.Opt = 1;
|
"Body" = ? ,
|
||||||
LB.TimeLimit = 0;
|
"EyeColor" = ? ,
|
||||||
db.insert(LB);
|
"FaceStyle" = ? ,
|
||||||
UB.playerId = character->PCStyle.iPC_UID;
|
"Gender" = ? ,
|
||||||
UB.id = character->sOn_Item.iEquipUBID;
|
"HairColor" = ? ,
|
||||||
UB.Type = 1;
|
"HairStyle" = ? ,
|
||||||
UB.slot = 1;
|
"Height" = ? ,
|
||||||
UB.Opt = 1;
|
"SkinColor" = ?
|
||||||
UB.TimeLimit = 0;
|
WHERE "PlayerID" = ? ;
|
||||||
db.insert(UB);
|
)";
|
||||||
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
|
|
||||||
|
sqlite3_bind_int(stmt, 1, character->PCStyle.iBody);
|
||||||
|
sqlite3_bind_int(stmt, 2, character->PCStyle.iEyeColor);
|
||||||
|
sqlite3_bind_int(stmt, 3, character->PCStyle.iFaceStyle);
|
||||||
|
sqlite3_bind_int(stmt, 4, character->PCStyle.iGender);
|
||||||
|
sqlite3_bind_int(stmt, 5, character->PCStyle.iHairColor);
|
||||||
|
sqlite3_bind_int(stmt, 6, character->PCStyle.iHairStyle);
|
||||||
|
sqlite3_bind_int(stmt, 7, character->PCStyle.iHeight);
|
||||||
|
sqlite3_bind_int(stmt, 8, character->PCStyle.iSkinColor);
|
||||||
|
sqlite3_bind_int(stmt, 9, character->PCStyle.iPC_UID);
|
||||||
|
|
||||||
|
if (sqlite3_step(stmt) != SQLITE_DONE)
|
||||||
|
{
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
sql = R"(
|
||||||
|
INSERT INTO "Inventory"
|
||||||
|
("PlayerID", "Slot", "Id", "Type", "Opt")
|
||||||
|
VALUES (?, ?, ?, ?, 1);
|
||||||
|
)";
|
||||||
|
int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
|
|
||||||
|
int items[3] = { character->sOn_Item.iEquipUBID, character->sOn_Item.iEquipLBID, character->sOn_Item.iEquipFootID };
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
rc = sqlite3_bind_int(stmt, 1, character->PCStyle.iPC_UID);
|
||||||
|
rc = sqlite3_bind_int(stmt, 2, i+1);
|
||||||
|
rc = sqlite3_bind_int(stmt, 3, items[i]);
|
||||||
|
rc = sqlite3_bind_int(stmt, 4, i+1);
|
||||||
|
|
||||||
|
if (sqlite3_step(stmt) != SQLITE_DONE)
|
||||||
|
{
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::finishTutorial(int PlayerID) {
|
bool Database::finishTutorial(int playerID) {
|
||||||
std::lock_guard<std::mutex> lock(dbCrit);
|
std::lock_guard<std::mutex> lock(dbCrit);
|
||||||
|
|
||||||
Player finish = getPlayer(PlayerID);
|
const char* sql = R"(
|
||||||
// set flag
|
SELECT "PlayerID"
|
||||||
finish.PCStyle2.iTutorialFlag= 1;
|
FROM "Players"
|
||||||
// add Gun
|
WHERE "PlayerID" = ? AND "TutorialFlag" = 0
|
||||||
Inventory LightningGun = {};
|
LIMIT 1;
|
||||||
LightningGun.playerId = PlayerID;
|
)";
|
||||||
LightningGun.id = 328;
|
sqlite3_stmt* stmt;
|
||||||
LightningGun.slot = 0;
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
LightningGun.Type = 0;
|
sqlite3_bind_int(stmt, 1, playerID);
|
||||||
LightningGun.Opt = 1;
|
|
||||||
db.insert(LightningGun);
|
|
||||||
// add Nano
|
|
||||||
Nano Buttercup = {};
|
|
||||||
Buttercup.playerId = PlayerID;
|
|
||||||
Buttercup.iID = 1;
|
|
||||||
Buttercup.iSkillID = 1;
|
|
||||||
Buttercup.iStamina = 150;
|
|
||||||
finish.equippedNanos[0] = 1;
|
|
||||||
db.insert(Buttercup);
|
|
||||||
// save missions
|
|
||||||
MissionManager::saveMission(&finish, 0);
|
|
||||||
MissionManager::saveMission(&finish, 1);
|
|
||||||
|
|
||||||
db.update(playerToDb(&finish));
|
if (sqlite3_step(stmt) != SQLITE_ROW)
|
||||||
|
{
|
||||||
|
std::cout << "[WARN] Player tried to finish tutorial on a character that already did?!" << std::endl;
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
// Lightning Gun
|
||||||
|
sql = R"(
|
||||||
|
INSERT INTO "Inventory"
|
||||||
|
("PlayerID", "Slot", "Id", "Type", "Opt")
|
||||||
|
VALUES (?, ?, ?, ?, 1);
|
||||||
|
)";
|
||||||
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
|
|
||||||
|
sqlite3_bind_int(stmt, 1, playerID);
|
||||||
|
sqlite3_bind_int(stmt, 2, 0);
|
||||||
|
sqlite3_bind_int(stmt, 3, 328);
|
||||||
|
sqlite3_bind_int(stmt, 4, 0);
|
||||||
|
|
||||||
|
if (sqlite3_step(stmt) != SQLITE_DONE)
|
||||||
|
{
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nano Buttercup
|
||||||
|
|
||||||
|
sql = R"(
|
||||||
|
INSERT INTO "Nanos"
|
||||||
|
("PlayerID", "Id", "Skill")
|
||||||
|
VALUES (?, ?, ?);
|
||||||
|
)";
|
||||||
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
|
|
||||||
|
sqlite3_bind_int(stmt, 1, playerID);
|
||||||
|
sqlite3_bind_int(stmt, 2, 1);
|
||||||
|
sqlite3_bind_int(stmt, 3, 1);
|
||||||
|
|
||||||
|
if (sqlite3_step(stmt) != SQLITE_DONE)
|
||||||
|
{
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sql = R"(
|
||||||
|
UPDATE "Players"
|
||||||
|
SET "TutorialFlag" = 1,
|
||||||
|
"Nano1" = 1,
|
||||||
|
"Quests" = ?
|
||||||
|
WHERE "PlayerID" = ?;
|
||||||
|
)";
|
||||||
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||||
|
|
||||||
|
// save missions nr 1 & 2
|
||||||
|
unsigned char questBuffer[128] = { 0 };
|
||||||
|
questBuffer[0] = 3;
|
||||||
|
sqlite3_bind_blob(stmt, 1, questBuffer, sizeof(questBuffer), 0);
|
||||||
|
sqlite3_bind_int(stmt, 2, playerID);
|
||||||
|
|
||||||
|
if (sqlite3_step(stmt) != SQLITE_DONE)
|
||||||
|
{
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Database::deleteCharacter(int characterID, int userID) {
|
int Database::deleteCharacter(int characterID, int userID) {
|
||||||
|
@ -9,11 +9,8 @@ namespace Database {
|
|||||||
|
|
||||||
struct Account {
|
struct Account {
|
||||||
int AccountID;
|
int AccountID;
|
||||||
std::string Login;
|
|
||||||
std::string Password;
|
std::string Password;
|
||||||
int Selected;
|
int Selected;
|
||||||
uint64_t Created;
|
|
||||||
uint64_t LastLogin;
|
|
||||||
};
|
};
|
||||||
struct Inventory {
|
struct Inventory {
|
||||||
int playerId;
|
int playerId;
|
||||||
@ -72,21 +69,20 @@ namespace Database {
|
|||||||
void open();
|
void open();
|
||||||
void createTables();
|
void createTables();
|
||||||
|
|
||||||
int getAccountsCount();
|
int getTableSize(std::string tableName);
|
||||||
int getPlayersCount();
|
// returns ID, 0 if something failed
|
||||||
// returns ID
|
|
||||||
int addAccount(std::string login, std::string password);
|
int addAccount(std::string login, std::string password);
|
||||||
void updateSelected(int accountId, int playerId);
|
void updateSelected(int accountId, int playerId);
|
||||||
std::unique_ptr<Account> findAccount(std::string login);
|
void findAccount(Account* account, std::string login);
|
||||||
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);
|
||||||
bool isSlotFree(int accountId, int slotNum);
|
bool isSlotFree(int accountId, int slotNum);
|
||||||
// called after chosing name, returns ID
|
// called after chosing name, returns ID
|
||||||
int createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID);
|
int createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID);
|
||||||
// called after finishing creation
|
// called after finishing creation
|
||||||
void finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character);
|
bool finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character);
|
||||||
// called after tutorial
|
// called after tutorial
|
||||||
void finishTutorial(int PlayerID);
|
bool finishTutorial(int playerID);
|
||||||
// returns slot number
|
// returns slot number
|
||||||
int deleteCharacter(int characterID, int userID);
|
int deleteCharacter(int characterID, int userID);
|
||||||
std::vector <Player> getCharacters(int userID);
|
std::vector <Player> getCharacters(int userID);
|
||||||
|
Loading…
Reference in New Issue
Block a user