diff --git a/src/Database.cpp b/src/Database.cpp index 79f5d61..038f947 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -214,6 +214,7 @@ void Database::createTables() { "WarpLocationFlag" INTEGER DEFAULT 0 NOT NULL, "SkywayLocationFlag" BLOB NOT NULL, "CurrentMissionID" INTEGER DEFAULT 0 NOT NULL, + "FirstUseFlag" BLOB NOT NULL, PRIMARY KEY("PlayerID" AUTOINCREMENT), FOREIGN KEY("AccountID") REFERENCES "Accounts"("AccountID") ON DELETE CASCADE, UNIQUE ("AccountID", "Slot"), @@ -512,8 +513,8 @@ int Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID) const char* sql = R"( INSERT INTO "Players" ("AccountID", "Slot", "Firstname", "LastName", "XCoordinates" , "YCoordinates", "ZCoordinates", "Angle", - "HP", "NameCheck", "Quests", "SkywayLocationFlag") - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + "HP", "NameCheck", "Quests", "SkywayLocationFlag", "FirstUseFlag") + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); )"; sqlite3_stmt* stmt; std::string firstName = U16toU8(save->szFirstName); @@ -535,9 +536,10 @@ int Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID) sqlite3_bind_int(stmt, 10, nameCheck); // blobs - unsigned char blobBuffer[128] = { 0 }; - sqlite3_bind_blob(stmt, 11, blobBuffer, 128, 0); - sqlite3_bind_blob(stmt, 12, blobBuffer, 16, 0); + unsigned char blobBuffer[sizeof(Player::aQuestFlag)] = { 0 }; + sqlite3_bind_blob(stmt, 11, blobBuffer, sizeof(Player::aQuestFlag), 0); + sqlite3_bind_blob(stmt, 12, blobBuffer, sizeof(Player::aSkywayLocationFlag), 0); + sqlite3_bind_blob(stmt, 13, blobBuffer, sizeof(Player::iFirstUseFlag), 0); int rc = sqlite3_step(stmt); sqlite3_finalize(stmt); @@ -932,7 +934,7 @@ void Database::getPlayer(Player* plr, int id) { p.XCoordinates, p.YCoordinates, p.ZCoordinates, p.NameCheck, p.Angle, p.HP, acc.AccountLevel, p.FusionMatter, p.Taros, p.Quests, p.BatteryW, p.BatteryN, p.Mentor, p.WarpLocationFlag, - p.SkywayLocationFlag, p.CurrentMissionID, + p.SkywayLocationFlag, p.CurrentMissionID, p.FirstUseFlag, a.Body, a.EyeColor, a.FaceStyle, a.Gender, a.HairColor, a.HairStyle, a.Height, a.SkinColor FROM "Players" as p INNER JOIN "Appearances" as a ON p.PlayerID = a.PlayerID @@ -992,14 +994,16 @@ void Database::getPlayer(Player* plr, int id) { plr->CurrentMissionID = sqlite3_column_int(stmt, 26); - plr->PCStyle.iBody = sqlite3_column_int(stmt, 27); - plr->PCStyle.iEyeColor = sqlite3_column_int(stmt, 28); - plr->PCStyle.iFaceStyle = sqlite3_column_int(stmt, 29); - plr->PCStyle.iGender = sqlite3_column_int(stmt, 30); - plr->PCStyle.iHairColor = sqlite3_column_int(stmt, 31); - plr->PCStyle.iHairStyle = sqlite3_column_int(stmt, 32); - plr->PCStyle.iHeight = sqlite3_column_int(stmt, 33); - plr->PCStyle.iSkinColor = sqlite3_column_int(stmt, 34); + memcpy(plr->iFirstUseFlag, sqlite3_column_blob(stmt, 27), sizeof(plr->iFirstUseFlag)); + + plr->PCStyle.iBody = sqlite3_column_int(stmt, 28); + plr->PCStyle.iEyeColor = sqlite3_column_int(stmt, 29); + plr->PCStyle.iFaceStyle = sqlite3_column_int(stmt, 30); + plr->PCStyle.iGender = sqlite3_column_int(stmt, 31); + plr->PCStyle.iHairColor = sqlite3_column_int(stmt, 32); + plr->PCStyle.iHairStyle = sqlite3_column_int(stmt, 33); + plr->PCStyle.iHeight = sqlite3_column_int(stmt, 34); + plr->PCStyle.iSkinColor = sqlite3_column_int(stmt, 35); // get inventory @@ -1148,7 +1152,7 @@ void Database::updatePlayer(Player *player) { "Angle" = ?, "HP" = ?, "FusionMatter" = ?, "Taros" = ?, "Quests" = ?, "BatteryW" = ?, "BatteryN" = ?, "WarplocationFlag" = ?, "SkywayLocationFlag" = ?, "CurrentMissionID" = ?, - "PayZoneFlag" = ? + "PayZoneFlag" = ?, "FirstUseFlag" = ? WHERE "PlayerID" = ? )"; sqlite3_stmt* stmt; @@ -1181,7 +1185,8 @@ void Database::updatePlayer(Player *player) { sqlite3_bind_blob(stmt, 16, player->aSkywayLocationFlag, sizeof(player->aSkywayLocationFlag), 0); sqlite3_bind_int(stmt, 17, player->CurrentMissionID); sqlite3_bind_int(stmt, 18, player->PCStyle2.iPayzoneFlag); - sqlite3_bind_int(stmt, 19, player->iID); + sqlite3_bind_blob(stmt, 19, player->iFirstUseFlag, sizeof(player->iFirstUseFlag), 0); + sqlite3_bind_int(stmt, 20, player->iID); int rc = sqlite3_step(stmt); if (rc != SQLITE_DONE) { diff --git a/src/Player.hpp b/src/Player.hpp index 6b374d9..5089277 100644 --- a/src/Player.hpp +++ b/src/Player.hpp @@ -79,6 +79,8 @@ struct Player { int64_t buddyIDs[50]; bool isBuddyBlocked[50]; + uint64_t iFirstUseFlag[2]; + ChunkPos chunkPos; std::set* viewableChunks; time_t lastHeartbeat; diff --git a/src/PlayerManager.cpp b/src/PlayerManager.cpp index 081d887..136fb39 100644 --- a/src/PlayerManager.cpp +++ b/src/PlayerManager.cpp @@ -45,6 +45,7 @@ void PlayerManager::init() { REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VEHICLE_ON, enterPlayerVehicle); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VEHICLE_OFF, exitPlayerVehicle); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_CHANGE_MENTOR, changePlayerGuide); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_FIRST_USE_FLAG_SET, setFirstUseFlag); } void PlayerManager::addPlayer(CNSocket* key, Player plr) { @@ -257,9 +258,9 @@ void PlayerManager::enterPlayer(CNSocket* sock, CNPacketData* data) { response.PCLoadData2CL.aQuestFlag[i] = plr.aQuestFlag[i]; } - // shut Computress up - response.PCLoadData2CL.iFirstUseFlag1 = UINT64_MAX; - response.PCLoadData2CL.iFirstUseFlag2 = UINT64_MAX; + // Computress tips + response.PCLoadData2CL.iFirstUseFlag1 = plr.iFirstUseFlag[0]; + response.PCLoadData2CL.iFirstUseFlag2 = plr.iFirstUseFlag[1]; plr.SerialKey = enter->iEnterSerialKey; plr.instanceID = INSTANCE_OVERWORLD; // the player should never be in an instance on enter @@ -858,6 +859,24 @@ void PlayerManager::changePlayerGuide(CNSocket *sock, CNPacketData *data) { plr->mentor = pkt->iMentor; } +void PlayerManager::setFirstUseFlag(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_FIRST_USE_FLAG_SET)) + return; + + sP_CL2FE_REQ_PC_FIRST_USE_FLAG_SET* flag = (sP_CL2FE_REQ_PC_FIRST_USE_FLAG_SET*)data->buf; + Player* plr = getPlayer(sock); + + if (flag->iFlagCode < 1 || flag->iFlagCode > 128) { + std::cout << "[WARN] Client submitted invalid first use flag number?!" << std::endl; + return; + } + + if (flag->iFlagCode <= 64) + plr->iFirstUseFlag[0] |= (1ULL << (flag->iFlagCode - 1)); + else + plr->iFirstUseFlag[1] |= (1ULL << (flag->iFlagCode - 65)); +} + #pragma region Helper methods Player *PlayerManager::getPlayer(CNSocket* key) { if (players.find(key) != players.end()) diff --git a/src/PlayerManager.hpp b/src/PlayerManager.hpp index 25b0aad..41e41ba 100644 --- a/src/PlayerManager.hpp +++ b/src/PlayerManager.hpp @@ -50,6 +50,8 @@ namespace PlayerManager { void enterPlayerVehicle(CNSocket* sock, CNPacketData* data); void exitPlayerVehicle(CNSocket* sock, CNPacketData* data); + void setFirstUseFlag(CNSocket* sock, CNPacketData* data); + Player *getPlayer(CNSocket* key); std::string getPlayerName(Player *plr, bool id=true); WarpLocation* getRespawnPoint(Player *plr);