mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-05 06:50:04 +00:00
dongresource
fb281b0237
All DB functions that are called outside of Database.cpp are now locked by the same mutex. This might be a bit overkill, but it's not a hot code path, so it doesn't matter. Better to avoid the potential deadlocks if we made it too granular. From now on a clear distinction must be made between external and internal functions in Database.cpp, or else deadlock will occur. Note that sqlite database operations are already locked, but if execute multiple transactions within the same operation, it could have still caused problems. I also removed the DbPlayer fetch when writing to DB by making it a part of the Player struct. This, by itself, should have fixed the crash we found.
733 lines
24 KiB
C++
733 lines
24 KiB
C++
#include "Database.hpp"
|
|
#include "contrib/bcrypt/BCrypt.hpp"
|
|
#include "CNProtocol.hpp"
|
|
#include <string>
|
|
#include "contrib/JSON.hpp"
|
|
#include "CNStructs.hpp"
|
|
#include "settings.hpp"
|
|
#include "Player.hpp"
|
|
#include "CNStructs.hpp"
|
|
#include "contrib/sqlite/sqlite_orm.h"
|
|
#include "MissionManager.hpp"
|
|
|
|
#if defined(__MINGW32__) && !defined(_GLIBCXX_HAS_GTHREADS)
|
|
#include "mingw/mingw.mutex.h"
|
|
#else
|
|
#include <mutex>
|
|
#endif
|
|
|
|
using namespace sqlite_orm;
|
|
|
|
std::mutex dbCrit;
|
|
|
|
# pragma region DatabaseScheme
|
|
auto db = make_storage("database.db",
|
|
make_table("Accounts",
|
|
make_column("AccountID", &Database::Account::AccountID, autoincrement(), primary_key()),
|
|
make_column("Login", &Database::Account::Login),
|
|
make_column("Password", &Database::Account::Password),
|
|
make_column("Selected", &Database::Account::Selected),
|
|
make_column("Created", &Database::Account::Created),
|
|
make_column("LastLogin", &Database::Account::LastLogin)
|
|
),
|
|
make_table("Players",
|
|
make_column("PlayerID", &Database::DbPlayer::PlayerID, autoincrement(), primary_key()),
|
|
make_column("AccountID", &Database::DbPlayer::AccountID),
|
|
make_column("Slot", &Database::DbPlayer::slot),
|
|
make_column("Firstname", &Database::DbPlayer::FirstName, collate_nocase()),
|
|
make_column("LastName", &Database::DbPlayer::LastName, collate_nocase()),
|
|
make_column("Created", &Database::DbPlayer::Created),
|
|
make_column("LastLogin", &Database::DbPlayer::LastLogin),
|
|
make_column("Level", &Database::DbPlayer::Level),
|
|
make_column("Nano1", &Database::DbPlayer::Nano1),
|
|
make_column("Nano2", &Database::DbPlayer::Nano2),
|
|
make_column("Nano3", &Database::DbPlayer::Nano3),
|
|
make_column("AppearanceFlag", &Database::DbPlayer::AppearanceFlag),
|
|
make_column("TutorialFlag", &Database::DbPlayer::TutorialFlag),
|
|
make_column("PayZoneFlag", &Database::DbPlayer::PayZoneFlag),
|
|
make_column("XCoordinates", &Database::DbPlayer::x_coordinates),
|
|
make_column("YCoordinates", &Database::DbPlayer::y_coordinates),
|
|
make_column("ZCoordinates", &Database::DbPlayer::z_coordinates),
|
|
make_column("Angle", &Database::DbPlayer::angle),
|
|
make_column("Body", &Database::DbPlayer::Body),
|
|
make_column("Class", &Database::DbPlayer::Class),
|
|
make_column("EyeColor", &Database::DbPlayer::EyeColor),
|
|
make_column("FaceStyle", &Database::DbPlayer::FaceStyle),
|
|
make_column("Gender", &Database::DbPlayer::Gender),
|
|
make_column("HP", &Database::DbPlayer::HP),
|
|
make_column("HairColor", &Database::DbPlayer::HairColor),
|
|
make_column("HairStyle", &Database::DbPlayer::HairStyle),
|
|
make_column("Height", &Database::DbPlayer::Height),
|
|
make_column("NameCheck", &Database::DbPlayer::NameCheck),
|
|
make_column("SkinColor", &Database::DbPlayer::SkinColor),
|
|
make_column("AccountLevel", &Database::DbPlayer::AccountLevel),
|
|
make_column("FusionMatter", &Database::DbPlayer::FusionMatter),
|
|
make_column("Taros", &Database::DbPlayer::Taros),
|
|
make_column("Quests", &Database::DbPlayer::QuestFlag),
|
|
make_column("BatteryW", &Database::DbPlayer::BatteryW),
|
|
make_column("BatteryN", &Database::DbPlayer::BatteryN),
|
|
make_column("Mentor", &Database::DbPlayer::Mentor),
|
|
make_column("WarpLocationFlag", &Database::DbPlayer::WarpLocationFlag),
|
|
make_column("SkywayLocationFlag1", &Database::DbPlayer::SkywayLocationFlag1),
|
|
make_column("SkywayLocationFlag2", &Database::DbPlayer::SkywayLocationFlag2),
|
|
make_column("CurrentMissionID", &Database::DbPlayer::CurrentMissionID)
|
|
),
|
|
make_table("Inventory",
|
|
make_column("PlayerId", &Database::Inventory::playerId),
|
|
make_column("Slot", &Database::Inventory::slot),
|
|
make_column("Id", &Database::Inventory::id),
|
|
make_column("Type", &Database::Inventory::Type),
|
|
make_column("Opt", &Database::Inventory::Opt),
|
|
make_column("TimeLimit", &Database::Inventory::TimeLimit)
|
|
),
|
|
make_table("Nanos",
|
|
make_column("PlayerId", &Database::Nano::playerId),
|
|
make_column("Id", &Database::Nano::iID),
|
|
make_column("Skill", &Database::Nano::iSkillID),
|
|
make_column("Stamina", &Database::Nano::iStamina)
|
|
),
|
|
make_table("RunningQuests",
|
|
make_column("PlayerId", &Database::DbQuest::PlayerId),
|
|
make_column("TaskId", &Database::DbQuest::TaskId),
|
|
make_column("RemainingNPCCount1", &Database::DbQuest::RemainingNPCCount1),
|
|
make_column("RemainingNPCCount2", &Database::DbQuest::RemainingNPCCount2),
|
|
make_column("RemainingNPCCount3", &Database::DbQuest::RemainingNPCCount3)
|
|
)
|
|
);
|
|
|
|
# pragma endregion DatabaseScheme
|
|
|
|
#pragma region LoginServer
|
|
|
|
void Database::open()
|
|
{
|
|
// this parameter means it will try to preserve data during migration
|
|
bool preserve = true;
|
|
db.sync_schema(preserve);
|
|
std::cout << "[INFO] Database in operation ";
|
|
int accounts = getAccountsCount();
|
|
int players = getPlayersCount();
|
|
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;
|
|
}
|
|
|
|
int Database::getAccountsCount() {
|
|
return db.count<Account>();
|
|
}
|
|
|
|
int Database::getPlayersCount() {
|
|
return db.count<DbPlayer>();
|
|
}
|
|
|
|
int Database::addAccount(std::string login, std::string password)
|
|
{
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
password = BCrypt::generateHash(password);
|
|
Account account = {};
|
|
account.Login = login;
|
|
account.Password = password;
|
|
account.Selected = 1;
|
|
account.Created = getTimestamp();
|
|
return db.insert(account);
|
|
}
|
|
|
|
void Database::updateSelected(int accountId, int slot)
|
|
{
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
Account acc = db.get<Account>(accountId);
|
|
acc.Selected = slot;
|
|
//timestamp
|
|
acc.LastLogin = getTimestamp();
|
|
db.update(acc);
|
|
}
|
|
|
|
std::unique_ptr<Database::Account> Database::findAccount(std::string login)
|
|
{
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
// this is awful, I've tried everything to improve it
|
|
auto find = db.get_all<Account>(
|
|
where(c(&Account::Login) == login), limit(1));
|
|
if (find.empty())
|
|
return nullptr;
|
|
return
|
|
std::unique_ptr<Account>(new Account(find.front()));
|
|
}
|
|
|
|
bool Database::isNameFree(sP_CL2LS_REQ_CHECK_CHAR_NAME* nameCheck)
|
|
{
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
std::string First = U16toU8(nameCheck->szFirstName);
|
|
std::string Last = U16toU8(nameCheck->szLastName);
|
|
return
|
|
(db.get_all<DbPlayer>
|
|
(where((c(&DbPlayer::FirstName) == First)
|
|
and (c(&DbPlayer::LastName) == Last)))
|
|
.empty());
|
|
}
|
|
|
|
int Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID)
|
|
{
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
// fail if the player already has 4 or more characters
|
|
if (db.count<DbPlayer>(where(c(&DbPlayer::AccountID) == AccountID)) >= 4)
|
|
return -1;
|
|
|
|
DbPlayer create = {};
|
|
|
|
//set timestamp
|
|
create.Created = getTimestamp();
|
|
// save packet data
|
|
create.FirstName = U16toU8(save->szFirstName);
|
|
create.LastName = U16toU8(save->szLastName);
|
|
create.slot = save->iSlotNum;
|
|
create.AccountID = AccountID;
|
|
|
|
// set flags
|
|
create.AppearanceFlag = 0;
|
|
create.TutorialFlag = 0;
|
|
create.PayZoneFlag = 0;
|
|
|
|
// set namecheck based on setting
|
|
if (settings::APPROVEALLNAMES || save->iFNCode)
|
|
create.NameCheck = 1;
|
|
else
|
|
create.NameCheck = 0;
|
|
|
|
// create default body character
|
|
create.Body = 0;
|
|
create.Class = 0;
|
|
create.EyeColor = 1;
|
|
create.FaceStyle = 1;
|
|
create.Gender = 1;
|
|
create.Level = 1;
|
|
create.HP = PC_MAXHEALTH(create.Level);
|
|
create.HairColor = 1;
|
|
create.HairStyle = 1;
|
|
create.Height = 0;
|
|
create.SkinColor = 1;
|
|
create.AccountLevel = settings::ACCLEVEL;
|
|
create.x_coordinates = settings::SPAWN_X;
|
|
create.y_coordinates = settings::SPAWN_Y;
|
|
create.z_coordinates = settings::SPAWN_Z;
|
|
create.angle = settings::SPAWN_ANGLE;
|
|
create.QuestFlag = std::vector<char>();
|
|
//set mentor to computress
|
|
create.Mentor = 5;
|
|
|
|
return db.insert(create);
|
|
}
|
|
|
|
void Database::finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character)
|
|
{
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
DbPlayer finish = getDbPlayerById(character->PCStyle.iPC_UID);
|
|
finish.AppearanceFlag = 1;
|
|
finish.Body = character->PCStyle.iBody;
|
|
finish.Class = character->PCStyle.iClass;
|
|
finish.EyeColor = character->PCStyle.iEyeColor;
|
|
finish.FaceStyle = character->PCStyle.iFaceStyle;
|
|
finish.Gender = character->PCStyle.iGender;
|
|
finish.HairColor = character->PCStyle.iHairColor;
|
|
finish.HairStyle = character->PCStyle.iHairStyle;
|
|
finish.Height = character->PCStyle.iHeight;
|
|
finish.Level = 1;
|
|
finish.SkinColor = character->PCStyle.iSkinColor;
|
|
db.update(finish);
|
|
// clothes
|
|
Inventory Foot, LB, UB;
|
|
Foot.playerId = character->PCStyle.iPC_UID;
|
|
Foot.id = character->sOn_Item.iEquipFootID;
|
|
Foot.Type = 3;
|
|
Foot.slot = 3;
|
|
Foot.Opt = 1;
|
|
Foot.TimeLimit = 0;
|
|
db.insert(Foot);
|
|
LB.playerId = character->PCStyle.iPC_UID;
|
|
LB.id = character->sOn_Item.iEquipLBID;
|
|
LB.Type = 2;
|
|
LB.slot = 2;
|
|
LB.Opt = 1;
|
|
LB.TimeLimit = 0;
|
|
db.insert(LB);
|
|
UB.playerId = character->PCStyle.iPC_UID;
|
|
UB.id = character->sOn_Item.iEquipUBID;
|
|
UB.Type = 1;
|
|
UB.slot = 1;
|
|
UB.Opt = 1;
|
|
UB.TimeLimit = 0;
|
|
db.insert(UB);
|
|
}
|
|
|
|
void Database::finishTutorial(int PlayerID)
|
|
{
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
Player finish = getPlayer(PlayerID);
|
|
// set flag
|
|
finish.PCStyle2.iTutorialFlag= 1;
|
|
// add Gun
|
|
Inventory LightningGun = {};
|
|
LightningGun.playerId = PlayerID;
|
|
LightningGun.id = 328;
|
|
LightningGun.slot = 0;
|
|
LightningGun.Type = 0;
|
|
LightningGun.Opt = 1;
|
|
db.insert(LightningGun);
|
|
// add Nano
|
|
Nano Buttercup = {};
|
|
Buttercup.playerId = PlayerID;
|
|
Buttercup.iID = 1;
|
|
Buttercup.iSkillID = 1;
|
|
Buttercup.iStamina = 150;
|
|
finish.equippedNanos[0] = 1;
|
|
db.insert(Buttercup);
|
|
// save missions
|
|
MissionManager::saveMission(&finish, 0);
|
|
MissionManager::saveMission(&finish, 1);
|
|
|
|
db.update(playerToDb(&finish));
|
|
}
|
|
|
|
int Database::deleteCharacter(int characterID, int userID)
|
|
{
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
auto find =
|
|
db.get_all<DbPlayer>(where(c(&DbPlayer::PlayerID) == characterID and c(&DbPlayer::AccountID)==userID));
|
|
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));
|
|
|
|
return slot;
|
|
}
|
|
|
|
std::vector <Player> Database::getCharacters(int UserID)
|
|
{
|
|
std::lock_guard<std::mutex> lock(dbCrit);
|
|
|
|
std::vector<DbPlayer>characters =
|
|
db.get_all<DbPlayer>(where
|
|
(c(&DbPlayer::AccountID) == UserID));
|
|
// parsing DbPlayer to Player
|
|
std::vector<Player> result = std::vector<Player>();
|
|
for (auto &character : characters) {
|
|
Player toadd = DbToPlayer(character);
|
|
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);
|
|
|
|
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);
|
|
|
|
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
|
|
DbPlayer result = {};
|
|
|
|
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;
|
|
result.x_coordinates = player->x;
|
|
result.y_coordinates = player->y;
|
|
result.z_coordinates = player->z;
|
|
result.angle = player->angle;
|
|
result.Nano1 = player->equippedNanos[0];
|
|
result.Nano2 = player->equippedNanos[1];
|
|
result.Nano3 = player->equippedNanos[2];
|
|
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;
|
|
|
|
// finished quests: parsing to blob
|
|
result.QuestFlag = std::vector<char>();
|
|
for (int i=0; i<16; i++)
|
|
{
|
|
int64_t flag = player->aQuestFlag[i];
|
|
appendBlob(&result.QuestFlag, flag);
|
|
}
|
|
//timestamp
|
|
result.LastLogin = getTimestamp();
|
|
result.Created = player->creationTime;
|
|
|
|
return result;
|
|
}
|
|
|
|
Player Database::DbToPlayer(DbPlayer player) {
|
|
Player result = {}; // fixes some weird memory errors, this zeros out the members (not the padding inbetween though)
|
|
|
|
result.iID = player.PlayerID;
|
|
result.accountId = player.AccountID;
|
|
result.creationTime = player.Created;
|
|
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;
|
|
U8toU16(player.FirstName, result.PCStyle.szFirstName);
|
|
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;
|
|
U8toU16(player.LastName, result.PCStyle.szLastName);
|
|
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;
|
|
result.batteryN = player.BatteryN;
|
|
result.batteryW = player.BatteryW;
|
|
result.mentor = player.Mentor;
|
|
result.CurrentMissionID = player.CurrentMissionID;
|
|
|
|
result.equippedNanos[0] = player.Nano1;
|
|
result.equippedNanos[1] = player.Nano2;
|
|
result.equippedNanos[2] = player.Nano3;
|
|
|
|
result.dotDamage = false;
|
|
result.inCombat = false;
|
|
|
|
result.iWarpLocationFlag = player.WarpLocationFlag;
|
|
result.aSkywayLocationFlag[0] = player.SkywayLocationFlag1;
|
|
result.aSkywayLocationFlag[1] = player.SkywayLocationFlag2;
|
|
|
|
Database::getInventory(&result);
|
|
Database::removeExpiredVehicles(&result);
|
|
Database::getNanos(&result);
|
|
Database::getQuests(&result);
|
|
|
|
std::vector<char>::iterator it = player.QuestFlag.begin();
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
if (it == player.QuestFlag.end())
|
|
break;
|
|
result.aQuestFlag[i] = blobToInt64(it);
|
|
//move iterator to the next flag
|
|
it += 8;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
Database::DbPlayer Database::getDbPlayerById(int id) {
|
|
return db.get_all<DbPlayer>(where(c(&DbPlayer::PlayerID) == id))
|
|
.front();
|
|
}
|
|
|
|
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);
|
|
updateInventory(player);
|
|
updateNanos(player);
|
|
updateQuests(player);
|
|
}
|
|
|
|
void Database::updateInventory(Player *player){
|
|
// start transaction
|
|
db.begin_transaction();
|
|
// remove all
|
|
db.remove_all<Inventory>(
|
|
where(c(&Inventory::playerId) == player->iID)
|
|
);
|
|
// insert equip
|
|
for (int i = 0; i < AEQUIP_COUNT; i++) {
|
|
if (player->Equip[i].iID != 0) {
|
|
sItemBase* next = &player->Equip[i];
|
|
Inventory toAdd = {};
|
|
toAdd.playerId = player->iID;
|
|
toAdd.slot = i;
|
|
toAdd.id = next->iID;
|
|
toAdd.Opt = next->iOpt;
|
|
toAdd.Type = next->iType;
|
|
toAdd.TimeLimit = next->iTimeLimit;
|
|
db.insert(toAdd);
|
|
}
|
|
}
|
|
// insert inventory
|
|
for (int i = 0; i < AINVEN_COUNT; i++) {
|
|
if (player->Inven[i].iID != 0) {
|
|
sItemBase* next = &player->Inven[i];
|
|
Inventory toAdd = {};
|
|
toAdd.playerId = player->iID;
|
|
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);
|
|
}
|
|
}
|
|
// 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);
|
|
}
|
|
}
|
|
db.commit();
|
|
}
|
|
|
|
void Database::updateNanos(Player *player) {
|
|
// start transaction
|
|
db.begin_transaction();
|
|
// remove all
|
|
db.remove_all<Nano>(
|
|
where(c(&Nano::playerId) == player->iID)
|
|
);
|
|
// insert
|
|
for (int i=1; i < SIZEOF_NANO_BANK_SLOT; i++)
|
|
{
|
|
if ((player->Nanos[i]).iID == 0)
|
|
continue;
|
|
Nano toAdd = {};
|
|
sNano* next = &player->Nanos[i];
|
|
toAdd.playerId = player->iID;
|
|
toAdd.iID = next->iID;
|
|
toAdd.iSkillID = next->iSkillID;
|
|
toAdd.iStamina = next->iStamina;
|
|
db.insert(toAdd);
|
|
}
|
|
db.commit();
|
|
}
|
|
|
|
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++)
|
|
{
|
|
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();
|
|
}
|
|
|
|
void Database::getInventory(Player* player) {
|
|
// get items from DB
|
|
auto items = db.get_all<Inventory>(
|
|
where(c(&Inventory::playerId) == player->iID)
|
|
);
|
|
// set items
|
|
for (const Inventory ¤t : items) {
|
|
sItemBase toSet = {};
|
|
toSet.iID = current.id;
|
|
toSet.iType = current.Type;
|
|
toSet.iOpt = current.Opt;
|
|
toSet.iTimeLimit = current.TimeLimit;
|
|
// assign to proper arrays
|
|
if (current.slot < AEQUIP_COUNT)
|
|
player->Equip[current.slot] = toSet;
|
|
else if (current.slot < (AEQUIP_COUNT + AINVEN_COUNT))
|
|
player->Inven[current.slot - AEQUIP_COUNT] = toSet;
|
|
else if (current.slot < (AEQUIP_COUNT + AINVEN_COUNT + ABANK_COUNT))
|
|
player->Bank[current.slot - AEQUIP_COUNT - AINVEN_COUNT] = toSet;
|
|
else
|
|
player->QInven[current.slot - AEQUIP_COUNT - AINVEN_COUNT - ABANK_COUNT] = toSet;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
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
|
|
for (int i = 0; i < (int)toRemove.size()-1; i++) {
|
|
memset(toRemove[i], 0, sizeof(sItemBase));
|
|
}
|
|
}
|
|
|
|
void Database::getNanos(Player* player) {
|
|
// get from DB
|
|
auto nanos = db.get_all<Nano>(
|
|
where(c(&Nano::playerId) == player->iID)
|
|
);
|
|
// set
|
|
for (const Nano& current : nanos) {
|
|
sNano *toSet = &player->Nanos[current.iID];
|
|
toSet->iID = current.iID;
|
|
toSet->iSkillID = current.iSkillID;
|
|
toSet->iStamina = current.iStamina;
|
|
}
|
|
}
|
|
|
|
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++;
|
|
}
|
|
}
|
|
|
|
#pragma endregion ShardServer
|
|
|
|
#pragma region parsingBlobs
|
|
|
|
void Database::appendBlob(std::vector<char> *blob, int64_t input) {
|
|
for (int i = 0; i < 8; i++) {
|
|
char toadd = (input >> (8 * (7 - i)));
|
|
blob->push_back(toadd);
|
|
}
|
|
}
|
|
|
|
int64_t Database::blobToInt64(std::vector<char>::iterator it) {
|
|
int64_t result = 0;
|
|
for (int i = 0; i < 8; i++) {
|
|
int64_t toAdd = ((int64_t)*it << (8 * (7 - i)));
|
|
result += toAdd;
|
|
it++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#pragma endregion parsingBlobs
|