[Quest API] Add Last Login and First Login Flags to EVENT_CONNECT (#4866)

* [Quest API] Add Last Login and First Login Flags to EVENT_CONNECT

* Push

* Update base_character_data_repository.h

* Update version field

---------

Co-authored-by: Chris Miles <akkadius1@gmail.com>
This commit is contained in:
Alex King
2025-05-14 21:46:25 -04:00
committed by GitHub
parent c36c336bc7
commit 3b617a6652
12 changed files with 166 additions and 78 deletions
+2 -1
View File
@@ -2082,7 +2082,8 @@ private:
uint16 trader_id;
uint16 customer_id;
uint32 account_creation;
uint8 firstlogon;
bool first_login;
bool ingame;
uint32 mercid; // current merc
uint8 mercSlot; // selected merc slot
time_t m_trader_transaction_date;
+47 -23
View File
@@ -807,29 +807,42 @@ void Client::CompleteConnect()
m_last_position_before_bulk_update = GetPosition();
/* This sub event is for if a player logs in for the first time since entering world. */
if (firstlogon == 1) {
TraderRepository::DeleteWhere(database, fmt::format("`char_id` = '{}'", CharacterID()));
BuyerRepository::DeleteBuyer(database, CharacterID());
LogTradingDetail(
"Removed trader abd buyer entries for Character ID {} on first logon to ensure table consistency.",
if (ingame) {
auto e = CharacterDataRepository::FindOne(
database,
CharacterID()
);
bool is_first_login = e.first_login == 0;
RecordPlayerEventLog(PlayerEvent::WENT_ONLINE, PlayerEvent::EmptyEvent{});
if (parse->PlayerHasQuestSub(EVENT_CONNECT)) {
parse->EventPlayer(EVENT_CONNECT, this, "", 0);
const std::string& export_string = fmt::format(
"{} {} {}",
e.last_login,
time(nullptr) - e.last_login,
is_first_login ? 1 : 0
);
parse->EventPlayer(EVENT_CONNECT, this, export_string, 0);
}
/**
* Update last login since this doesn't get updated until a late save later so we can update online status
*/
database.QueryDatabase(
StringFormat(
"UPDATE `character_data` SET `last_login` = UNIX_TIMESTAMP() WHERE id = %u",
if (is_first_login) {
e.first_login = time(nullptr);
TraderRepository::DeleteWhere(database, fmt::format("`char_id` = '{}'", CharacterID()));
BuyerRepository::DeleteBuyer(database, CharacterID());
LogTradingDetail(
"Removed trader abd buyer entries for Character ID {} on first logon to ensure table consistency.",
CharacterID()
)
);
);
}
e.last_login = time(nullptr);
const int updated = CharacterDataRepository::UpdateOne(database, e);
if (!updated) {
LogError("Failed to update login time for character_id [{}]", CharacterID());
}
if (IsPetNameChangeAllowed() && !RuleB(Pets, AlwaysAllowPetRename)) {
InvokeChangePetName(false);
@@ -878,7 +891,7 @@ void Client::CompleteConnect()
entity_list.SendFindableNPCList(this);
if (IsInAGuild()) {
if (firstlogon == 1) {
if (ingame) {
guild_mgr.UpdateDbMemberOnline(CharacterID(), true);
SendGuildMembersList();
}
@@ -1314,14 +1327,14 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
/* Load Character Data */
query = fmt::format(
"SELECT `lfp`, `lfg`, `xtargets`, `firstlogon`, `guild_id`, `rank`, `exp_enabled`, `tribute_enable`, `extra_haste`, `illusion_block` FROM `character_data` LEFT JOIN `guild_members` ON `id` = `char_id` WHERE `id` = {}",
"SELECT `lfp`, `lfg`, `xtargets`, `first_login`, `guild_id`, `rank`, `exp_enabled`, `tribute_enable`, `extra_haste`, `illusion_block`, `ingame` FROM `character_data` LEFT JOIN `guild_members` ON `id` = `char_id` WHERE `id` = {}",
cid
);
auto results = database.QueryDatabase(query);
for (auto row : results) {
if (row[4] && Strings::ToInt(row[4]) > 0) {
guild_id = Strings::ToInt(row[4]);
guildrank = row[5] ? Strings::ToInt(row[5]) : GUILD_RANK_NONE;
guild_id = Strings::ToInt(row[4]);
guildrank = row[5] ? Strings::ToInt(row[5]) : GUILD_RANK_NONE;
guild_tribute_opt_in = row[7] ? Strings::ToBool(row[7]) : 0;
}
@@ -1329,10 +1342,21 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
SetExtraHaste(Strings::ToInt(row[8]), false);
SetIllusionBlock(Strings::ToBool(row[9]));
if (LFP) { LFP = Strings::ToInt(row[0]); }
if (LFG) { LFG = Strings::ToInt(row[1]); }
if (row[3])
firstlogon = Strings::ToInt(row[3]);
if (LFP) {
LFP = Strings::ToInt(row[0]);
}
if (LFG) {
LFG = Strings::ToInt(row[1]);
}
if (row[3]) {
first_login = Strings::ToUnsignedInt(row[3]);
}
if (row[10]) {
ingame = Strings::ToBool(row[10]);
}
}
if (RuleB(Character, SharedBankPlat))
@@ -1721,7 +1745,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
// Taunt persists when zoning on newer clients, overwrite default.
if (m_ClientVersionBit & EQ::versions::maskUFAndLater) {
if (!firstlogon) {
if (!ingame) {
pet->SetTaunting(m_petinfo.taunting);
}
}
+1 -1
View File
@@ -727,7 +727,7 @@ void Client::OnDisconnect(bool hard_disconnect) {
o->trade->Reset();
}
database.SetFirstLogon(CharacterID(), 0); //We change firstlogon status regardless of if a player logs out to zone or not, because we only want to trigger it on their first login from world.
database.SetIngame(CharacterID(), 0); //We change ingame status regardless of if a player logs out to zone or not, because we only want to trigger it on their first login from world.
/* Remove from all proximities */
ClearAllProximities();
+8
View File
@@ -2543,6 +2543,14 @@ void PerlembParser::ExportEventVariables(
break;
}
case EVENT_CONNECT: {
Seperator sep(data);
ExportVar(package_name.c_str(), "last_login", sep.arg[0]);
ExportVar(package_name.c_str(), "seconds_since_last_login", sep.arg[1]);
ExportVar(package_name.c_str(), "is_first_login", sep.arg[2]);
break;
}
default: {
break;
}
+1
View File
@@ -353,6 +353,7 @@ LuaParser::LuaParser() {
PlayerArgumentDispatch[EVENT_AA_LOSS] = handle_player_aa_loss;
PlayerArgumentDispatch[EVENT_SPELL_BLOCKED] = handle_player_spell_blocked;
PlayerArgumentDispatch[EVENT_READ_ITEM] = handle_player_read_item;
PlayerArgumentDispatch[EVENT_CONNECT] = handle_player_connect;
ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click;
ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click;
+20
View File
@@ -1809,6 +1809,26 @@ void handle_player_read_item(
}
}
void handle_player_connect(
QuestInterface *parse,
lua_State* L,
Client* client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
)
{
Seperator sep(data.c_str());
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[0]));
lua_setfield(L, -2, "last_login");
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[1]));
lua_setfield(L, -2, "seconds_since_last_login");
lua_pushboolean(L, Strings::ToBool(sep.arg[2]));
lua_setfield(L, -2, "is_first_login");
}
// Item
void handle_item_click(
QuestInterface *parse,
+9
View File
@@ -865,6 +865,15 @@ void handle_player_read_item(
std::vector<std::any> *extra_pointers
);
void handle_player_connect(
QuestInterface *parse,
lua_State* L,
Client* client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
// Item
void handle_item_click(
QuestInterface *parse,