mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-05 06:50:04 +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))
|
||||
return loginFail(LoginError::LOGIN_ERROR, userLogin, sock);
|
||||
|
||||
std::unique_ptr<Database::Account> findUser = Database::findAccount(userLogin);
|
||||
if (findUser == nullptr)
|
||||
Database::Account findUser = {};
|
||||
Database::findAccount(&findUser, userLogin);
|
||||
|
||||
// if 0 was returned, account was not found
|
||||
if (findUser.AccountID == 0)
|
||||
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);
|
||||
|
||||
/*
|
||||
* calling this here to timestamp login attempt,
|
||||
* 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);
|
||||
|
||||
loginSessions[sock] = CNLoginData();
|
||||
loginSessions[sock].userID = findUser->AccountID;
|
||||
loginSessions[sock].userID = findUser.AccountID;
|
||||
loginSessions[sock].lastHeartbeat = getTime();
|
||||
|
||||
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));
|
||||
|
||||
resp.iCharCount = characters.size();
|
||||
resp.iSlotNum = findUser->Selected;
|
||||
resp.iSlotNum = findUser.Selected;
|
||||
resp.iPaymentFlag = 1;
|
||||
resp.iOpenBetaFlag = 0;
|
||||
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) {
|
||||
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();
|
||||
|
||||
INITSTRUCT(sP_LS2CL_REP_LOGIN_SUCC, resp);
|
||||
|
565
src/Database.cpp
565
src/Database.cpp
@ -33,8 +33,8 @@ void Database::open() {
|
||||
createTables();
|
||||
|
||||
std::cout << "[INFO] Database in operation ";
|
||||
int accounts = getAccountsCount();
|
||||
int players = getPlayersCount();
|
||||
int accounts = getTableSize("Accounts");
|
||||
int players = getTableSize("Players");
|
||||
std::string message = "";
|
||||
if (accounts > 0) {
|
||||
message += ": Found " + std::to_string(accounts) + " Account";
|
||||
@ -59,9 +59,9 @@ void Database::createTables() {
|
||||
"AccountID" INTEGER NOT NULL,
|
||||
"Login" TEXT NOT NULL UNIQUE,
|
||||
"Password" TEXT NOT NULL,
|
||||
"Selected" INTEGER NOT NULL,
|
||||
"Created" INTEGER NOT NULL,
|
||||
"LastLogin" INTEGER NOT NULL,
|
||||
"Selected" INTEGER DEFAULT 1 NOT NULL,
|
||||
"Created" INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
"LastLogin" INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
PRIMARY KEY("AccountID" AUTOINCREMENT)
|
||||
);
|
||||
|
||||
@ -71,46 +71,46 @@ void Database::createTables() {
|
||||
"Slot" INTEGER NOT NULL,
|
||||
"Firstname" TEXT NOT NULL COLLATE NOCASE,
|
||||
"LastName" TEXT NOT NULL COLLATE NOCASE,
|
||||
"Created" INTEGER NOT NULL,
|
||||
"LastLogin" INTEGER NOT NULL,
|
||||
"Level" INTEGER NOT NULL,
|
||||
"Nano1" INTEGER NOT NULL,
|
||||
"Nano2" INTEGER NOT NULL,
|
||||
"Nano3" INTEGER NOT NULL,
|
||||
"AppearanceFlag" INTEGER NOT NULL,
|
||||
"TutorialFlag" INTEGER NOT NULL,
|
||||
"PayZoneFlag" INTEGER NOT NULL,
|
||||
"Created" INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
"LastLogin" INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
"Level" INTEGER DEFAULT 1 NOT NULL,
|
||||
"Nano1" INTEGER DEFAULT 0 NOT NULL,
|
||||
"Nano2" INTEGER DEFAULT 0 NOT NULL,
|
||||
"Nano3" INTEGER DEFAULT 0 NOT NULL,
|
||||
"AppearanceFlag" INTEGER DEFAULT 0 NOT NULL,
|
||||
"TutorialFlag" INTEGER DEFAULT 0 NOT NULL,
|
||||
"PayZoneFlag" INTEGER DEFAULT 0 NOT NULL,
|
||||
"XCoordinates" INTEGER NOT NULL,
|
||||
"YCoordinates" INTEGER NOT NULL,
|
||||
"ZCoordinates" INTEGER NOT NULL,
|
||||
"Angle" INTEGER NOT NULL,
|
||||
"HP" INTEGER NOT NULL,
|
||||
"NameCheck" INTEGER NOT NULL,
|
||||
"AccountLevel" INTEGER NOT NULL,
|
||||
"FusionMatter" INTEGER NOT NULL,
|
||||
"Taros" INTEGER NOT NULL,
|
||||
"FusionMatter" INTEGER DEFAULT 0 NOT NULL,
|
||||
"Taros" INTEGER DEFAULT 0 NOT NULL,
|
||||
"Quests" BLOB NOT NULL,
|
||||
"BatteryW" INTEGER NOT NULL,
|
||||
"BatteryN" INTEGER NOT NULL,
|
||||
"Mentor" INTEGER NOT NULL,
|
||||
"WarpLocationFlag" INTEGER NOT NULL,
|
||||
"SkywayLocationFlag1" INTEGER NOT NULL,
|
||||
"SkywayLocationFlag2" INTEGER NOT NULL,
|
||||
"CurrentMissionID" INTEGER NOT NULL,
|
||||
"BatteryW" INTEGER DEFAULT 0 NOT NULL,
|
||||
"BatteryN" INTEGER DEFAULT 0 NOT NULL,
|
||||
"Mentor" INTEGER DEFAULT 5 NOT NULL,
|
||||
"WarpLocationFlag" INTEGER DEFAULT 0 NOT NULL,
|
||||
"SkywayLocationFlag1" INTEGER DEFAULT 0 NOT NULL,
|
||||
"SkywayLocationFlag2" INTEGER DEFAULT 0 NOT NULL,
|
||||
"CurrentMissionID" INTEGER DEFAULT 0 NOT NULL,
|
||||
PRIMARY KEY("PlayerID" AUTOINCREMENT),
|
||||
FOREIGN KEY("AccountID") REFERENCES "Accounts"("AccountID") ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Appearances" (
|
||||
"PlayerID" INTEGER NOT NULL,
|
||||
"Angle" INTEGER NOT NULL,
|
||||
"Body" INTEGER NOT NULL,
|
||||
"EyeColor" INTEGER NOT NULL,
|
||||
"FaceStyle" INTEGER NOT NULL,
|
||||
"Gender" INTEGER NOT NULL,
|
||||
"HairColor" INTEGER NOT NULL,
|
||||
"HairStyle" INTEGER NOT NULL,
|
||||
"Height" INTEGER NOT NULL,
|
||||
"SkinColor" INTEGER NOT NULL,
|
||||
"Body" INTEGER DEFAULT 0 NOT NULL,
|
||||
"EyeColor" INTEGER DEFAULT 1 NOT NULL,
|
||||
"FaceStyle" INTEGER DEFAULT 1 NOT NULL,
|
||||
"Gender" INTEGER DEFAULT 1 NOT NULL,
|
||||
"HairColor" INTEGER DEFAULT 1 NOT NULL,
|
||||
"HairStyle" INTEGER DEFAULT 1 NOT NULL,
|
||||
"Height" INTEGER DEFAULT 0 NOT NULL,
|
||||
"SkinColor" INTEGER DEFAULT 1 NOT NULL,
|
||||
FOREIGN KEY("PlayerID") REFERENCES "Players"("PlayerID") ON DELETE CASCADE
|
||||
);
|
||||
|
||||
@ -120,7 +120,7 @@ void Database::createTables() {
|
||||
"Id" INTEGER NOT NULL,
|
||||
"Type" 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
|
||||
);
|
||||
|
||||
@ -128,7 +128,7 @@ void Database::createTables() {
|
||||
"PlayerId" INTEGER NOT NULL,
|
||||
"Id" 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
|
||||
);
|
||||
|
||||
@ -191,206 +191,397 @@ void Database::createTables() {
|
||||
}
|
||||
}
|
||||
|
||||
int Database::getTableSize(std::string tableName) {
|
||||
std::lock_guard<std::mutex> lock(dbCrit);
|
||||
|
||||
|
||||
int Database::getAccountsCount() {
|
||||
int result;
|
||||
char* sql = "SELECT COUNT(*) FROM Accounts";
|
||||
rc = sqlite3_exec(db, sql, CBAccountsCount, result, &err_msg);
|
||||
}
|
||||
|
||||
static int Database::CBAccountsCount(void* AccountsCount, int count, char** data, char** columns) {
|
||||
*AccountsCount = (int)data[0];
|
||||
}
|
||||
|
||||
|
||||
int Database::getPlayersCount() {
|
||||
return db.count<DbPlayer>();
|
||||
std::string query = "SELECT COUNT(*) FROM " + tableName + ";";
|
||||
const char* sql = query.c_str();
|
||||
sqlite3_stmt* stmt;
|
||||
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||
sqlite3_step(stmt);
|
||||
int result = sqlite3_column_int(stmt, 0);
|
||||
sqlite3_finalize(stmt);
|
||||
return result;
|
||||
}
|
||||
|
||||
int Database::addAccount(std::string login, std::string password) {
|
||||
std::lock_guard<std::mutex> lock(dbCrit);
|
||||
|
||||
password = BCrypt::generateHash(password);
|
||||
Account account = {};
|
||||
account.Login = login;
|
||||
account.Password = password;
|
||||
account.Selected = 1;
|
||||
account.Created = getTimestamp();
|
||||
account.LastLogin = account.Created;
|
||||
return db.insert(account);
|
||||
const char* sql = R"(
|
||||
INSERT INTO "Accounts"
|
||||
("Login", "Password")
|
||||
VALUES (?, ?);
|
||||
)";
|
||||
sqlite3_stmt* stmt;
|
||||
|
||||
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) {
|
||||
std::lock_guard<std::mutex> lock(dbCrit);
|
||||
|
||||
Account acc = db.get<Account>(accountId);
|
||||
acc.Selected = slot;
|
||||
// timestamp
|
||||
acc.LastLogin = getTimestamp();
|
||||
db.update(acc);
|
||||
if (slot < 0 || slot > 4) {
|
||||
std::cout << "[WARN] Invalid slot number passed to updateSelected()! " << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<Database::Account> Database::findAccount(std::string login) {
|
||||
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);
|
||||
}
|
||||
|
||||
void Database::findAccount(Account* account, std::string login) {
|
||||
std::lock_guard<std::mutex> lock(dbCrit);
|
||||
|
||||
// this is awful, I've tried everything to improve it
|
||||
auto find = db.get_all<Account>(
|
||||
where(c(&Account::Login) == login), limit(1));
|
||||
if (find.empty())
|
||||
return nullptr;
|
||||
return
|
||||
std::unique_ptr<Account>(new Account(find.front()));
|
||||
const char* sql = R"(
|
||||
SELECT "AccountID", "Password", "Selected"
|
||||
FROM "Accounts"
|
||||
WHERE "Login" = ?
|
||||
LIMIT 1;
|
||||
)";
|
||||
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) {
|
||||
return db.select(&DbPlayer::PlayerID,
|
||||
where((c(&DbPlayer::PlayerID) == characterID) && (c(&DbPlayer::AccountID) == userID)))
|
||||
.size() > 0;
|
||||
std::lock_guard<std::mutex> lock(dbCrit);
|
||||
|
||||
// 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) {
|
||||
std::lock_guard<std::mutex> lock(dbCrit);
|
||||
|
||||
return
|
||||
(db.get_all<DbPlayer>
|
||||
(where((c(&DbPlayer::FirstName) == firstName)
|
||||
and (c(&DbPlayer::LastName) == lastName)))
|
||||
.empty());
|
||||
const char* sql = R"(
|
||||
SELECT "PlayerID"
|
||||
FROM "Players"
|
||||
WHERE "Firstname" = ? AND "LastName" = ?
|
||||
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) {
|
||||
std::lock_guard<std::mutex> lock(dbCrit);
|
||||
|
||||
return
|
||||
(db.get_all<DbPlayer>
|
||||
(where((c(&DbPlayer::AccountID) == accountId)
|
||||
and (c(&DbPlayer::slot) == slotNum)))
|
||||
.empty());
|
||||
if (slotNum < 0 || slotNum > 4) {
|
||||
std::cout << "[WARN] Invalid slot number passed to isSlotFree()! " << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
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) {
|
||||
std::lock_guard<std::mutex> lock(dbCrit);
|
||||
|
||||
// fail if the player already has 4 or more characters
|
||||
if (db.count<DbPlayer>(where(c(&DbPlayer::AccountID) == AccountID)) >= 4)
|
||||
return -1;
|
||||
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL);
|
||||
|
||||
DbPlayer create = {};
|
||||
const char* sql = R"(
|
||||
INSERT INTO "Players"
|
||||
("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);
|
||||
|
||||
// set timestamp
|
||||
create.Created = getTimestamp();
|
||||
// save packet data
|
||||
create.FirstName = U16toU8(save->szFirstName);
|
||||
create.LastName = U16toU8(save->szLastName);
|
||||
create.slot = save->iSlotNum;
|
||||
create.AccountID = AccountID;
|
||||
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));
|
||||
|
||||
// set flags
|
||||
create.AppearanceFlag = 0;
|
||||
create.TutorialFlag = 0;
|
||||
create.PayZoneFlag = 0;
|
||||
// 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 namecheck based on setting
|
||||
if (settings::APPROVEALLNAMES || save->iFNCode)
|
||||
create.NameCheck = 1;
|
||||
else
|
||||
create.NameCheck = 0;
|
||||
// 128 byte blob for completed quests
|
||||
unsigned char blobBuffer[128];
|
||||
sqlite3_bind_blob(stmt, 12, blobBuffer, sizeof(blobBuffer), 0);
|
||||
|
||||
// create default body character
|
||||
create.Body = 0;
|
||||
create.Class = 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;
|
||||
sqlite3_step(stmt);
|
||||
|
||||
// initialize the quest blob to 128 0-bytes
|
||||
create.QuestFlag = std::vector<char>(128, 0);
|
||||
int playerId = sqlite3_last_insert_rowid(db);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return db.insert(create);
|
||||
// if something failed
|
||||
if (playerId == 0)
|
||||
return 0;
|
||||
|
||||
sql = R"(
|
||||
INSERT INTO "Appearances"
|
||||
("PlayerID")
|
||||
VALUES (?);
|
||||
)";
|
||||
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||
sqlite3_bind_int(stmt, 1, playerId);
|
||||
|
||||
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||
sqlite3_finalize(stmt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Database::finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character) {
|
||||
std::lock_guard<std::mutex> lock(dbCrit);
|
||||
|
||||
DbPlayer finish = getDbPlayerById(character->PCStyle.iPC_UID);
|
||||
finish.AppearanceFlag = 1;
|
||||
finish.Body = character->PCStyle.iBody;
|
||||
finish.Class = character->PCStyle.iClass;
|
||||
finish.EyeColor = character->PCStyle.iEyeColor;
|
||||
finish.FaceStyle = character->PCStyle.iFaceStyle;
|
||||
finish.Gender = character->PCStyle.iGender;
|
||||
finish.HairColor = character->PCStyle.iHairColor;
|
||||
finish.HairStyle = character->PCStyle.iHairStyle;
|
||||
finish.Height = character->PCStyle.iHeight;
|
||||
finish.Level = 1;
|
||||
finish.SkinColor = character->PCStyle.iSkinColor;
|
||||
db.update(finish);
|
||||
// clothes
|
||||
Inventory Foot, LB, UB;
|
||||
Foot.playerId = character->PCStyle.iPC_UID;
|
||||
Foot.id = character->sOn_Item.iEquipFootID;
|
||||
Foot.Type = 3;
|
||||
Foot.slot = 3;
|
||||
Foot.Opt = 1;
|
||||
Foot.TimeLimit = 0;
|
||||
db.insert(Foot);
|
||||
LB.playerId = character->PCStyle.iPC_UID;
|
||||
LB.id = character->sOn_Item.iEquipLBID;
|
||||
LB.Type = 2;
|
||||
LB.slot = 2;
|
||||
LB.Opt = 1;
|
||||
LB.TimeLimit = 0;
|
||||
db.insert(LB);
|
||||
UB.playerId = character->PCStyle.iPC_UID;
|
||||
UB.id = character->sOn_Item.iEquipUBID;
|
||||
UB.Type = 1;
|
||||
UB.slot = 1;
|
||||
UB.Opt = 1;
|
||||
UB.TimeLimit = 0;
|
||||
db.insert(UB);
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL);
|
||||
return playerId;
|
||||
}
|
||||
|
||||
void Database::finishTutorial(int PlayerID) {
|
||||
bool Database::finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character) {
|
||||
std::lock_guard<std::mutex> lock(dbCrit);
|
||||
|
||||
Player finish = getPlayer(PlayerID);
|
||||
// set flag
|
||||
finish.PCStyle2.iTutorialFlag= 1;
|
||||
// add Gun
|
||||
Inventory LightningGun = {};
|
||||
LightningGun.playerId = PlayerID;
|
||||
LightningGun.id = 328;
|
||||
LightningGun.slot = 0;
|
||||
LightningGun.Type = 0;
|
||||
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);
|
||||
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);
|
||||
|
||||
db.update(playerToDb(&finish));
|
||||
if (sqlite3_step(stmt) != SQLITE_ROW)
|
||||
{
|
||||
std::cout << "[WARN] Player tried Character Creation on already existing character?!" << std::endl;
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL);
|
||||
|
||||
sql = R"(
|
||||
UPDATE "Players"
|
||||
SET "AppearanceFlag" = 1
|
||||
WHERE "PlayerID" = ?;
|
||||
)";
|
||||
|
||||
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||
sqlite3_bind_int(stmt, 1, character->PCStyle.iPC_UID);
|
||||
if (sqlite3_step(stmt) != SQLITE_DONE)
|
||||
{
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
sql = R"(
|
||||
UPDATE "Appearances"
|
||||
SET
|
||||
"Body" = ? ,
|
||||
"EyeColor" = ? ,
|
||||
"FaceStyle" = ? ,
|
||||
"Gender" = ? ,
|
||||
"HairColor" = ? ,
|
||||
"HairStyle" = ? ,
|
||||
"Height" = ? ,
|
||||
"SkinColor" = ?
|
||||
WHERE "PlayerID" = ? ;
|
||||
)";
|
||||
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;
|
||||
}
|
||||
|
||||
bool Database::finishTutorial(int playerID) {
|
||||
std::lock_guard<std::mutex> lock(dbCrit);
|
||||
|
||||
const char* sql = R"(
|
||||
SELECT "PlayerID"
|
||||
FROM "Players"
|
||||
WHERE "PlayerID" = ? AND "TutorialFlag" = 0
|
||||
LIMIT 1;
|
||||
)";
|
||||
sqlite3_stmt* stmt;
|
||||
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
||||
sqlite3_bind_int(stmt, 1, playerID);
|
||||
|
||||
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) {
|
||||
|
@ -9,11 +9,8 @@ namespace Database {
|
||||
|
||||
struct Account {
|
||||
int AccountID;
|
||||
std::string Login;
|
||||
std::string Password;
|
||||
int Selected;
|
||||
uint64_t Created;
|
||||
uint64_t LastLogin;
|
||||
};
|
||||
struct Inventory {
|
||||
int playerId;
|
||||
@ -72,21 +69,20 @@ namespace Database {
|
||||
void open();
|
||||
void createTables();
|
||||
|
||||
int getAccountsCount();
|
||||
int getPlayersCount();
|
||||
// returns ID
|
||||
int getTableSize(std::string tableName);
|
||||
// returns ID, 0 if something failed
|
||||
int addAccount(std::string login, std::string password);
|
||||
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 isNameFree(std::string firstName, std::string lastName);
|
||||
bool isSlotFree(int accountId, int slotNum);
|
||||
// called after chosing name, returns ID
|
||||
int createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID);
|
||||
// called after finishing creation
|
||||
void finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character);
|
||||
bool finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character);
|
||||
// called after tutorial
|
||||
void finishTutorial(int PlayerID);
|
||||
bool finishTutorial(int playerID);
|
||||
// returns slot number
|
||||
int deleteCharacter(int characterID, int userID);
|
||||
std::vector <Player> getCharacters(int userID);
|
||||
|
Loading…
Reference in New Issue
Block a user