2020-08-28 18:02:03 +00:00
|
|
|
#include "Database.hpp"
|
2020-10-04 23:54:08 +00:00
|
|
|
#include "Database.hpp"
|
2020-09-03 02:25:09 +00:00
|
|
|
#include "contrib/bcrypt/BCrypt.hpp"
|
2020-09-01 22:37:09 +00:00
|
|
|
#include "CNProtocol.hpp"
|
|
|
|
#include <string>
|
|
|
|
#include "contrib/JSON.hpp"
|
2020-11-28 23:19:07 +00:00
|
|
|
#include "contrib/sqlite/sqlite3.h"
|
2020-08-28 18:02:03 +00:00
|
|
|
#include "CNStructs.hpp"
|
|
|
|
#include "settings.hpp"
|
|
|
|
#include "Player.hpp"
|
2020-09-01 22:37:09 +00:00
|
|
|
#include "CNStructs.hpp"
|
2020-09-13 18:45:51 +00:00
|
|
|
#include "MissionManager.hpp"
|
2020-09-01 22:37:09 +00:00
|
|
|
|
2020-09-29 14:47:39 +00:00
|
|
|
#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;
|
2020-09-29 14:47:39 +00:00
|
|
|
|
2020-09-01 22:37:09 +00:00
|
|
|
#pragma region LoginServer
|
|
|
|
|
2020-10-19 17:26:14 +00:00
|
|
|
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();
|
|
|
|
|
2020-09-21 19:43:53 +00:00
|
|
|
std::cout << "[INFO] Database in operation ";
|
2020-12-02 00:21:45 +00:00
|
|
|
int accounts = getTableSize("Accounts");
|
|
|
|
int players = getTableSize("Players");
|
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,
|
2020-12-02 00:21:45 +00:00
|
|
|
"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)
|
|
|
|
);
|
|
|
|
|
2020-12-02 00:21:45 +00:00
|
|
|
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,
|
2020-12-02 00:21:45 +00:00
|
|
|
"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,
|
2020-12-02 00:21:45 +00:00
|
|
|
"Angle" INTEGER NOT NULL,
|
2020-11-30 12:44:43 +00:00
|
|
|
"HP" INTEGER NOT NULL,
|
|
|
|
"NameCheck" INTEGER NOT NULL,
|
|
|
|
"AccountLevel" INTEGER NOT NULL,
|
2020-12-02 00:21:45 +00:00
|
|
|
"FusionMatter" INTEGER DEFAULT 0 NOT NULL,
|
|
|
|
"Taros" INTEGER DEFAULT 0 NOT NULL,
|
2020-11-30 12:44:43 +00:00
|
|
|
"Quests" BLOB NOT NULL,
|
2020-12-02 00:21:45 +00:00
|
|
|
"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),
|
2020-12-02 13:29:05 +00:00
|
|
|
FOREIGN KEY("AccountID") REFERENCES "Accounts"("AccountID") ON DELETE CASCADE,
|
2020-12-02 13:55:17 +00:00
|
|
|
UNIQUE ("AccountID", "Slot"),
|
|
|
|
UNIQUE ("Firstname", "LastName")
|
2020-11-30 12:44:43 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS "Appearances" (
|
2020-12-02 13:29:05 +00:00
|
|
|
"PlayerID" INTEGER UNIQUE NOT NULL,
|
2020-12-02 00:21:45 +00:00
|
|
|
"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,
|
2020-12-02 13:29:05 +00:00
|
|
|
"TimeLimit" INTEGER DEFAULT 0 NOT NULL,
|
|
|
|
FOREIGN KEY("PlayerID") REFERENCES "Players"("PlayerID") ON DELETE CASCADE,
|
|
|
|
UNIQUE ("PlayerId", "Slot")
|
2020-11-30 12:44:43 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS "Nanos" (
|
|
|
|
"PlayerId" INTEGER NOT NULL,
|
|
|
|
"Id" INTEGER NOT NULL,
|
|
|
|
"Skill" INTEGER NOT NULL,
|
2020-12-02 13:29:05 +00:00
|
|
|
"Stamina" INTEGER DEFAULT 150 NOT NULL,
|
|
|
|
FOREIGN KEY("PlayerID") REFERENCES "Players"("PlayerID") ON DELETE CASCADE,
|
|
|
|
UNIQUE ("PlayerId", "Id")
|
2020-11-30 12:44:43 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-02 00:21:45 +00:00
|
|
|
int Database::getTableSize(std::string tableName) {
|
|
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
2020-11-30 12:44:43 +00:00
|
|
|
|
2020-12-02 13:55:17 +00:00
|
|
|
const char* sql = "SELECT COUNT(*) FROM ?;";
|
2020-12-02 00:21:45 +00:00
|
|
|
sqlite3_stmt* stmt;
|
|
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
2020-12-02 13:55:17 +00:00
|
|
|
sqlite3_bind_text(stmt, 1, tableName.c_str(), -1, 0);
|
2020-12-02 00:21:45 +00:00
|
|
|
sqlite3_step(stmt);
|
|
|
|
int result = sqlite3_column_int(stmt, 0);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
return result;
|
2020-08-28 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
2020-10-19 17:26:14 +00:00
|
|
|
int Database::addAccount(std::string login, std::string password) {
|
2020-09-29 14:47:39 +00:00
|
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
|
2020-12-02 00:21:45 +00:00
|
|
|
const char* sql = R"(
|
|
|
|
INSERT INTO "Accounts"
|
|
|
|
("Login", "Password")
|
|
|
|
VALUES (?, ?);
|
|
|
|
)";
|
|
|
|
sqlite3_stmt* stmt;
|
|
|
|
|
2020-12-02 13:55:17 +00:00
|
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
2020-12-02 00:21:45 +00:00
|
|
|
sqlite3_bind_text(stmt, 1, login.c_str(), -1, 0);
|
|
|
|
sqlite3_bind_text(stmt, 2, BCrypt::generateHash(password).c_str(), -1, 0);
|
2020-12-02 13:55:17 +00:00
|
|
|
int rc = sqlite3_step(stmt);
|
2020-12-02 00:21:45 +00:00
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
|
2020-12-02 13:55:17 +00:00
|
|
|
return rc==SQLITE_DONE ? sqlite3_last_insert_rowid(db) : 0;
|
2020-08-28 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
2020-10-19 17:26:14 +00:00
|
|
|
void Database::updateSelected(int accountId, int slot) {
|
2020-09-29 14:47:39 +00:00
|
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
|
2020-12-02 13:55:17 +00:00
|
|
|
if (slot < 1 || slot > 4) {
|
2020-12-02 00:21:45 +00:00
|
|
|
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);
|
2020-08-28 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
2020-12-02 00:21:45 +00:00
|
|
|
void Database::findAccount(Account* account, std::string login) {
|
2020-09-29 14:47:39 +00:00
|
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
|
2020-12-02 00:21:45 +00:00
|
|
|
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);
|
2020-08-28 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
2020-11-17 02:21:41 +00:00
|
|
|
bool Database::validateCharacter(int characterID, int userID) {
|
2020-12-02 00:21:45 +00:00
|
|
|
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;
|
2020-11-17 02:21:41 +00:00
|
|
|
}
|
|
|
|
|
2020-11-25 23:39:41 +00:00
|
|
|
bool Database::isNameFree(std::string firstName, std::string lastName) {
|
2020-09-29 14:47:39 +00:00
|
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
|
2020-12-02 00:21:45 +00:00
|
|
|
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;
|
2020-08-28 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
2020-11-26 00:15:30 +00:00
|
|
|
bool Database::isSlotFree(int accountId, int slotNum) {
|
|
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
|
2020-12-02 13:55:17 +00:00
|
|
|
if (slotNum < 1 || slotNum > 4) {
|
2020-12-02 00:21:45 +00:00
|
|
|
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;
|
2020-11-26 00:15:30 +00:00
|
|
|
}
|
|
|
|
|
2020-10-19 17:26:14 +00:00
|
|
|
int Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID) {
|
2020-09-29 14:47:39 +00:00
|
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
2020-12-02 00:21:45 +00:00
|
|
|
|
|
|
|
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
|
2020-12-02 13:55:17 +00:00
|
|
|
unsigned char blobBuffer[128] = { 0 };
|
2020-12-02 00:21:45 +00:00
|
|
|
sqlite3_bind_blob(stmt, 12, blobBuffer, sizeof(blobBuffer), 0);
|
|
|
|
|
2020-12-02 13:55:17 +00:00
|
|
|
int rc = sqlite3_step(stmt);
|
2020-12-02 00:21:45 +00:00
|
|
|
sqlite3_finalize(stmt);
|
2020-12-02 13:55:17 +00:00
|
|
|
if (rc != SQLITE_DONE) {
|
|
|
|
sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
|
2020-12-02 00:21:45 +00:00
|
|
|
return 0;
|
2020-12-02 13:55:17 +00:00
|
|
|
}
|
2020-12-02 00:21:45 +00:00
|
|
|
|
2020-12-02 13:55:17 +00:00
|
|
|
int playerId = sqlite3_last_insert_rowid(db);
|
|
|
|
|
2020-12-02 00:21:45 +00:00
|
|
|
sql = R"(
|
|
|
|
INSERT INTO "Appearances"
|
|
|
|
("PlayerID")
|
|
|
|
VALUES (?);
|
|
|
|
)";
|
|
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
|
|
|
sqlite3_bind_int(stmt, 1, playerId);
|
2020-09-29 14:47:39 +00:00
|
|
|
|
2020-12-02 13:55:17 +00:00
|
|
|
rc = sqlite3_step(stmt);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
if (rc != SQLITE_DONE) {
|
|
|
|
sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
|
2020-12-02 00:21:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL);
|
|
|
|
return playerId;
|
2020-08-28 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
2020-12-02 00:21:45 +00:00
|
|
|
bool Database::finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character) {
|
2020-09-29 14:47:39 +00:00
|
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
2020-12-02 00:21:45 +00:00
|
|
|
|
|
|
|
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);
|
2020-12-02 13:55:17 +00:00
|
|
|
int rc = sqlite3_step(stmt);
|
|
|
|
sqlite3_finalize(stmt);
|
2020-12-02 00:21:45 +00:00
|
|
|
|
2020-12-02 13:55:17 +00:00
|
|
|
if ( rc != SQLITE_ROW)
|
2020-12-02 00:21:45 +00:00
|
|
|
{
|
2020-12-02 13:55:17 +00:00
|
|
|
std::cout << "[WARN] Player tried Character Creation on already existing character?!" << std::endl;
|
2020-12-02 00:21:45 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL);
|
|
|
|
|
|
|
|
sql = R"(
|
|
|
|
UPDATE "Players"
|
|
|
|
SET "AppearanceFlag" = 1
|
|
|
|
WHERE "PlayerID" = ?;
|
|
|
|
)";
|
2020-09-29 14:47:39 +00:00
|
|
|
|
2020-12-02 00:21:45 +00:00
|
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
|
|
|
sqlite3_bind_int(stmt, 1, character->PCStyle.iPC_UID);
|
2020-12-02 13:55:17 +00:00
|
|
|
int rc = sqlite3_step(stmt);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
|
|
|
|
if (rc != SQLITE_DONE)
|
2020-12-02 00:21:45 +00:00
|
|
|
{
|
2020-12-02 13:55:17 +00:00
|
|
|
sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
|
2020-12-02 00:21:45 +00:00
|
|
|
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);
|
|
|
|
|
2020-12-02 13:55:17 +00:00
|
|
|
rc = sqlite3_step(stmt);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
|
|
|
|
if (rc != SQLITE_DONE)
|
2020-12-02 00:21:45 +00:00
|
|
|
{
|
2020-12-02 13:55:17 +00:00
|
|
|
sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
|
2020-12-02 00:21:45 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
sql = R"(
|
|
|
|
INSERT INTO "Inventory"
|
|
|
|
("PlayerID", "Slot", "Id", "Type", "Opt")
|
|
|
|
VALUES (?, ?, ?, ?, 1);
|
|
|
|
)";
|
2020-12-02 13:55:17 +00:00
|
|
|
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
|
2020-12-02 00:21:45 +00:00
|
|
|
|
|
|
|
int items[3] = { character->sOn_Item.iEquipUBID, character->sOn_Item.iEquipLBID, character->sOn_Item.iEquipFootID };
|
|
|
|
for (int i = 0; i < 3; i++) {
|
2020-12-02 13:55:17 +00:00
|
|
|
sqlite3_bind_int(stmt, 1, character->PCStyle.iPC_UID);
|
|
|
|
sqlite3_bind_int(stmt, 2, i+1);
|
|
|
|
sqlite3_bind_int(stmt, 3, items[i]);
|
|
|
|
sqlite3_bind_int(stmt, 4, i+1);
|
2020-12-02 00:21:45 +00:00
|
|
|
|
2020-12-02 13:55:17 +00:00
|
|
|
rc = sqlite3_step(stmt);
|
|
|
|
|
|
|
|
if (rc != SQLITE_DONE)
|
2020-12-02 00:21:45 +00:00
|
|
|
{
|
|
|
|
sqlite3_finalize(stmt);
|
2020-12-02 13:55:17 +00:00
|
|
|
sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
|
2020-12-02 00:21:45 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
sqlite3_reset(stmt);
|
|
|
|
}
|
|
|
|
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL);
|
|
|
|
return true;
|
2020-08-28 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
2020-12-02 00:21:45 +00:00
|
|
|
bool Database::finishTutorial(int playerID) {
|
2020-09-29 14:47:39 +00:00
|
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
|
2020-12-02 00:21:45 +00:00
|
|
|
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);
|
|
|
|
|
2020-12-02 13:55:17 +00:00
|
|
|
int rc = sqlite3_step(stmt);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
|
|
|
|
if (rc != SQLITE_DONE)
|
2020-12-02 00:21:45 +00:00
|
|
|
{
|
2020-12-02 13:55:17 +00:00
|
|
|
sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
|
2020-12-02 00:21:45 +00:00
|
|
|
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);
|
|
|
|
|
2020-12-02 13:55:17 +00:00
|
|
|
rc = sqlite3_step(stmt);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
|
|
|
|
if (rc != SQLITE_DONE)
|
2020-12-02 00:21:45 +00:00
|
|
|
{
|
2020-12-02 13:55:17 +00:00
|
|
|
sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
|
2020-12-02 00:21:45 +00:00
|
|
|
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);
|
|
|
|
|
2020-12-02 13:55:17 +00:00
|
|
|
rc = sqlite3_step(stmt);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
|
|
|
|
if (rc != SQLITE_DONE)
|
2020-12-02 00:21:45 +00:00
|
|
|
{
|
2020-12-02 13:55:17 +00:00
|
|
|
sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
|
2020-12-02 00:21:45 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL);
|
|
|
|
return true;
|
2020-08-28 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
2020-10-19 17:26:14 +00:00
|
|
|
int Database::deleteCharacter(int characterID, int userID) {
|
2020-09-29 14:47:39 +00:00
|
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
|
2020-09-02 15:53:39 +00:00
|
|
|
auto find =
|
2020-09-13 20:29:30 +00:00
|
|
|
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);
|
2020-09-08 20:41:02 +00:00
|
|
|
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;
|
2020-08-28 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
2020-10-19 17:26:14 +00:00
|
|
|
std::vector <Player> Database::getCharacters(int UserID) {
|
2020-09-29 14:47:39 +00:00
|
|
|
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));
|
2020-09-14 14:03:30 +00:00
|
|
|
// parsing DbPlayer to Player
|
2020-09-02 15:53:39 +00:00
|
|
|
std::vector<Player> result = std::vector<Player>();
|
|
|
|
for (auto &character : characters) {
|
2020-09-14 13:53:48 +00:00
|
|
|
Player toadd = DbToPlayer(character);
|
2020-09-02 15:53:39 +00:00
|
|
|
result.push_back(
|
|
|
|
toadd
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return result;
|
2020-08-28 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
2020-11-18 01:27:04 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-09-14 13:20:55 +00:00
|
|
|
// XXX: This is never called?
|
|
|
|
void Database::evaluateCustomName(int characterID, CustomName decision) {
|
2020-09-29 14:47:39 +00:00
|
|
|
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);
|
2020-08-28 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
2020-09-01 22:37:09 +00:00
|
|
|
void Database::changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save) {
|
2020-09-29 14:47:39 +00:00
|
|
|
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);
|
2020-08-28 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
2020-10-19 17:26:14 +00:00
|
|
|
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
|
2020-09-21 19:43:53 +00:00
|
|
|
DbPlayer result = {};
|
2020-09-03 20:29:29 +00:00
|
|
|
|
2020-09-14 13:20:55 +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;
|
2020-09-22 02:26:12 +00:00
|
|
|
result.AccountLevel = player->accountLevel;
|
2020-09-14 13:20:55 +00:00
|
|
|
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
|
2020-10-14 04:26:30 +00:00
|
|
|
result.x_coordinates = player->x;
|
|
|
|
result.y_coordinates = player->y;
|
|
|
|
result.z_coordinates = player->z;
|
|
|
|
result.angle = player->angle;
|
2020-10-19 17:26:14 +00:00
|
|
|
} else {
|
2020-10-14 04:26:30 +00:00
|
|
|
result.x_coordinates = player->lastX;
|
|
|
|
result.y_coordinates = player->lastY;
|
|
|
|
result.z_coordinates = player->lastZ;
|
|
|
|
result.angle = player->lastAngle;
|
|
|
|
}
|
2020-09-14 13:20:55 +00:00
|
|
|
result.Nano1 = player->equippedNanos[0];
|
|
|
|
result.Nano2 = player->equippedNanos[1];
|
|
|
|
result.Nano3 = player->equippedNanos[2];
|
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;
|
|
|
|
|
2020-10-02 16:55:54 +00:00
|
|
|
// timestamp
|
2020-09-22 19:15:47 +00:00
|
|
|
result.LastLogin = getTimestamp();
|
2020-09-29 14:47:39 +00:00
|
|
|
result.Created = player->creationTime;
|
2020-09-13 18:45:51 +00:00
|
|
|
|
2020-10-02 16:55:54 +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;
|
2020-08-28 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
2020-09-01 22:37:09 +00:00
|
|
|
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)
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-09-02 22:22:00 +00:00
|
|
|
result.iID = player.PlayerID;
|
2020-09-02 15:53:39 +00:00
|
|
|
result.accountId = player.AccountID;
|
2020-09-29 14:47:39 +00:00
|
|
|
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;
|
2020-09-22 02:26:12 +00:00
|
|
|
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;
|
2020-09-02 22:22:00 +00:00
|
|
|
result.angle = player.angle;
|
|
|
|
result.money = player.Taros;
|
|
|
|
result.fusionmatter = player.FusionMatter;
|
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-01 22:37:09 +00:00
|
|
|
|
2020-09-07 00:16:44 +00:00
|
|
|
result.equippedNanos[0] = player.Nano1;
|
|
|
|
result.equippedNanos[1] = player.Nano2;
|
|
|
|
result.equippedNanos[2] = player.Nano3;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-09-26 22:16:15 +00:00
|
|
|
result.inCombat = false;
|
|
|
|
|
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);
|
2020-09-22 11:16:09 +00:00
|
|
|
Database::removeExpiredVehicles(&result);
|
2020-09-07 00:16:44 +00:00
|
|
|
Database::getNanos(&result);
|
2020-10-05 00:03:13 +00:00
|
|
|
Database::getQuests(&result);
|
2020-11-08 17:42:27 +00:00
|
|
|
Database::getBuddies(&result);
|
2020-09-13 18:45:51 +00:00
|
|
|
|
2020-10-02 16:55:54 +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;
|
2020-08-28 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
2020-09-01 22:37:09 +00:00
|
|
|
Database::DbPlayer Database::getDbPlayerById(int id) {
|
2020-11-10 23:00:34 +00:00
|
|
|
auto player = db.get_all<DbPlayer>(where(c(&DbPlayer::PlayerID) == id));
|
2020-11-10 23:16:06 +00:00
|
|
|
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));
|
2020-11-10 23:00:34 +00:00
|
|
|
return DbPlayer{ -1 };
|
2020-11-10 23:16:06 +00:00
|
|
|
}
|
2020-11-10 23:00:34 +00:00
|
|
|
return player.front();
|
2020-08-28 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
2020-09-07 00:16:44 +00:00
|
|
|
Player Database::getPlayer(int id) {
|
|
|
|
return DbToPlayer(
|
|
|
|
getDbPlayerById(id)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-09-01 22:37:09 +00:00
|
|
|
#pragma endregion LoginServer
|
2020-09-02 22:22:00 +00:00
|
|
|
|
2020-09-09 12:36:35 +00:00
|
|
|
#pragma region ShardServer
|
|
|
|
|
2020-09-14 13:20:55 +00:00
|
|
|
void Database::updatePlayer(Player *player) {
|
2020-09-29 14:47:39 +00:00
|
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
|
2020-09-02 22:22:00 +00:00
|
|
|
DbPlayer toUpdate = playerToDb(player);
|
|
|
|
db.update(toUpdate);
|
2020-09-07 00:16:44 +00:00
|
|
|
updateInventory(player);
|
|
|
|
updateNanos(player);
|
2020-09-21 19:43:53 +00:00
|
|
|
updateQuests(player);
|
2020-11-09 10:01:51 +00:00
|
|
|
//updateBuddies(player); we add/remove buddies explicitly now
|
2020-09-07 00:16:44 +00:00
|
|
|
}
|
|
|
|
|
2020-09-14 13:20:55 +00:00
|
|
|
void Database::updateInventory(Player *player){
|
2020-09-14 14:03:30 +00:00
|
|
|
// start transaction
|
2020-09-07 00:16:44 +00:00
|
|
|
db.begin_transaction();
|
2020-09-14 14:03:30 +00:00
|
|
|
// remove all
|
2020-09-07 00:16:44 +00:00
|
|
|
db.remove_all<Inventory>(
|
2020-09-14 13:20:55 +00:00
|
|
|
where(c(&Inventory::playerId) == player->iID)
|
2020-09-07 00:16:44 +00:00
|
|
|
);
|
2020-09-14 14:03:30 +00:00
|
|
|
// insert equip
|
2020-09-07 00:16:44 +00:00
|
|
|
for (int i = 0; i < AEQUIP_COUNT; i++) {
|
2020-09-14 13:20:55 +00:00
|
|
|
if (player->Equip[i].iID != 0) {
|
|
|
|
sItemBase* next = &player->Equip[i];
|
2020-09-07 00:16:44 +00:00
|
|
|
Inventory toAdd = {};
|
2020-09-14 13:20:55 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2020-09-14 14:03:30 +00:00
|
|
|
// insert inventory
|
2020-09-07 00:16:44 +00:00
|
|
|
for (int i = 0; i < AINVEN_COUNT; i++) {
|
2020-09-14 13:20:55 +00:00
|
|
|
if (player->Inven[i].iID != 0) {
|
|
|
|
sItemBase* next = &player->Inven[i];
|
2020-09-07 00:16:44 +00:00
|
|
|
Inventory toAdd = {};
|
2020-09-14 13:20:55 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2020-09-14 14:03:30 +00:00
|
|
|
// insert bank
|
2020-09-09 20:42:55 +00:00
|
|
|
for (int i = 0; i < ABANK_COUNT; i++) {
|
2020-09-14 13:20:55 +00:00
|
|
|
if (player->Bank[i].iID != 0) {
|
|
|
|
sItemBase* next = &player->Bank[i];
|
2020-09-09 20:42:55 +00:00
|
|
|
Inventory toAdd = {};
|
2020-09-14 13:20:55 +00:00
|
|
|
toAdd.playerId = player->iID;
|
2020-09-09 20:42:55 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
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();
|
|
|
|
}
|
2020-09-21 19:43:53 +00:00
|
|
|
|
2020-09-14 13:20:55 +00:00
|
|
|
void Database::updateNanos(Player *player) {
|
2020-09-14 14:03:30 +00:00
|
|
|
// start transaction
|
2020-09-07 00:16:44 +00:00
|
|
|
db.begin_transaction();
|
2020-09-14 14:03:30 +00:00
|
|
|
// remove all
|
2020-09-07 00:16:44 +00:00
|
|
|
db.remove_all<Nano>(
|
2020-09-14 13:20:55 +00:00
|
|
|
where(c(&Nano::playerId) == player->iID)
|
2020-09-07 00:16:44 +00:00
|
|
|
);
|
2020-09-14 14:03:30 +00:00
|
|
|
// insert
|
2020-10-19 17:26:14 +00:00
|
|
|
for (int i=1; i < SIZEOF_NANO_BANK_SLOT; i++) {
|
2020-09-14 13:20:55 +00:00
|
|
|
if ((player->Nanos[i]).iID == 0)
|
2020-09-08 20:41:02 +00:00
|
|
|
continue;
|
2020-09-07 00:16:44 +00:00
|
|
|
Nano toAdd = {};
|
2020-09-14 13:20:55 +00:00
|
|
|
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();
|
|
|
|
}
|
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
|
2020-10-19 17:26:14 +00:00
|
|
|
for (int i = 0; i < ACTIVE_MISSION_COUNT; i++) {
|
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();
|
|
|
|
}
|
|
|
|
|
2020-11-09 10:01:51 +00:00
|
|
|
// 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) {
|
2020-09-14 14:03:30 +00:00
|
|
|
// get items from DB
|
2020-09-07 00:16:44 +00:00
|
|
|
auto items = db.get_all<Inventory>(
|
|
|
|
where(c(&Inventory::playerId) == player->iID)
|
|
|
|
);
|
2020-09-14 14:03:30 +00:00
|
|
|
// set items
|
2020-09-07 00:16:44 +00:00
|
|
|
for (const Inventory ¤t : items) {
|
|
|
|
sItemBase toSet = {};
|
|
|
|
toSet.iID = current.id;
|
|
|
|
toSet.iType = current.Type;
|
|
|
|
toSet.iOpt = current.Opt;
|
|
|
|
toSet.iTimeLimit = current.TimeLimit;
|
2020-09-14 14:03:30 +00:00
|
|
|
// assign to proper arrays
|
2020-09-21 19:43:53 +00:00
|
|
|
if (current.slot < AEQUIP_COUNT)
|
2020-09-14 13:53:48 +00:00
|
|
|
player->Equip[current.slot] = toSet;
|
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;
|
2020-09-21 19:43:53 +00:00
|
|
|
else if (current.slot < (AEQUIP_COUNT + AINVEN_COUNT + ABANK_COUNT))
|
2020-09-09 20:42:55 +00:00
|
|
|
player->Bank[current.slot - AEQUIP_COUNT - AINVEN_COUNT] = toSet;
|
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
|
|
|
}
|
|
|
|
|
2020-09-22 11:16:09 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Database::removeExpiredVehicles(Player* player) {
|
2020-09-23 09:05:18 +00:00
|
|
|
int32_t currentTime = getTimestamp();
|
2020-09-23 09:21:32 +00:00
|
|
|
// remove from bank immediately
|
2020-09-22 11:16:09 +00:00
|
|
|
for (int i = 0; i < ABANK_COUNT; i++) {
|
|
|
|
if (player->Bank[i].iType == 10 && player->Bank[i].iTimeLimit < currentTime)
|
|
|
|
player->Bank[i] = {};
|
|
|
|
}
|
2020-09-23 09:21:32 +00:00
|
|
|
// for the rest, we want to leave only 1 expired vehicle on player to delete it with the client packet
|
2020-09-22 11:16:09 +00:00
|
|
|
std::vector<sItemBase*> toRemove;
|
|
|
|
|
2020-09-23 09:21:32 +00:00
|
|
|
// equiped vehicle
|
2020-10-19 17:26:14 +00:00
|
|
|
if (player->Equip[8].iOpt > 0 && player->Equip[8].iTimeLimit < currentTime) {
|
2020-09-22 11:16:09 +00:00
|
|
|
toRemove.push_back(&player->Equip[8]);
|
|
|
|
player->toRemoveVehicle.eIL = 0;
|
|
|
|
player->toRemoveVehicle.iSlotNum = 8;
|
|
|
|
}
|
2020-09-23 09:21:32 +00:00
|
|
|
// inventory
|
2020-09-22 11:16:09 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-23 09:21:32 +00:00
|
|
|
// 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-22 11:16:09 +00:00
|
|
|
}
|
2020-09-07 00:16:44 +00:00
|
|
|
}
|
2020-09-21 19:43:53 +00:00
|
|
|
|
2020-09-07 00:16:44 +00:00
|
|
|
void Database::getNanos(Player* player) {
|
2020-09-14 14:03:30 +00:00
|
|
|
// get from DB
|
2020-09-07 00:16:44 +00:00
|
|
|
auto nanos = db.get_all<Nano>(
|
|
|
|
where(c(&Nano::playerId) == player->iID)
|
|
|
|
);
|
2020-09-14 14:03:30 +00:00
|
|
|
// 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;
|
|
|
|
}
|
2020-09-02 22:22:00 +00:00
|
|
|
}
|
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();
|
|
|
|
}
|
|
|
|
|
2020-11-09 10:01:51 +00:00
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
|
2020-11-07 01:50:17 +00:00
|
|
|
// 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();
|
|
|
|
|
2020-11-08 19:51:20 +00:00
|
|
|
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
|
|
|
|
|
2020-11-07 01:50:17 +00:00
|
|
|
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,
|
2020-11-08 19:51:20 +00:00
|
|
|
slot++,
|
2020-11-07 01:50:17 +00:00
|
|
|
item.iType,
|
|
|
|
item.iID,
|
|
|
|
item.iOpt,
|
|
|
|
item.iTimeLimit
|
|
|
|
};
|
|
|
|
db.insert(dbItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
db.commit();
|
|
|
|
}
|
|
|
|
|
2020-09-09 12:36:35 +00:00
|
|
|
#pragma endregion ShardServer
|