OpenFusion/src/Database.cpp

1272 lines
41 KiB
C++
Raw Normal View History

#include "Database.hpp"
#include "Database.hpp"
#include "contrib/bcrypt/BCrypt.hpp"
#include "CNProtocol.hpp"
#include <string>
#include "contrib/JSON.hpp"
2020-11-28 23:19:07 +00:00
#include "contrib/sqlite/sqlite3.h"
#include "CNStructs.hpp"
#include "settings.hpp"
#include "Player.hpp"
#include "CNStructs.hpp"
2020-09-13 18:45:51 +00:00
#include "MissionManager.hpp"
#if defined(__MINGW32__) && !defined(_GLIBCXX_HAS_GTHREADS)
#include "mingw/mingw.mutex.h"
#else
#include <mutex>
#endif
std::mutex dbCrit;
2020-11-28 23:31:00 +00:00
sqlite3* db;
#pragma region LoginServer
void Database::open() {
2020-11-28 23:31:00 +00:00
int rc = sqlite3_open(settings::DBPATH.c_str(), &db);
if (rc != SQLITE_OK) {
std::cout << "[FATAL] Cannot open database: " << sqlite3_errmsg(db) << std::endl;
terminate(0);
}
2020-11-30 12:44:43 +00:00
createTables();
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
std::cout << "[INFO] Database in operation ";
int accounts = getTableSize("Accounts");
int players = getTableSize("Players");
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
std::string message = "";
if (accounts > 0) {
message += ": Found " + std::to_string(accounts) + " Account";
if (accounts > 1)
message += "s";
}
if (players > 0) {
message += " and " + std::to_string(players) + " Player Character";
if (players > 1)
message += "s";
}
std::cout << message << std::endl;
}
2020-11-30 12:44:43 +00:00
void Database::createTables() {
char* errMsg = 0;
char* sql;
sql = R"(
CREATE TABLE IF NOT EXISTS "Accounts" (
"AccountID" INTEGER NOT NULL,
"Login" TEXT NOT NULL UNIQUE,
"Password" TEXT NOT NULL,
"Selected" INTEGER DEFAULT 1 NOT NULL,
"Created" INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
"LastLogin" INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
2020-11-30 12:44:43 +00:00
PRIMARY KEY("AccountID" AUTOINCREMENT)
);
CREATE TABLE IF NOT EXISTS "Players" (
2020-11-30 12:44:43 +00:00
"PlayerID" INTEGER NOT NULL,
"AccountID" INTEGER NOT NULL,
"Slot" INTEGER NOT NULL,
"Firstname" TEXT NOT NULL COLLATE NOCASE,
"LastName" TEXT NOT NULL COLLATE NOCASE,
"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,
2020-11-30 12:44:43 +00:00
"XCoordinates" INTEGER NOT NULL,
"YCoordinates" INTEGER NOT NULL,
"ZCoordinates" INTEGER NOT NULL,
"Angle" INTEGER NOT NULL,
2020-11-30 12:44:43 +00:00
"HP" INTEGER NOT NULL,
"NameCheck" INTEGER NOT NULL,
"AccountLevel" INTEGER NOT NULL,
"FusionMatter" INTEGER DEFAULT 0 NOT NULL,
"Taros" INTEGER DEFAULT 0 NOT NULL,
2020-11-30 12:44:43 +00:00
"Quests" BLOB 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,
2020-11-30 12:44:43 +00:00
PRIMARY KEY("PlayerID" AUTOINCREMENT),
FOREIGN KEY("AccountID") REFERENCES "Accounts"("AccountID") ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS "Appearances" (
"PlayerID" 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,
2020-11-30 12:44:43 +00:00
FOREIGN KEY("PlayerID") REFERENCES "Players"("PlayerID") ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS "Inventory" (
"PlayerId" INTEGER NOT NULL,
"Slot" INTEGER NOT NULL,
"Id" INTEGER NOT NULL,
"Type" INTEGER NOT NULL,
"Opt" INTEGER NOT NULL,
"TimeLimit" INTEGER NOT DEFALUT 0 NULL,
2020-11-30 12:44:43 +00:00
FOREIGN KEY("PlayerID") REFERENCES "Players"("PlayerID") ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS "Nanos" (
"PlayerId" INTEGER NOT NULL,
"Id" INTEGER NOT NULL,
"Skill" INTEGER NOT NULL,
"Stamina" INTEGER DEFAULT = 150 NOT NULL,
2020-11-30 12:44:43 +00:00
FOREIGN KEY("PlayerID") REFERENCES "Players"("PlayerID") ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS "RunningQuests" (
"PlayerId" INTEGER NOT NULL,
"TaskId" INTEGER NOT NULL,
"RemainingNPCCount1" INTEGER NOT NULL,
"RemainingNPCCount2" INTEGER NOT NULL,
"RemainingNPCCount3" INTEGER NOT NULL,
FOREIGN KEY("PlayerID") REFERENCES "Players"("PlayerID") ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS "Buddyships" (
"PlayerAId" INTEGER NOT NULL,
"PlayerBId" INTEGER NOT NULL,
"Status" INTEGER NOT NULL,
FOREIGN KEY("PlayerAId") REFERENCES "Players"("PlayerID") ON DELETE CASCADE,
FOREIGN KEY("PlayerBId") REFERENCES "Players"("PlayerID") ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS "EmailData" (
"PlayerId" INTEGER NOT NULL,
"MsgIndex" INTEGER NOT NULL,
"ReadFlag" INTEGER NOT NULL,
"ItemFlag" INTEGER NOT NULL,
"SenderId" INTEGER NOT NULL,
"SenderFirstName" TEXT NOT NULL COLLATE NOCASE,
"SenderLastName" TEXT NOT NULL COLLATE NOCASE,
"SubjectLine" TEXT NOT NULL,
"MsgBody" TEXT NOT NULL,
"Taros" INTEGER NOT NULL,
"SendTime" INTEGER NOT NULL,
"DeleteTime" INTEGER NOT NULL,
FOREIGN KEY("PlayerID") REFERENCES "Players"("PlayerID") ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS "EmailItems" (
"PlayerId" INTEGER NOT NULL,
"MsgIndex" INTEGER NOT NULL,
"Slot" INTEGER NOT NULL,
"Id" INTEGER NOT NULL,
"Type" INTEGER NOT NULL,
"Opt" INTEGER NOT NULL,
"TimeLimit" INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS "RaceResults"(
"EPID" INTEGER NOT NULL,
"PlayerID" INTEGER NOT NULL,
"Score" INTEGER NOT NULL,
"Timestamp" INTEGER NOT NULL,
FOREIGN KEY("PlayerID") REFERENCES "Players"("PlayerID") ON DELETE CASCADE
)
)";
int rc = sqlite3_exec(db, sql, 0, 0, &errMsg);
if (rc != SQLITE_OK) {
std::cout << "[FATAL] Database failed to create tables: " << errMsg << std::endl;
terminate(0);
}
}
int Database::getTableSize(std::string tableName) {
std::lock_guard<std::mutex> lock(dbCrit);
2020-11-30 12:44:43 +00:00
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);
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);
if (slot < 0 || slot > 4) {
std::cout << "[WARN] Invalid slot number passed to updateSelected()! " << std::endl;
return;
}
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);
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) {
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);
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);
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);
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL);
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);
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));
// 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);
// 128 byte blob for completed quests
unsigned char blobBuffer[128];
sqlite3_bind_blob(stmt, 12, blobBuffer, sizeof(blobBuffer), 0);
sqlite3_step(stmt);
int playerId = sqlite3_last_insert_rowid(db);
sqlite3_finalize(stmt);
// 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;
}
sqlite3_finalize(stmt);
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL);
return playerId;
}
bool Database::finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character) {
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);
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) {
std::lock_guard<std::mutex> lock(dbCrit);
2020-09-02 15:53:39 +00:00
auto find =
db.get_all<DbPlayer>(where(c(&DbPlayer::PlayerID) == characterID and c(&DbPlayer::AccountID)==userID));
2020-09-02 15:53:39 +00:00
int slot = find.front().slot;
db.remove<DbPlayer>(find.front().PlayerID);
db.remove_all<Inventory>(where(c(&Inventory::playerId) == characterID));
db.remove_all<Nano>(where(c(&Nano::playerId) == characterID));
2020-09-02 15:53:39 +00:00
return slot;
}
std::vector <Player> Database::getCharacters(int UserID) {
std::lock_guard<std::mutex> lock(dbCrit);
2020-09-02 15:53:39 +00:00
std::vector<DbPlayer>characters =
db.get_all<DbPlayer>(where
(c(&DbPlayer::AccountID) == UserID));
// parsing DbPlayer to Player
2020-09-02 15:53:39 +00:00
std::vector<Player> result = std::vector<Player>();
for (auto &character : characters) {
Player toadd = DbToPlayer(character);
2020-09-02 15:53:39 +00:00
result.push_back(
toadd
);
}
return result;
}
std::vector <sP_LS2CL_REP_CHAR_INFO> Database::getCharInfo(int userID) {
std::lock_guard<std::mutex> lock(dbCrit);
std::vector<DbPlayer>characters =
db.get_all<DbPlayer>(where
(c(&DbPlayer::AccountID) == userID));
std::vector<sP_LS2CL_REP_CHAR_INFO> result = std::vector<sP_LS2CL_REP_CHAR_INFO>();
for (auto& character : characters) {
sP_LS2CL_REP_CHAR_INFO toAdd = {};
toAdd.iX = character.x_coordinates;
toAdd.iY = character.y_coordinates;
toAdd.iZ = character.z_coordinates;
toAdd.iLevel = character.Level;
toAdd.iSlot = character.slot;
toAdd.sPC_Style.iBody = character.Body;
toAdd.sPC_Style.iClass = character.Class;
toAdd.sPC_Style.iEyeColor = character.EyeColor;
toAdd.sPC_Style.iFaceStyle = character.FaceStyle;
toAdd.sPC_Style.iGender = character.Gender;
toAdd.sPC_Style.iHairColor = character.HairColor;
toAdd.sPC_Style.iHairStyle = character.HairStyle;
toAdd.sPC_Style.iHeight = character.Height;
toAdd.sPC_Style.iNameCheck = character.NameCheck;
toAdd.sPC_Style.iPC_UID = character.PlayerID;
toAdd.sPC_Style.iSkinColor = character.SkinColor;
U8toU16(character.FirstName, toAdd.sPC_Style.szFirstName, sizeof(toAdd.sPC_Style.szFirstName));
U8toU16(character.LastName, toAdd.sPC_Style.szLastName, sizeof(toAdd.sPC_Style.szLastName));
toAdd.sPC_Style2.iAppearanceFlag = character.AppearanceFlag;
toAdd.sPC_Style2.iPayzoneFlag = character.PayZoneFlag;
toAdd.sPC_Style2.iTutorialFlag = character.TutorialFlag;
//get equipment
auto items = db.get_all<Inventory>(
where(c(&Inventory::playerId) == character.PlayerID && c(&Inventory::slot) < AEQUIP_COUNT));
for (auto& item : items) {
sItemBase addItem = {};
addItem.iID = item.id;
addItem.iType = item.Type;
addItem.iOpt = item.Opt;
addItem.iTimeLimit = item.TimeLimit;
toAdd.aEquip[item.slot] = addItem;
}
result.push_back(toAdd);
}
return result;
}
// XXX: This is never called?
void Database::evaluateCustomName(int characterID, CustomName decision) {
std::lock_guard<std::mutex> lock(dbCrit);
2020-09-02 15:53:39 +00:00
DbPlayer player = getDbPlayerById(characterID);
player.NameCheck = (int)decision;
db.update(player);
}
void Database::changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save) {
std::lock_guard<std::mutex> lock(dbCrit);
2020-09-02 15:53:39 +00:00
DbPlayer Player = getDbPlayerById(save->iPCUID);
Player.FirstName = U16toU8(save->szFirstName);
Player.LastName = U16toU8(save->szLastName);
if (settings::APPROVEALLNAMES || save->iFNCode)
Player.NameCheck = 1;
else
Player.NameCheck = 0;
db.update(Player);
}
Database::DbPlayer Database::playerToDb(Player *player) {
// TODO: move stuff that is never updated to separate table so it doesn't try to update it every time
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
DbPlayer result = {};
2020-09-03 20:29:29 +00:00
result.PlayerID = player->iID;
result.AccountID = player->accountId;
result.AppearanceFlag = player->PCStyle2.iAppearanceFlag;
result.Body = player->PCStyle.iBody;
result.Class = player->PCStyle.iClass;
result.EyeColor = player->PCStyle.iEyeColor;
result.FaceStyle = player->PCStyle.iFaceStyle;
result.FirstName = U16toU8( player->PCStyle.szFirstName);
result.FusionMatter = player->fusionmatter;
result.Gender = player->PCStyle.iGender;
result.HairColor = player->PCStyle.iHairColor;
result.HairStyle = player->PCStyle.iHairStyle;
result.Height = player->PCStyle.iHeight;
result.HP = player->HP;
result.AccountLevel = player->accountLevel;
result.LastName = U16toU8(player->PCStyle.szLastName);
result.Level = player->level;
result.NameCheck = player->PCStyle.iNameCheck;
result.PayZoneFlag = player->PCStyle2.iPayzoneFlag;
result.PlayerID = player->PCStyle.iPC_UID;
result.SkinColor = player->PCStyle.iSkinColor;
result.slot = player->slot;
result.Taros = player->money;
result.TutorialFlag = player->PCStyle2.iTutorialFlag;
2020-11-26 15:01:48 +00:00
if (player->instanceID == 0 && !player->onMonkey) { // only save coords if player isn't instanced
result.x_coordinates = player->x;
result.y_coordinates = player->y;
result.z_coordinates = player->z;
result.angle = player->angle;
} else {
result.x_coordinates = player->lastX;
result.y_coordinates = player->lastY;
result.z_coordinates = player->lastZ;
result.angle = player->lastAngle;
}
result.Nano1 = player->equippedNanos[0];
result.Nano2 = player->equippedNanos[1];
result.Nano3 = player->equippedNanos[2];
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
result.BatteryN = player->batteryN;
result.BatteryW = player->batteryW;
result.Mentor = player->mentor;
result.WarpLocationFlag = player->iWarpLocationFlag;
result.SkywayLocationFlag1 = player->aSkywayLocationFlag[0];
result.SkywayLocationFlag2 = player->aSkywayLocationFlag[1];
result.CurrentMissionID = player->CurrentMissionID;
// timestamp
result.LastLogin = getTimestamp();
result.Created = player->creationTime;
2020-09-13 18:45:51 +00:00
// save completed quests
result.QuestFlag = std::vector<char>((char*)player->aQuestFlag, (char*)player->aQuestFlag + 128);
2020-09-02 15:53:39 +00:00
return result;
}
Player Database::DbToPlayer(DbPlayer player) {
2020-09-03 20:29:29 +00:00
Player result = {}; // fixes some weird memory errors, this zeros out the members (not the padding inbetween though)
result.iID = player.PlayerID;
2020-09-02 15:53:39 +00:00
result.accountId = player.AccountID;
result.creationTime = player.Created;
2020-09-02 15:53:39 +00:00
result.PCStyle2.iAppearanceFlag = player.AppearanceFlag;
result.PCStyle.iBody = player.Body;
result.PCStyle.iClass = player.Class;
result.PCStyle.iEyeColor = player.EyeColor;
result.PCStyle.iFaceStyle = player.FaceStyle;
2020-10-04 17:50:58 +00:00
U8toU16(player.FirstName, result.PCStyle.szFirstName, sizeof(result.PCStyle.szFirstName));
2020-09-02 15:53:39 +00:00
result.PCStyle.iGender = player.Gender;
result.PCStyle.iHairColor = player.HairColor;
result.PCStyle.iHairStyle = player.HairStyle;
result.PCStyle.iHeight = player.Height;
result.HP = player.HP;
result.accountLevel = player.AccountLevel;
2020-10-04 17:50:58 +00:00
U8toU16(player.LastName, result.PCStyle.szLastName, sizeof(result.PCStyle.szLastName));
2020-09-02 15:53:39 +00:00
result.level = player.Level;
result.PCStyle.iNameCheck = player.NameCheck;
result.PCStyle2.iPayzoneFlag = player.PayZoneFlag;
result.iID = player.PlayerID;
result.PCStyle.iPC_UID = player.PlayerID;
result.PCStyle.iSkinColor = player.SkinColor;
result.slot = player.slot;
result.PCStyle2.iTutorialFlag = player.TutorialFlag;
result.x = player.x_coordinates;
result.y = player.y_coordinates;
result.z = player.z_coordinates;
result.angle = player.angle;
result.money = player.Taros;
result.fusionmatter = player.FusionMatter;
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
result.batteryN = player.BatteryN;
result.batteryW = player.BatteryW;
result.mentor = player.Mentor;
result.CurrentMissionID = player.CurrentMissionID;
2020-09-07 00:16:44 +00:00
result.equippedNanos[0] = player.Nano1;
result.equippedNanos[1] = player.Nano2;
result.equippedNanos[2] = player.Nano3;
result.inCombat = false;
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
result.iWarpLocationFlag = player.WarpLocationFlag;
result.aSkywayLocationFlag[0] = player.SkywayLocationFlag1;
result.aSkywayLocationFlag[1] = player.SkywayLocationFlag2;
2020-09-07 00:16:44 +00:00
Database::getInventory(&result);
Database::removeExpiredVehicles(&result);
2020-09-07 00:16:44 +00:00
Database::getNanos(&result);
Database::getQuests(&result);
2020-11-08 17:42:27 +00:00
Database::getBuddies(&result);
2020-09-13 18:45:51 +00:00
// load completed quests
memcpy(&result.aQuestFlag, player.QuestFlag.data(), std::min(sizeof(result.aQuestFlag), player.QuestFlag.size()));
2020-09-13 18:45:51 +00:00
2020-09-02 15:53:39 +00:00
return result;
}
Database::DbPlayer Database::getDbPlayerById(int id) {
auto player = db.get_all<DbPlayer>(where(c(&DbPlayer::PlayerID) == id));
if (player.size() < 1) {
// garbage collection
db.remove_all<Inventory>(where(c(&Inventory::playerId) == id));
db.remove_all<Nano>(where(c(&Nano::playerId) == id));
db.remove_all<DbQuest>(where(c(&DbQuest::PlayerId) == id));
db.remove_all<Buddyship>(where(c(&Buddyship::PlayerAId) == id || c(&Buddyship::PlayerBId) == id));
db.remove_all<EmailData>(where(c(&EmailData::PlayerId) == id));
db.remove_all<EmailItem>(where(c(&EmailItem::PlayerId) == id));
return DbPlayer{ -1 };
}
return player.front();
}
2020-09-07 00:16:44 +00:00
Player Database::getPlayer(int id) {
return DbToPlayer(
getDbPlayerById(id)
);
}
#pragma endregion LoginServer
#pragma region ShardServer
void Database::updatePlayer(Player *player) {
std::lock_guard<std::mutex> lock(dbCrit);
DbPlayer toUpdate = playerToDb(player);
db.update(toUpdate);
2020-09-07 00:16:44 +00:00
updateInventory(player);
updateNanos(player);
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
updateQuests(player);
//updateBuddies(player); we add/remove buddies explicitly now
2020-09-07 00:16:44 +00:00
}
void Database::updateInventory(Player *player){
// start transaction
2020-09-07 00:16:44 +00:00
db.begin_transaction();
// remove all
2020-09-07 00:16:44 +00:00
db.remove_all<Inventory>(
where(c(&Inventory::playerId) == player->iID)
2020-09-07 00:16:44 +00:00
);
// insert equip
2020-09-07 00:16:44 +00:00
for (int i = 0; i < AEQUIP_COUNT; i++) {
if (player->Equip[i].iID != 0) {
sItemBase* next = &player->Equip[i];
2020-09-07 00:16:44 +00:00
Inventory toAdd = {};
toAdd.playerId = player->iID;
2020-09-07 00:16:44 +00:00
toAdd.slot = i;
toAdd.id = next->iID;
toAdd.Opt = next->iOpt;
toAdd.Type = next->iType;
toAdd.TimeLimit = next->iTimeLimit;
db.insert(toAdd);
}
}
// insert inventory
2020-09-07 00:16:44 +00:00
for (int i = 0; i < AINVEN_COUNT; i++) {
if (player->Inven[i].iID != 0) {
sItemBase* next = &player->Inven[i];
2020-09-07 00:16:44 +00:00
Inventory toAdd = {};
toAdd.playerId = player->iID;
2020-09-07 00:16:44 +00:00
toAdd.slot = i + AEQUIP_COUNT;
toAdd.id = next->iID;
toAdd.Opt = next->iOpt;
toAdd.Type = next->iType;
toAdd.TimeLimit = next->iTimeLimit;
db.insert(toAdd);
}
}
// insert bank
for (int i = 0; i < ABANK_COUNT; i++) {
if (player->Bank[i].iID != 0) {
sItemBase* next = &player->Bank[i];
Inventory toAdd = {};
toAdd.playerId = player->iID;
toAdd.slot = i + AEQUIP_COUNT + AINVEN_COUNT;
toAdd.id = next->iID;
toAdd.Opt = next->iOpt;
toAdd.Type = next->iType;
toAdd.TimeLimit = next->iTimeLimit;
db.insert(toAdd);
}
}
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
// insert quest items
for (int i = 0; i < AQINVEN_COUNT; i++) {
if (player->QInven[i].iID != 0) {
sItemBase* next = &player->QInven[i];
Inventory toAdd = {};
toAdd.playerId = player->iID;
toAdd.slot = i + AEQUIP_COUNT + AINVEN_COUNT + ABANK_COUNT;
toAdd.id = next->iID;
toAdd.Opt = next->iOpt;
toAdd.Type = next->iType;
toAdd.TimeLimit = next->iTimeLimit;
db.insert(toAdd);
}
}
2020-09-07 00:16:44 +00:00
db.commit();
}
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
void Database::updateNanos(Player *player) {
// start transaction
2020-09-07 00:16:44 +00:00
db.begin_transaction();
// remove all
2020-09-07 00:16:44 +00:00
db.remove_all<Nano>(
where(c(&Nano::playerId) == player->iID)
2020-09-07 00:16:44 +00:00
);
// insert
for (int i=1; i < SIZEOF_NANO_BANK_SLOT; i++) {
if ((player->Nanos[i]).iID == 0)
continue;
2020-09-07 00:16:44 +00:00
Nano toAdd = {};
sNano* next = &player->Nanos[i];
toAdd.playerId = player->iID;
2020-09-07 00:16:44 +00:00
toAdd.iID = next->iID;
toAdd.iSkillID = next->iSkillID;
toAdd.iStamina = next->iStamina;
db.insert(toAdd);
}
db.commit();
}
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
void Database::updateQuests(Player* player) {
// start transaction
db.begin_transaction();
// remove all
db.remove_all<DbQuest>(
where(c(&DbQuest::PlayerId) == player->iID)
);
// insert
for (int i = 0; i < ACTIVE_MISSION_COUNT; i++) {
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
if (player->tasks[i] == 0)
continue;
DbQuest toAdd = {};
toAdd.PlayerId = player->iID;
toAdd.TaskId = player->tasks[i];
toAdd.RemainingNPCCount1 = player->RemainingNPCCount[i][0];
toAdd.RemainingNPCCount2 = player->RemainingNPCCount[i][1];
toAdd.RemainingNPCCount3 = player->RemainingNPCCount[i][2];
db.insert(toAdd);
}
db.commit();
}
// note: do not use. explicitly add/remove records instead.
2020-11-08 17:42:27 +00:00
void Database::updateBuddies(Player* player) {
db.begin_transaction();
db.remove_all<Buddyship>( // remove all buddyships with this player involved
where(c(&Buddyship::PlayerAId) == player->iID || c(&Buddyship::PlayerBId) == player->iID)
);
// iterate through player's buddies and add records for each non-zero entry
for (int i = 0; i < 50; i++) {
if (player->buddyIDs[i] != 0) {
Buddyship record;
record.PlayerAId = player->iID;
record.PlayerBId = player->buddyIDs[i];
record.Status = 0; // still not sure how we'll handle blocking
db.insert(record);
}
}
db.commit();
}
2020-09-07 00:16:44 +00:00
void Database::getInventory(Player* player) {
// get items from DB
2020-09-07 00:16:44 +00:00
auto items = db.get_all<Inventory>(
where(c(&Inventory::playerId) == player->iID)
);
// set items
2020-09-07 00:16:44 +00:00
for (const Inventory &current : items) {
sItemBase toSet = {};
toSet.iID = current.id;
toSet.iType = current.Type;
toSet.iOpt = current.Opt;
toSet.iTimeLimit = current.TimeLimit;
// assign to proper arrays
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
if (current.slot < AEQUIP_COUNT)
player->Equip[current.slot] = toSet;
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
else if (current.slot < (AEQUIP_COUNT + AINVEN_COUNT))
2020-09-07 00:16:44 +00:00
player->Inven[current.slot - AEQUIP_COUNT] = toSet;
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
else if (current.slot < (AEQUIP_COUNT + AINVEN_COUNT + ABANK_COUNT))
player->Bank[current.slot - AEQUIP_COUNT - AINVEN_COUNT] = toSet;
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
else
player->QInven[current.slot - AEQUIP_COUNT - AINVEN_COUNT - ABANK_COUNT] = toSet;
2020-09-07 00:16:44 +00:00
}
}
void Database::removeExpiredVehicles(Player* player) {
int32_t currentTime = getTimestamp();
// remove from bank immediately
for (int i = 0; i < ABANK_COUNT; i++) {
if (player->Bank[i].iType == 10 && player->Bank[i].iTimeLimit < currentTime)
player->Bank[i] = {};
}
// for the rest, we want to leave only 1 expired vehicle on player to delete it with the client packet
std::vector<sItemBase*> toRemove;
// equiped vehicle
if (player->Equip[8].iOpt > 0 && player->Equip[8].iTimeLimit < currentTime) {
toRemove.push_back(&player->Equip[8]);
player->toRemoveVehicle.eIL = 0;
player->toRemoveVehicle.iSlotNum = 8;
}
// inventory
for (int i = 0; i < AINVEN_COUNT; i++) {
if (player->Inven[i].iType == 10 && player->Inven[i].iTimeLimit < currentTime) {
toRemove.push_back(&player->Inven[i]);
player->toRemoveVehicle.eIL = 1;
player->toRemoveVehicle.iSlotNum = i;
}
}
// delete all but one vehicles, leave last one for ceremonial deletion
2020-09-22 11:41:28 +00:00
for (int i = 0; i < (int)toRemove.size()-1; i++) {
memset(toRemove[i], 0, sizeof(sItemBase));
}
2020-09-07 00:16:44 +00:00
}
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
2020-09-07 00:16:44 +00:00
void Database::getNanos(Player* player) {
// get from DB
2020-09-07 00:16:44 +00:00
auto nanos = db.get_all<Nano>(
where(c(&Nano::playerId) == player->iID)
);
// set
2020-09-07 00:16:44 +00:00
for (const Nano& current : nanos) {
sNano *toSet = &player->Nanos[current.iID];
toSet->iID = current.iID;
toSet->iSkillID = current.iSkillID;
toSet->iStamina = current.iStamina;
}
}
Database saving update (#104) * implemented saving BatteryN and BatteryW * implemented saving mentor * moved int64->blob parsing to a separate function * moved parsing blob->int64 to a separate function * added functions for parsing int32->blob and vice versa * added functions for parsing int16->blob and vice versa * WIP saving quest items and active tasks * Quest items are stored in inventory table instead of blob * added sanity check for missionId * saving active missions works * removed unneccesary include * implemented saving warplocationflag, skywaylocationflag and currentmissionid in database * INFO DB message now shows how many accounts and player characters are in the database * fixed dbsaveinterval being in [login] instead of [shard] * fixed mission quit: - fixed wrong json name, causing qitems not deleting properly - quitting mission now resets npc kill count * adjusted saving active missions * removed blob parsing functions that ended up being unused * removed accidentaly added include * removed sending PCStyle2 on Player Enter * added a sanity check in itemMoveHandler * removed MapNum from PCLoad, as client doesn't even read it * set BuddyWarpCooldown to 60s on PCLoad * fixed a bug causing EXIT DUPLICATE not working * added creation and last login timestamps to accounts and players * added a sanity check for P_CL2LS_REQ_PC_EXIT_DUPLICATE * implemented web api support, toggled by new setting (off by default) * add usewebapi to config Co-authored-by: Gent <gentsemaj@live.com>
2020-09-21 19:43:53 +00:00
void Database::getQuests(Player* player) {
// get from DB
auto quests = db.get_all<DbQuest>(
where(c(&DbQuest::PlayerId) == player->iID)
);
// set
int i = 0;
for (const DbQuest& current : quests) {
player->tasks[i] = current.TaskId;
player->RemainingNPCCount[i][0] = current.RemainingNPCCount1;
player->RemainingNPCCount[i][1] = current.RemainingNPCCount2;
player->RemainingNPCCount[i][2] = current.RemainingNPCCount3;
i++;
}
}
2020-11-08 17:42:27 +00:00
void Database::getBuddies(Player* player) {
auto buddies = db.get_all<Buddyship>( // player can be on either side
where(c(&Buddyship::PlayerAId) == player->iID || c(&Buddyship::PlayerBId) == player->iID)
);
// there should never be more than 50 buddyships per player, but just in case
for (int i = 0; i < 50 && i < buddies.size(); i++) {
// if the player is player A, then the buddy is player B, and vice versa
player->buddyIDs[i] = player->iID == buddies.at(i).PlayerAId
? buddies.at(i).PlayerBId : buddies.at(i).PlayerAId;
}
}
int Database::getNumBuddies(Player* player) {
std::lock_guard<std::mutex> lock(dbCrit);
auto buddies = db.get_all<Buddyship>( // player can be on either side
where(c(&Buddyship::PlayerAId) == player->iID || c(&Buddyship::PlayerBId) == player->iID)
);
// again, for peace of mind
return buddies.size() > 50 ? 50 : buddies.size();
}
// buddies
void Database::addBuddyship(int playerA, int playerB) {
std::lock_guard<std::mutex> lock(dbCrit);
db.begin_transaction();
Buddyship record;
record.PlayerAId = playerA;
record.PlayerBId = playerB;
record.Status = 0; // blocking ???
db.insert(record);
db.commit();
}
void Database::removeBuddyship(int playerA, int playerB) {
std::lock_guard<std::mutex> lock(dbCrit);
db.begin_transaction();
db.remove_all<Buddyship>(
where(c(&Buddyship::PlayerAId) == playerA && c(&Buddyship::PlayerBId) == playerB)
);
db.remove_all<Buddyship>( // the pair could be in either position
where(c(&Buddyship::PlayerAId) == playerB && c(&Buddyship::PlayerBId) == playerA)
);
db.commit();
}
// email
int Database::getUnreadEmailCount(int playerID) {
std::lock_guard<std::mutex> lock(dbCrit);
auto emailData = db.get_all<Database::EmailData>(
where(c(&Database::EmailData::PlayerId) == playerID && c(&Database::EmailData::ReadFlag) == 0)
);
return emailData.size();
}
std::vector<Database::EmailData> Database::getEmails(int playerID, int page) {
std::lock_guard<std::mutex> lock(dbCrit);
std::vector<Database::EmailData> emails;
auto emailData = db.get_all<Database::EmailData>(
where(c(&Database::EmailData::PlayerId) == playerID),
order_by(&Database::EmailData::MsgIndex).desc(),
limit(5 * (page - 1), 5)
);
int i = 0;
for (Database::EmailData email : emailData) {
emails.push_back(email);
i++;
}
return emails;
}
Database::EmailData Database::getEmail(int playerID, int index) {
std::lock_guard<std::mutex> lock(dbCrit);
auto emailData = db.get_all<Database::EmailData>(
where(c(&Database::EmailData::PlayerId) == playerID && c(&Database::EmailData::MsgIndex) == index)
);
if (emailData.size() > 1)
std::cout << "[WARN] Duplicate emails detected (player " << playerID << ", index " << index << ")" << std::endl;
return emailData.at(0);
}
sItemBase* Database::getEmailAttachments(int playerID, int index) {
std::lock_guard<std::mutex> lock(dbCrit);
sItemBase* items = new sItemBase[4];
for (int i = 0; i < 4; i++)
items[i] = { 0, 0, 0, 0 }; // zero out items
auto attachments = db.get_all<Database::EmailItem>(
where(c(&Database::EmailItem::PlayerId) == playerID && c(&Database::EmailItem::MsgIndex) == index)
);
if (attachments.size() > 4)
std::cout << "[WARN] Email has too many attachments (player " << playerID << ", index " << index << ")" << std::endl;
for (Database::EmailItem& item : attachments) {
items[item.Slot - 1] = { item.Type, item.Id, item.Opt, item.TimeLimit };
}
return items;
}
void Database::updateEmailContent(EmailData* data) {
std::lock_guard<std::mutex> lock(dbCrit);
db.begin_transaction();
auto attachments = db.get_all<Database::EmailItem>(
where(c(&Database::EmailItem::PlayerId) == data->PlayerId && c(&Database::EmailItem::MsgIndex) == data->MsgIndex)
);
data->ItemFlag = (data->Taros > 0 || attachments.size() > 0) ? 1 : 0; // set attachment flag dynamically
db.remove_all<Database::EmailData>(
where(c(&Database::EmailData::PlayerId) == data->PlayerId && c(&Database::EmailData::MsgIndex) == data->MsgIndex)
);
db.insert(*data);
db.commit();
}
void Database::deleteEmailAttachments(int playerID, int index, int slot) {
std::lock_guard<std::mutex> lock(dbCrit);
db.begin_transaction();
if (slot == -1) { // delete all
db.remove_all<Database::EmailItem>(
where(c(&Database::EmailItem::PlayerId) == playerID && c(&Database::EmailItem::MsgIndex) == index)
);
} else { // delete single by comparing slot num
db.remove_all<Database::EmailItem>(
where(c(&Database::EmailItem::PlayerId) == playerID && c(&Database::EmailItem::MsgIndex) == index
&& c(&Database::EmailItem::Slot) == slot)
);
}
db.commit();
}
void Database::deleteEmails(int playerID, int64_t* indices) {
std::lock_guard<std::mutex> lock(dbCrit);
db.begin_transaction();
for (int i = 0; i < 5; i++) {
db.remove_all<Database::EmailData>(
where(c(&Database::EmailData::PlayerId) == playerID && c(&Database::EmailData::MsgIndex) == indices[i])
); // no need to check if the index is 0, since an email will never have index < 1
}
db.commit();
}
int Database::getNextEmailIndex(int playerID) {
std::lock_guard<std::mutex> lock(dbCrit);
auto emailData = db.get_all<Database::EmailData>(
where(c(&Database::EmailData::PlayerId) == playerID),
order_by(&Database::EmailData::MsgIndex).desc(),
limit(1)
);
return (emailData.size() > 0 ? emailData.at(0).MsgIndex + 1 : 1);
}
void Database::sendEmail(EmailData* data, std::vector<sItemBase> attachments) {
std::lock_guard<std::mutex> lock(dbCrit);
db.begin_transaction();
db.insert(*data); // add email data to db
// add email attachments to db email inventory
int slot = 1;
for (sItemBase item : attachments) {
EmailItem dbItem = {
data->PlayerId,
data->MsgIndex,
slot++,
item.iType,
item.iID,
item.iOpt,
item.iTimeLimit
};
db.insert(dbItem);
}
db.commit();
}
#pragma endregion ShardServer