mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-04 22:40:05 +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>
This commit is contained in:
parent
321dca3f79
commit
5e0948ea93
@ -14,6 +14,8 @@ acceptallcustomnames=true
|
|||||||
# how often should everything be flushed to the database?
|
# how often should everything be flushed to the database?
|
||||||
# the default is 4 minutes
|
# the default is 4 minutes
|
||||||
dbsaveinterval=240
|
dbsaveinterval=240
|
||||||
|
# use the web API
|
||||||
|
usewebapi=false
|
||||||
|
|
||||||
# Shard Server configuration
|
# Shard Server configuration
|
||||||
[shard]
|
[shard]
|
||||||
|
@ -53,17 +53,27 @@ void CNLoginServer::handlePacket(CNSocket* sock, CNPacketData* data) {
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::unique_ptr<Database::Account> findUser = Database::findAccount(userLogin);
|
std::unique_ptr<Database::Account> findUser = Database::findAccount(userLogin);
|
||||||
// if account not found, create it
|
// if account not found
|
||||||
if (findUser == nullptr)
|
if (findUser == nullptr)
|
||||||
{
|
{
|
||||||
loginSessions[sock] = CNLoginData();
|
//web api takes care of registration
|
||||||
loginSessions[sock].userID = Database::addAccount(userLogin, userPassword);
|
if (settings::USEWEBAPI)
|
||||||
loginSessions[sock].slot = 1;
|
errorCode = (int)LoginError::ID_DOESNT_EXIST;
|
||||||
success = true;
|
//without web api, create new account
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loginSessions[sock] = CNLoginData();
|
||||||
|
loginSessions[sock].userID = Database::addAccount(userLogin, userPassword);
|
||||||
|
loginSessions[sock].slot = 1;
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// if user exists, check if password is correct
|
// if user exists, check if password is correct
|
||||||
else if (CNLoginServer::isPasswordCorrect(findUser->Password, userPassword))
|
else if (CNLoginServer::isPasswordCorrect(findUser->Password, userPassword))
|
||||||
{
|
{
|
||||||
|
/*calling this here to timestamp login attempt,
|
||||||
|
* in order to make duplicate exit sanity check work*/
|
||||||
|
Database::updateSelected(findUser->AccountID, findUser->Selected);
|
||||||
// check if account isn't currently in use
|
// check if account isn't currently in use
|
||||||
if (CNLoginServer::isAccountInUse(findUser->AccountID) ||
|
if (CNLoginServer::isAccountInUse(findUser->AccountID) ||
|
||||||
PlayerManager::isAccountInUse(findUser->AccountID))
|
PlayerManager::isAccountInUse(findUser->AccountID))
|
||||||
@ -100,7 +110,7 @@ void CNLoginServer::handlePacket(CNSocket* sock, CNPacketData* data) {
|
|||||||
resp.iPaymentFlag = 1;
|
resp.iPaymentFlag = 1;
|
||||||
resp.iOpenBetaFlag = 0;
|
resp.iOpenBetaFlag = 0;
|
||||||
resp.uiSvrTime = getTime();
|
resp.uiSvrTime = getTime();
|
||||||
|
|
||||||
// send the resp in with original key
|
// send the resp in with original key
|
||||||
sock->sendPacket((void*)&resp, P_LS2CL_REP_LOGIN_SUCC, sizeof(sP_LS2CL_REP_LOGIN_SUCC));
|
sock->sendPacket((void*)&resp, P_LS2CL_REP_LOGIN_SUCC, sizeof(sP_LS2CL_REP_LOGIN_SUCC));
|
||||||
|
|
||||||
@ -144,10 +154,8 @@ void CNLoginServer::handlePacket(CNSocket* sock, CNPacketData* data) {
|
|||||||
// Failure
|
// Failure
|
||||||
else {
|
else {
|
||||||
INITSTRUCT(sP_LS2CL_REP_LOGIN_FAIL, resp);
|
INITSTRUCT(sP_LS2CL_REP_LOGIN_FAIL, resp);
|
||||||
|
U8toU16(userLogin, resp.szID);
|
||||||
memcpy(resp.szID, login->szID, sizeof(char16_t) * 33);
|
|
||||||
resp.iErrorCode = errorCode;
|
resp.iErrorCode = errorCode;
|
||||||
|
|
||||||
sock->sendPacket((void*)&resp, P_LS2CL_REP_LOGIN_FAIL, sizeof(sP_LS2CL_REP_LOGIN_FAIL));
|
sock->sendPacket((void*)&resp, P_LS2CL_REP_LOGIN_FAIL, sizeof(sP_LS2CL_REP_LOGIN_FAIL));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,8 +356,22 @@ void CNLoginServer::handlePacket(CNSocket* sock, CNPacketData* data) {
|
|||||||
|
|
||||||
sP_CL2LS_REQ_PC_EXIT_DUPLICATE* exit = (sP_CL2LS_REQ_PC_EXIT_DUPLICATE*)data->buf;
|
sP_CL2LS_REQ_PC_EXIT_DUPLICATE* exit = (sP_CL2LS_REQ_PC_EXIT_DUPLICATE*)data->buf;
|
||||||
auto account = Database::findAccount(U16toU8(exit->szID));
|
auto account = Database::findAccount(U16toU8(exit->szID));
|
||||||
|
//sanity check
|
||||||
if (account == nullptr)
|
if (account == nullptr)
|
||||||
|
{
|
||||||
|
std::cout << "[WARN] P_CL2LS_REQ_PC_EXIT_DUPLICATE submitted unknown username: " << exit->szID << std::endl;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
/* sanity check
|
||||||
|
* client is supposed to send us user password for verification,
|
||||||
|
* however it never sends it (>_<)
|
||||||
|
* therefore, we check if the account made a login attempt within last 30s
|
||||||
|
*/
|
||||||
|
if (account->LastLogin + 30000 < getTime())
|
||||||
|
{
|
||||||
|
std::cout << "[WARN] P_CL2LS_REQ_PC_EXIT_DUPLICATE submitted without a login attempt on: " << exit->szID << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
int accountId = account->AccountID;
|
int accountId = account->AccountID;
|
||||||
if (!exitDuplicate(accountId))
|
if (!exitDuplicate(accountId))
|
||||||
@ -407,11 +429,19 @@ bool CNLoginServer::exitDuplicate(int accountId) {
|
|||||||
|
|
||||||
bool CNLoginServer::isLoginDataGood(std::string login, std::string password) {
|
bool CNLoginServer::isLoginDataGood(std::string login, std::string password) {
|
||||||
std::regex loginRegex("[a-zA-Z0-9_-]{4,32}");
|
std::regex loginRegex("[a-zA-Z0-9_-]{4,32}");
|
||||||
|
//web api sends password hashed, so we don't check it
|
||||||
|
if (settings::USEWEBAPI)
|
||||||
|
return (std::regex_match(login, loginRegex));
|
||||||
|
//without web api
|
||||||
std::regex passwordRegex("[a-zA-Z0-9!@#$%^&*()_+]{8,32}");
|
std::regex passwordRegex("[a-zA-Z0-9!@#$%^&*()_+]{8,32}");
|
||||||
return (std::regex_match(login, loginRegex) && std::regex_match(password, passwordRegex));
|
return (std::regex_match(login, loginRegex) && std::regex_match(password, passwordRegex));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CNLoginServer::isPasswordCorrect(std::string actualPassword, std::string tryPassword) {
|
bool CNLoginServer::isPasswordCorrect(std::string actualPassword, std::string tryPassword) {
|
||||||
|
//web api sends password already hashed
|
||||||
|
if (settings::USEWEBAPI)
|
||||||
|
return actualPassword == tryPassword;
|
||||||
|
//without web api
|
||||||
return BCrypt::validatePassword(tryPassword, actualPassword);
|
return BCrypt::validatePassword(tryPassword, actualPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
202
src/Database.cpp
202
src/Database.cpp
@ -18,7 +18,9 @@ auto db = make_storage("database.db",
|
|||||||
make_column("AccountID", &Database::Account::AccountID, autoincrement(), primary_key()),
|
make_column("AccountID", &Database::Account::AccountID, autoincrement(), primary_key()),
|
||||||
make_column("Login", &Database::Account::Login),
|
make_column("Login", &Database::Account::Login),
|
||||||
make_column("Password", &Database::Account::Password),
|
make_column("Password", &Database::Account::Password),
|
||||||
make_column("Selected", &Database::Account::Selected)
|
make_column("Selected", &Database::Account::Selected),
|
||||||
|
make_column("Created", &Database::Account::Created),
|
||||||
|
make_column("LastLogin", &Database::Account::LastLogin)
|
||||||
),
|
),
|
||||||
make_table("Players",
|
make_table("Players",
|
||||||
make_column("PlayerID", &Database::DbPlayer::PlayerID, autoincrement(), primary_key()),
|
make_column("PlayerID", &Database::DbPlayer::PlayerID, autoincrement(), primary_key()),
|
||||||
@ -26,6 +28,8 @@ auto db = make_storage("database.db",
|
|||||||
make_column("Slot", &Database::DbPlayer::slot),
|
make_column("Slot", &Database::DbPlayer::slot),
|
||||||
make_column("Firstname", &Database::DbPlayer::FirstName, collate_nocase()),
|
make_column("Firstname", &Database::DbPlayer::FirstName, collate_nocase()),
|
||||||
make_column("LastName", &Database::DbPlayer::LastName, 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("Level", &Database::DbPlayer::Level),
|
||||||
make_column("Nano1", &Database::DbPlayer::Nano1),
|
make_column("Nano1", &Database::DbPlayer::Nano1),
|
||||||
make_column("Nano2", &Database::DbPlayer::Nano2),
|
make_column("Nano2", &Database::DbPlayer::Nano2),
|
||||||
@ -51,7 +55,14 @@ auto db = make_storage("database.db",
|
|||||||
make_column("isGM", &Database::DbPlayer::isGM),
|
make_column("isGM", &Database::DbPlayer::isGM),
|
||||||
make_column("FusionMatter", &Database::DbPlayer::FusionMatter),
|
make_column("FusionMatter", &Database::DbPlayer::FusionMatter),
|
||||||
make_column("Taros", &Database::DbPlayer::Taros),
|
make_column("Taros", &Database::DbPlayer::Taros),
|
||||||
make_column("Quests", &Database::DbPlayer::QuestFlag)
|
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_table("Inventory",
|
||||||
make_column("PlayerId", &Database::Inventory::playerId),
|
make_column("PlayerId", &Database::Inventory::playerId),
|
||||||
@ -66,6 +77,13 @@ auto db = make_storage("database.db",
|
|||||||
make_column("Id", &Database::Nano::iID),
|
make_column("Id", &Database::Nano::iID),
|
||||||
make_column("Skill", &Database::Nano::iSkillID),
|
make_column("Skill", &Database::Nano::iSkillID),
|
||||||
make_column("Stamina", &Database::Nano::iStamina)
|
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)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -78,25 +96,48 @@ void Database::open()
|
|||||||
// this parameter means it will try to preserve data during migration
|
// this parameter means it will try to preserve data during migration
|
||||||
bool preserve = true;
|
bool preserve = true;
|
||||||
db.sync_schema(preserve);
|
db.sync_schema(preserve);
|
||||||
DEBUGLOG(
|
std::cout << "[INFO] Database in operation ";
|
||||||
std::cout << "[DB] Database in operation" << std::endl;
|
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)
|
int Database::addAccount(std::string login, std::string password)
|
||||||
{
|
{
|
||||||
password = BCrypt::generateHash(password);
|
password = BCrypt::generateHash(password);
|
||||||
Account x = {};
|
Account account = {};
|
||||||
x.Login = login;
|
account.Login = login;
|
||||||
x.Password = password;
|
account.Password = password;
|
||||||
x.Selected = 1;
|
account.Selected = 1;
|
||||||
return db.insert(x);
|
account.Created = getTime();
|
||||||
|
return db.insert(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::updateSelected(int accountId, int slot)
|
void Database::updateSelected(int accountId, int slot)
|
||||||
{
|
{
|
||||||
Account acc = db.get<Account>(accountId);
|
Account acc = db.get<Account>(accountId);
|
||||||
acc.Selected = slot;
|
acc.Selected = slot;
|
||||||
|
//timestamp
|
||||||
|
acc.LastLogin = getTime();
|
||||||
db.update(acc);
|
db.update(acc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +170,9 @@ int Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID)
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
DbPlayer create = {};
|
DbPlayer create = {};
|
||||||
|
|
||||||
|
//set timestamp
|
||||||
|
create.Created = getTime();
|
||||||
// save packet data
|
// save packet data
|
||||||
create.FirstName = U16toU8(save->szFirstName);
|
create.FirstName = U16toU8(save->szFirstName);
|
||||||
create.LastName = U16toU8(save->szLastName);
|
create.LastName = U16toU8(save->szLastName);
|
||||||
@ -165,6 +208,8 @@ int Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID)
|
|||||||
create.z_coordinates = settings::SPAWN_Z;
|
create.z_coordinates = settings::SPAWN_Z;
|
||||||
create.angle = settings::SPAWN_ANGLE;
|
create.angle = settings::SPAWN_ANGLE;
|
||||||
create.QuestFlag = std::vector<char>();
|
create.QuestFlag = std::vector<char>();
|
||||||
|
//set mentor to computress
|
||||||
|
create.Mentor = 5;
|
||||||
|
|
||||||
return db.insert(create);
|
return db.insert(create);
|
||||||
}
|
}
|
||||||
@ -285,7 +330,8 @@ void Database::changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save) {
|
|||||||
|
|
||||||
Database::DbPlayer Database::playerToDb(Player *player)
|
Database::DbPlayer Database::playerToDb(Player *player)
|
||||||
{
|
{
|
||||||
DbPlayer result = {}; // fixes some weird memory errors, this zeros out the members (not the padding inbetween though)
|
//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.PlayerID = player->iID;
|
||||||
result.AccountID = player->accountId;
|
result.AccountID = player->accountId;
|
||||||
@ -318,22 +364,24 @@ Database::DbPlayer Database::playerToDb(Player *player)
|
|||||||
result.Nano1 = player->equippedNanos[0];
|
result.Nano1 = player->equippedNanos[0];
|
||||||
result.Nano2 = player->equippedNanos[1];
|
result.Nano2 = player->equippedNanos[1];
|
||||||
result.Nano3 = player->equippedNanos[2];
|
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;
|
||||||
|
|
||||||
// quests
|
// finished quests: parsing to blob
|
||||||
result.QuestFlag = std::vector<char>();
|
result.QuestFlag = std::vector<char>();
|
||||||
// parsing long array to char vector
|
|
||||||
for (int i=0; i<16; i++)
|
for (int i=0; i<16; i++)
|
||||||
{
|
{
|
||||||
int64_t temp = player->aQuestFlag[i];
|
int64_t flag = player->aQuestFlag[i];
|
||||||
for (int j = 0; j < 8; j++) {
|
appendBlob(&result.QuestFlag, flag);
|
||||||
int64_t check2 = (temp >> (8 * (7 - j)));
|
|
||||||
char toadd = check2;
|
|
||||||
result.QuestFlag.push_back(
|
|
||||||
toadd
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
//timestamp
|
||||||
|
result.LastLogin = getTime();
|
||||||
|
result.Created = getDbPlayerById(player->iID).Created;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -370,28 +418,31 @@ Player Database::DbToPlayer(DbPlayer player) {
|
|||||||
result.angle = player.angle;
|
result.angle = player.angle;
|
||||||
result.money = player.Taros;
|
result.money = player.Taros;
|
||||||
result.fusionmatter = player.FusionMatter;
|
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[0] = player.Nano1;
|
||||||
result.equippedNanos[1] = player.Nano2;
|
result.equippedNanos[1] = player.Nano2;
|
||||||
result.equippedNanos[2] = player.Nano3;
|
result.equippedNanos[2] = player.Nano3;
|
||||||
|
|
||||||
|
result.iWarpLocationFlag = player.WarpLocationFlag;
|
||||||
|
result.aSkywayLocationFlag[0] = player.SkywayLocationFlag1;
|
||||||
|
result.aSkywayLocationFlag[1] = player.SkywayLocationFlag2;
|
||||||
|
|
||||||
Database::getInventory(&result);
|
Database::getInventory(&result);
|
||||||
Database::getNanos(&result);
|
Database::getNanos(&result);
|
||||||
|
Database::getQuests(&result);
|
||||||
|
|
||||||
std::vector<char>::iterator it = player.QuestFlag.begin();
|
std::vector<char>::iterator it = player.QuestFlag.begin();
|
||||||
for (int i = 0; i < 16; i++)
|
for (int i = 0; i < 16; i++)
|
||||||
{
|
{
|
||||||
if (it == player.QuestFlag.end())
|
if (it == player.QuestFlag.end())
|
||||||
break;
|
break;
|
||||||
|
result.aQuestFlag[i] = blobToInt64(it);
|
||||||
int64_t toAdd = 0;
|
//move iterator to the next flag
|
||||||
for (int j = 0; j < 8; j++) {
|
it += 8;
|
||||||
int64_t temp = *it;
|
|
||||||
int64_t check2 = (temp << (8 * (7 - j)));
|
|
||||||
toAdd += check2;
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
result.aQuestFlag[i] = toAdd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -417,6 +468,7 @@ void Database::updatePlayer(Player *player) {
|
|||||||
db.update(toUpdate);
|
db.update(toUpdate);
|
||||||
updateInventory(player);
|
updateInventory(player);
|
||||||
updateNanos(player);
|
updateNanos(player);
|
||||||
|
updateQuests(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::updateInventory(Player *player){
|
void Database::updateInventory(Player *player){
|
||||||
@ -468,8 +520,23 @@ void Database::updateInventory(Player *player){
|
|||||||
db.insert(toAdd);
|
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();
|
db.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::updateNanos(Player *player) {
|
void Database::updateNanos(Player *player) {
|
||||||
// start transaction
|
// start transaction
|
||||||
db.begin_transaction();
|
db.begin_transaction();
|
||||||
@ -492,6 +559,30 @@ void Database::updateNanos(Player *player) {
|
|||||||
}
|
}
|
||||||
db.commit();
|
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) {
|
void Database::getInventory(Player* player) {
|
||||||
// get items from DB
|
// get items from DB
|
||||||
auto items = db.get_all<Inventory>(
|
auto items = db.get_all<Inventory>(
|
||||||
@ -505,15 +596,18 @@ void Database::getInventory(Player* player) {
|
|||||||
toSet.iOpt = current.Opt;
|
toSet.iOpt = current.Opt;
|
||||||
toSet.iTimeLimit = current.TimeLimit;
|
toSet.iTimeLimit = current.TimeLimit;
|
||||||
// assign to proper arrays
|
// assign to proper arrays
|
||||||
if (current.slot <= AEQUIP_COUNT)
|
if (current.slot < AEQUIP_COUNT)
|
||||||
player->Equip[current.slot] = toSet;
|
player->Equip[current.slot] = toSet;
|
||||||
else if (current.slot <= (AEQUIP_COUNT + AINVEN_COUNT))
|
else if (current.slot < (AEQUIP_COUNT + AINVEN_COUNT))
|
||||||
player->Inven[current.slot - AEQUIP_COUNT] = toSet;
|
player->Inven[current.slot - AEQUIP_COUNT] = toSet;
|
||||||
else
|
else if (current.slot < (AEQUIP_COUNT + AINVEN_COUNT + ABANK_COUNT))
|
||||||
player->Bank[current.slot - AEQUIP_COUNT - AINVEN_COUNT] = toSet;
|
player->Bank[current.slot - AEQUIP_COUNT - AINVEN_COUNT] = toSet;
|
||||||
|
else
|
||||||
|
player->QInven[current.slot - AEQUIP_COUNT - AINVEN_COUNT - ABANK_COUNT] = toSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::getNanos(Player* player) {
|
void Database::getNanos(Player* player) {
|
||||||
// get from DB
|
// get from DB
|
||||||
auto nanos = db.get_all<Nano>(
|
auto nanos = db.get_all<Nano>(
|
||||||
@ -527,4 +621,42 @@ void Database::getNanos(Player* player) {
|
|||||||
toSet->iStamina = current.iStamina;
|
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 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
|
||||||
|
@ -13,6 +13,8 @@ namespace Database {
|
|||||||
std::string Login;
|
std::string Login;
|
||||||
std::string Password;
|
std::string Password;
|
||||||
int Selected;
|
int Selected;
|
||||||
|
uint64_t Created;
|
||||||
|
uint64_t LastLogin;
|
||||||
};
|
};
|
||||||
struct Inventory
|
struct Inventory
|
||||||
{
|
{
|
||||||
@ -36,6 +38,8 @@ namespace Database {
|
|||||||
short int slot;
|
short int slot;
|
||||||
std::string FirstName;
|
std::string FirstName;
|
||||||
std::string LastName;
|
std::string LastName;
|
||||||
|
uint64_t Created;
|
||||||
|
uint64_t LastLogin;
|
||||||
short int Level;
|
short int Level;
|
||||||
int Nano1;
|
int Nano1;
|
||||||
int Nano2;
|
int Nano2;
|
||||||
@ -62,15 +66,30 @@ namespace Database {
|
|||||||
int z_coordinates;
|
int z_coordinates;
|
||||||
int angle;
|
int angle;
|
||||||
short int PCState;
|
short int PCState;
|
||||||
|
int BatteryW;
|
||||||
|
int BatteryN;
|
||||||
|
int16_t Mentor;
|
||||||
std::vector<char> QuestFlag;
|
std::vector<char> QuestFlag;
|
||||||
|
int32_t CurrentMissionID;
|
||||||
|
int32_t WarpLocationFlag;
|
||||||
|
int64_t SkywayLocationFlag1;
|
||||||
|
int64_t SkywayLocationFlag2;
|
||||||
|
};
|
||||||
|
struct DbQuest {
|
||||||
|
int PlayerId;
|
||||||
|
int32_t TaskId;
|
||||||
|
int RemainingNPCCount1;
|
||||||
|
int RemainingNPCCount2;
|
||||||
|
int RemainingNPCCount3;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#pragma endregion DatabaseStructs
|
#pragma endregion DatabaseStructs
|
||||||
|
|
||||||
// handles migrations
|
// handles migrations
|
||||||
void open();
|
void open();
|
||||||
|
int getAccountsCount();
|
||||||
|
int getPlayersCount();
|
||||||
// returns ID
|
// returns ID
|
||||||
int addAccount(std::string login, std::string password);
|
int addAccount(std::string login, std::string password);
|
||||||
void updateSelected(int accountId, int playerId);
|
void updateSelected(int accountId, int playerId);
|
||||||
@ -104,7 +123,13 @@ namespace Database {
|
|||||||
void updatePlayer(Player *player);
|
void updatePlayer(Player *player);
|
||||||
void updateInventory(Player *player);
|
void updateInventory(Player *player);
|
||||||
void updateNanos(Player *player);
|
void updateNanos(Player *player);
|
||||||
|
void updateQuests(Player* player);
|
||||||
|
|
||||||
void getInventory(Player* player);
|
void getInventory(Player* player);
|
||||||
void getNanos(Player* player);
|
void getNanos(Player* player);
|
||||||
|
void getQuests(Player* player);
|
||||||
|
|
||||||
|
//parsing blobs
|
||||||
|
void appendBlob(std::vector<char>*blob, int64_t input);
|
||||||
|
int64_t blobToInt64(std::vector<char>::iterator it);
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,9 @@ void ItemManager::itemMoveHandler(CNSocket* sock, CNPacketData* data) {
|
|||||||
case SlotType::BANK:
|
case SlotType::BANK:
|
||||||
fromItem = &plr.plr->Bank[itemmove->iFromSlotNum];
|
fromItem = &plr.plr->Bank[itemmove->iFromSlotNum];
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
std::cout << "[WARN] MoveItem submitted unknown Item Type?! " << itemmove->eFrom << std::endl;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the toItem
|
// get the toItem
|
||||||
@ -83,6 +86,9 @@ void ItemManager::itemMoveHandler(CNSocket* sock, CNPacketData* data) {
|
|||||||
case SlotType::BANK:
|
case SlotType::BANK:
|
||||||
toItem = &plr.plr->Bank[itemmove->iToSlotNum];
|
toItem = &plr.plr->Bank[itemmove->iToSlotNum];
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
std::cout << "[WARN] MoveItem submitted unknown Item Type?! " << itemmove->eTo << std::endl;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// save items to response
|
// save items to response
|
||||||
|
@ -31,14 +31,19 @@ void MissionManager::taskStart(CNSocket* sock, CNPacketData* data) {
|
|||||||
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_START_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_START_SUCC));
|
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_START_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_START_SUCC));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TaskData& task = *Tasks[missionData->iTaskNum];
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < ACTIVE_MISSION_COUNT; i++) {
|
for (i = 0; i < ACTIVE_MISSION_COUNT; i++) {
|
||||||
if (plr->tasks[i] == 0) {
|
if (plr->tasks[i] == 0) {
|
||||||
plr->tasks[i] = missionData->iTaskNum;
|
plr->tasks[i] = missionData->iTaskNum;
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
plr->RemainingNPCCount[i][j] = (int)task["m_iCSUNumToKill"][j];
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == ACTIVE_MISSION_COUNT - 1 && plr->tasks[i] != missionData->iTaskNum) {
|
if (i == ACTIVE_MISSION_COUNT - 1 && plr->tasks[i] != missionData->iTaskNum) {
|
||||||
std::cout << "[WARN] Player has more than 6 active missions!?" << std::endl;
|
std::cout << "[WARN] Player has more than 6 active missions!?" << std::endl;
|
||||||
}
|
}
|
||||||
@ -80,6 +85,7 @@ void MissionManager::taskEnd(CNSocket* sock, CNPacketData* data) {
|
|||||||
* iSUInstancename is the number of items to give. It is usually negative at the end of
|
* iSUInstancename is the number of items to give. It is usually negative at the end of
|
||||||
* a mission, to clean up its quest items.
|
* a mission, to clean up its quest items.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
if (task["m_iSUItem"][i] != 0)
|
if (task["m_iSUItem"][i] != 0)
|
||||||
dropQuestItem(sock, missionData->iTaskNum, task["m_iSUInstancename"][i], task["m_iSUItem"][i], 0);
|
dropQuestItem(sock, missionData->iTaskNum, task["m_iSUInstancename"][i], task["m_iSUItem"][i], 0);
|
||||||
@ -94,7 +100,12 @@ void MissionManager::taskEnd(CNSocket* sock, CNPacketData* data) {
|
|||||||
int i;
|
int i;
|
||||||
for (i = 0; i < ACTIVE_MISSION_COUNT; i++) {
|
for (i = 0; i < ACTIVE_MISSION_COUNT; i++) {
|
||||||
if (plr->tasks[i] == missionData->iTaskNum)
|
if (plr->tasks[i] == missionData->iTaskNum)
|
||||||
|
{
|
||||||
plr->tasks[i] = 0;
|
plr->tasks[i] = 0;
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
plr->RemainingNPCCount[i][j] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (i == ACTIVE_MISSION_COUNT - 1 && plr->tasks[i] != 0) {
|
if (i == ACTIVE_MISSION_COUNT - 1 && plr->tasks[i] != 0) {
|
||||||
std::cout << "[WARN] Player completed non-active mission!?" << std::endl;
|
std::cout << "[WARN] Player completed non-active mission!?" << std::endl;
|
||||||
@ -105,6 +116,8 @@ void MissionManager::taskEnd(CNSocket* sock, CNPacketData* data) {
|
|||||||
{
|
{
|
||||||
// save completed mission on player
|
// save completed mission on player
|
||||||
saveMission(plr, (int)(task["m_iHMissionID"])-1);
|
saveMission(plr, (int)(task["m_iHMissionID"])-1);
|
||||||
|
// remove current mission
|
||||||
|
plr->CurrentMissionID = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_END_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_END_SUCC));
|
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_END_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_END_SUCC));
|
||||||
@ -116,9 +129,11 @@ void MissionManager::setMission(CNSocket* sock, CNPacketData* data) {
|
|||||||
|
|
||||||
sP_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID* missionData = (sP_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID*)data->buf;
|
sP_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID* missionData = (sP_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID*)data->buf;
|
||||||
INITSTRUCT(sP_FE2CL_REP_PC_SET_CURRENT_MISSION_ID, response);
|
INITSTRUCT(sP_FE2CL_REP_PC_SET_CURRENT_MISSION_ID, response);
|
||||||
|
|
||||||
response.iCurrentMissionID = missionData->iCurrentMissionID;
|
response.iCurrentMissionID = missionData->iCurrentMissionID;
|
||||||
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_SET_CURRENT_MISSION_ID, sizeof(sP_FE2CL_REP_PC_SET_CURRENT_MISSION_ID));
|
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_SET_CURRENT_MISSION_ID, sizeof(sP_FE2CL_REP_PC_SET_CURRENT_MISSION_ID));
|
||||||
|
Player* plr = PlayerManager::getPlayer(sock);
|
||||||
|
plr->CurrentMissionID = missionData->iCurrentMissionID;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MissionManager::quitMission(CNSocket* sock, CNPacketData* data) {
|
void MissionManager::quitMission(CNSocket* sock, CNPacketData* data) {
|
||||||
@ -133,17 +148,24 @@ void MissionManager::quitMission(CNSocket* sock, CNPacketData* data) {
|
|||||||
int i;
|
int i;
|
||||||
for (i = 0; i < ACTIVE_MISSION_COUNT; i++) {
|
for (i = 0; i < ACTIVE_MISSION_COUNT; i++) {
|
||||||
if (plr->tasks[i] == missionData->iTaskNum)
|
if (plr->tasks[i] == missionData->iTaskNum)
|
||||||
|
{
|
||||||
plr->tasks[i] = 0;
|
plr->tasks[i] = 0;
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
plr->RemainingNPCCount[i][j] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (i == ACTIVE_MISSION_COUNT - 1 && plr->tasks[i] != 0) {
|
if (i == ACTIVE_MISSION_COUNT - 1 && plr->tasks[i] != 0) {
|
||||||
std::cout << "[WARN] Player quit non-active mission!?" << std::endl;
|
std::cout << "[WARN] Player quit non-active mission!?" << std::endl;
|
||||||
}
|
}
|
||||||
|
// remove current mission
|
||||||
|
plr->CurrentMissionID = 0;
|
||||||
|
|
||||||
TaskData& task = *Tasks[missionData->iTaskNum];
|
TaskData& task = *Tasks[missionData->iTaskNum];
|
||||||
|
|
||||||
// clean up quest items
|
// clean up quest items
|
||||||
for (i = 0; i < 3; i++) {
|
for (i = 0; i < 3; i++) {
|
||||||
if (task["m_iSUItem"][i] == 0 && task["m_iCSUItem"][i] == 0)
|
if (task["m_iSUItem"][i] == 0 && task["m_iCSUItemID"][i] == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -151,7 +173,7 @@ void MissionManager::quitMission(CNSocket* sock, CNPacketData* data) {
|
|||||||
* slot later items will be placed in.
|
* slot later items will be placed in.
|
||||||
*/
|
*/
|
||||||
for (int j = 0; j < AQINVEN_COUNT; j++)
|
for (int j = 0; j < AQINVEN_COUNT; j++)
|
||||||
if (plr->QInven[j].iID == task["m_iSUItem"][i] || plr->QInven[j].iID == task["m_iCSUItem"][i])
|
if (plr->QInven[j].iID == task["m_iSUItem"][i] || plr->QInven[j].iID == task["m_iCSUItemID"][i])
|
||||||
memset(&plr->QInven[j], 0, sizeof(sItemBase));
|
memset(&plr->QInven[j], 0, sizeof(sItemBase));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,10 +326,12 @@ void MissionManager::mobKilled(CNSocket *sock, int mobid) {
|
|||||||
|
|
||||||
// acknowledge killing of mission mob...
|
// acknowledge killing of mission mob...
|
||||||
if (task["m_iCSUNumToKill"][j] != 0)
|
if (task["m_iCSUNumToKill"][j] != 0)
|
||||||
|
{
|
||||||
missionmob = true;
|
missionmob = true;
|
||||||
|
plr->RemainingNPCCount[i][j]--;
|
||||||
|
}
|
||||||
// drop quest item
|
// drop quest item
|
||||||
if (task["m_iCSUItemNumNeeded"][j] != 0) {
|
if (task["m_iCSUItemNumNeeded"][j] != 0 && !isQuestItemFull(sock, task["m_iCSUItemID"][j], task["m_iCSUItemNumNeeded"][j]) ) {
|
||||||
bool drop = rand() % 100 < task["m_iSTItemDropRate"][j];
|
bool drop = rand() % 100 < task["m_iSTItemDropRate"][j];
|
||||||
if (drop) {
|
if (drop) {
|
||||||
// XXX: are CSUItemID and CSTItemID the same?
|
// XXX: are CSUItemID and CSTItemID the same?
|
||||||
@ -332,8 +356,26 @@ void MissionManager::mobKilled(CNSocket *sock, int mobid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MissionManager::saveMission(Player* player, int missionId) {
|
void MissionManager::saveMission(Player* player, int missionId) {
|
||||||
|
//sanity check missionID so we don't get exceptions
|
||||||
|
if (missionId < 0 || missionId>1023) {
|
||||||
|
std::cout << "[WARN] Client submitted invalid missionId: " <<missionId<< std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Missions are stored in int_64t array
|
// Missions are stored in int_64t array
|
||||||
int row = missionId / 64;
|
int row = missionId / 64;
|
||||||
int column = missionId % 64;
|
int column = missionId % 64;
|
||||||
player->aQuestFlag[row] |= (1ULL << column);
|
player->aQuestFlag[row] |= (1ULL << column);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MissionManager::isQuestItemFull(CNSocket* sock, int itemId, int itemCount) {
|
||||||
|
Player* plr = PlayerManager::getPlayer(sock);
|
||||||
|
int slot = findQSlot(plr, itemId);
|
||||||
|
if (slot == -1) {
|
||||||
|
// this should never happen
|
||||||
|
std::cout << "[WARN] Player has no room for quest item!?" << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (itemCount == plr->QInven[slot].iOpt);
|
||||||
|
}
|
||||||
|
@ -47,6 +47,8 @@ namespace MissionManager {
|
|||||||
|
|
||||||
int findQSlot(Player *plr, int id);
|
int findQSlot(Player *plr, int id);
|
||||||
void dropQuestItem(CNSocket *sock, int task, int count, int id, int mobid);
|
void dropQuestItem(CNSocket *sock, int task, int count, int id, int mobid);
|
||||||
|
//checks if player doesn't have n/n quest items
|
||||||
|
bool isQuestItemFull(CNSocket* sock, int itemId, int itemCount);
|
||||||
int giveMissionReward(CNSocket *sock, int task);
|
int giveMissionReward(CNSocket *sock, int task);
|
||||||
|
|
||||||
void mobKilled(CNSocket *sock, int mobid);
|
void mobKilled(CNSocket *sock, int mobid);
|
||||||
|
@ -19,6 +19,7 @@ struct Player {
|
|||||||
int level;
|
int level;
|
||||||
int HP;
|
int HP;
|
||||||
int slot; // player slot, not nano slot
|
int slot; // player slot, not nano slot
|
||||||
|
int16_t mentor;
|
||||||
int32_t money;
|
int32_t money;
|
||||||
int32_t fusionmatter;
|
int32_t fusionmatter;
|
||||||
int32_t batteryW;
|
int32_t batteryW;
|
||||||
@ -45,5 +46,7 @@ struct Player {
|
|||||||
|
|
||||||
int64_t aQuestFlag[16];
|
int64_t aQuestFlag[16];
|
||||||
int tasks[ACTIVE_MISSION_COUNT];
|
int tasks[ACTIVE_MISSION_COUNT];
|
||||||
|
int RemainingNPCCount[ACTIVE_MISSION_COUNT][3];
|
||||||
sItemBase QInven[AQINVEN_COUNT];
|
sItemBase QInven[AQINVEN_COUNT];
|
||||||
|
int32_t CurrentMissionID;
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "NPCManager.hpp"
|
#include "NPCManager.hpp"
|
||||||
#include "CNShardServer.hpp"
|
#include "CNShardServer.hpp"
|
||||||
#include "CNShared.hpp"
|
#include "CNShared.hpp"
|
||||||
|
#include "MissionManager.hpp"
|
||||||
|
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
|
|
||||||
@ -207,24 +208,30 @@ void PlayerManager::enterPlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
response.PCLoadData2CL.iLevel = plr.level;
|
response.PCLoadData2CL.iLevel = plr.level;
|
||||||
response.PCLoadData2CL.iCandy = plr.money;
|
response.PCLoadData2CL.iCandy = plr.money;
|
||||||
response.PCLoadData2CL.iFusionMatter = plr.fusionmatter;
|
response.PCLoadData2CL.iFusionMatter = plr.fusionmatter;
|
||||||
response.PCLoadData2CL.iMentor = 5; // Computress
|
response.PCLoadData2CL.iMentor = plr.mentor;
|
||||||
response.PCLoadData2CL.iMentorCount = 1; // how many guides the player has had
|
response.PCLoadData2CL.iMentorCount = 1; // how many guides the player has had
|
||||||
response.PCLoadData2CL.iMapNum = 0;
|
|
||||||
response.PCLoadData2CL.iX = plr.x;
|
response.PCLoadData2CL.iX = plr.x;
|
||||||
response.PCLoadData2CL.iY = plr.y;
|
response.PCLoadData2CL.iY = plr.y;
|
||||||
response.PCLoadData2CL.iZ = plr.z;
|
response.PCLoadData2CL.iZ = plr.z;
|
||||||
response.PCLoadData2CL.iAngle = plr.angle;
|
response.PCLoadData2CL.iAngle = plr.angle;
|
||||||
|
response.PCLoadData2CL.iBatteryN = plr.batteryN;
|
||||||
|
response.PCLoadData2CL.iBatteryW = plr.batteryW;
|
||||||
|
response.PCLoadData2CL.iBuddyWarpTime = 60; //sets 60s warp cooldown on login
|
||||||
|
|
||||||
response.PCLoadData2CL.iActiveNanoSlotNum = -1;
|
response.PCLoadData2CL.iActiveNanoSlotNum = -1;
|
||||||
response.PCLoadData2CL.iFatigue = 50;
|
response.PCLoadData2CL.iFatigue = 50;
|
||||||
response.PCLoadData2CL.PCStyle = plr.PCStyle;
|
response.PCLoadData2CL.PCStyle = plr.PCStyle;
|
||||||
response.PCLoadData2CL.PCStyle2 = plr.PCStyle2;
|
|
||||||
|
//client doesnt read this, it gets it from charinfo
|
||||||
|
//response.PCLoadData2CL.PCStyle2 = plr.PCStyle2;
|
||||||
// inventory
|
// inventory
|
||||||
for (int i = 0; i < AEQUIP_COUNT; i++)
|
for (int i = 0; i < AEQUIP_COUNT; i++)
|
||||||
response.PCLoadData2CL.aEquip[i] = plr.Equip[i];
|
response.PCLoadData2CL.aEquip[i] = plr.Equip[i];
|
||||||
|
|
||||||
for (int i = 0; i < AINVEN_COUNT; i++)
|
for (int i = 0; i < AINVEN_COUNT; i++)
|
||||||
response.PCLoadData2CL.aInven[i] = plr.Inven[i];
|
response.PCLoadData2CL.aInven[i] = plr.Inven[i];
|
||||||
|
// quest inventory
|
||||||
|
for (int i = 0; i < AQINVEN_COUNT; i++)
|
||||||
|
response.PCLoadData2CL.aQInven[i] = plr.QInven[i];
|
||||||
// nanos
|
// nanos
|
||||||
for (int i = 1; i < SIZEOF_NANO_BANK_SLOT; i++) {
|
for (int i = 1; i < SIZEOF_NANO_BANK_SLOT; i++) {
|
||||||
response.PCLoadData2CL.aNanoBank[i] = plr.Nanos[i];
|
response.PCLoadData2CL.aNanoBank[i] = plr.Nanos[i];
|
||||||
@ -232,8 +239,28 @@ void PlayerManager::enterPlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
response.PCLoadData2CL.aNanoSlots[i] = plr.equippedNanos[i];
|
response.PCLoadData2CL.aNanoSlots[i] = plr.equippedNanos[i];
|
||||||
}
|
}
|
||||||
|
//missions in progress
|
||||||
|
for (int i = 0; i < ACTIVE_MISSION_COUNT; i++) {
|
||||||
|
if (plr.tasks[i] == 0)
|
||||||
|
break;
|
||||||
|
response.PCLoadData2CL.aRunningQuest[i].m_aCurrTaskID = plr.tasks[i];
|
||||||
|
TaskData &task = *MissionManager::Tasks[plr.tasks[i]];
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
response.PCLoadData2CL.aRunningQuest[i].m_aKillNPCID[j] = (int)task["m_iCSUEnemyID"][j];
|
||||||
|
response.PCLoadData2CL.aRunningQuest[i].m_aKillNPCCount[j] = plr.RemainingNPCCount[i][j];
|
||||||
|
/*
|
||||||
|
* client doesn't care about NeededItem ID and Count,
|
||||||
|
* it gets Count from Quest Inventory
|
||||||
|
*
|
||||||
|
* KillNPCCount sets RemainEnemyNum in the client
|
||||||
|
* Yes, this is extraordinary stupid, even for Grigon
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response.PCLoadData2CL.iCurrentMissionID = plr.CurrentMissionID;
|
||||||
|
|
||||||
// missions
|
// completed missions
|
||||||
|
// the packet requires 32 items, but the client only checks the first 16 (shrug)
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
response.PCLoadData2CL.aQuestFlag[i] = plr.aQuestFlag[i];
|
response.PCLoadData2CL.aQuestFlag[i] = plr.aQuestFlag[i];
|
||||||
}
|
}
|
||||||
@ -719,8 +746,10 @@ void PlayerManager::changePlayerGuide(CNSocket *sock, CNPacketData *data) {
|
|||||||
resp.iMentor = pkt->iMentor;
|
resp.iMentor = pkt->iMentor;
|
||||||
resp.iMentorCnt = 1;
|
resp.iMentorCnt = 1;
|
||||||
resp.iFusionMatter = plr->fusionmatter; // no cost
|
resp.iFusionMatter = plr->fusionmatter; // no cost
|
||||||
|
|
||||||
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_CHANGE_MENTOR_SUCC, sizeof(sP_FE2CL_REP_PC_CHANGE_MENTOR_SUCC));
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_CHANGE_MENTOR_SUCC, sizeof(sP_FE2CL_REP_PC_CHANGE_MENTOR_SUCC));
|
||||||
|
//save it on player
|
||||||
|
plr->mentor = pkt->iMentor;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma region Helper methods
|
#pragma region Helper methods
|
||||||
|
@ -7,6 +7,7 @@ int settings::VERBOSITY = 1;
|
|||||||
|
|
||||||
int settings::LOGINPORT = 8001;
|
int settings::LOGINPORT = 8001;
|
||||||
bool settings::APPROVEALLNAMES = true;
|
bool settings::APPROVEALLNAMES = true;
|
||||||
|
bool settings::USEWEBAPI = false;
|
||||||
int settings::DBSAVEINTERVAL = 240;
|
int settings::DBSAVEINTERVAL = 240;
|
||||||
|
|
||||||
int settings::SHARDPORT = 8002;
|
int settings::SHARDPORT = 8002;
|
||||||
@ -41,9 +42,10 @@ void settings::init() {
|
|||||||
APPROVEALLNAMES = reader.GetBoolean("", "acceptallcustomnames", APPROVEALLNAMES);
|
APPROVEALLNAMES = reader.GetBoolean("", "acceptallcustomnames", APPROVEALLNAMES);
|
||||||
VERBOSITY = reader.GetInteger("", "verbosity", VERBOSITY);
|
VERBOSITY = reader.GetInteger("", "verbosity", VERBOSITY);
|
||||||
LOGINPORT = reader.GetInteger("login", "port", LOGINPORT);
|
LOGINPORT = reader.GetInteger("login", "port", LOGINPORT);
|
||||||
|
USEWEBAPI = reader.GetBoolean("login", "usewebapi", USEWEBAPI);
|
||||||
SHARDPORT = reader.GetInteger("shard", "port", SHARDPORT);
|
SHARDPORT = reader.GetInteger("shard", "port", SHARDPORT);
|
||||||
SHARDSERVERIP = reader.Get("shard", "ip", "127.0.0.1");
|
SHARDSERVERIP = reader.Get("shard", "ip", "127.0.0.1");
|
||||||
DBSAVEINTERVAL = reader.GetInteger("login", "dbsaveinterval", DBSAVEINTERVAL);
|
DBSAVEINTERVAL = reader.GetInteger("shard", "dbsaveinterval", DBSAVEINTERVAL);
|
||||||
TIMEOUT = reader.GetInteger("shard", "timeout", TIMEOUT);
|
TIMEOUT = reader.GetInteger("shard", "timeout", TIMEOUT);
|
||||||
PLAYERDISTANCE = reader.GetInteger("shard", "playerdistance", PLAYERDISTANCE);
|
PLAYERDISTANCE = reader.GetInteger("shard", "playerdistance", PLAYERDISTANCE);
|
||||||
NPCDISTANCE = reader.GetInteger("shard", "npcdistance", NPCDISTANCE);
|
NPCDISTANCE = reader.GetInteger("shard", "npcdistance", NPCDISTANCE);
|
||||||
|
@ -4,6 +4,7 @@ namespace settings {
|
|||||||
extern int VERBOSITY;
|
extern int VERBOSITY;
|
||||||
extern int LOGINPORT;
|
extern int LOGINPORT;
|
||||||
extern bool APPROVEALLNAMES;
|
extern bool APPROVEALLNAMES;
|
||||||
|
extern bool USEWEBAPI;
|
||||||
extern int DBSAVEINTERVAL;
|
extern int DBSAVEINTERVAL;
|
||||||
extern int SHARDPORT;
|
extern int SHARDPORT;
|
||||||
extern std::string SHARDSERVERIP;
|
extern std::string SHARDSERVERIP;
|
||||||
|
Loading…
Reference in New Issue
Block a user