From 6a31af81ee36eed5e1c4196f032936dabd2fe0a6 Mon Sep 17 00:00:00 2001 From: Justin Wienckowski Date: Mon, 15 Apr 2019 02:07:29 -0700 Subject: [PATCH 1/7] Add GM:MinStatusToLevelTarget rule, which determines the GM status needed to use the #level command on your target. --- common/ruletypes.h | 1 + zone/command.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 18360280a..f5d21b694 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -205,6 +205,7 @@ RULE_CATEGORY_END() RULE_CATEGORY(GM) RULE_INT(GM, MinStatusToSummonItem, 250) RULE_INT(GM, MinStatusToZoneAnywhere, 250) +RULE_INT(GM, MinStatusToLevelTarget, 100) RULE_CATEGORY_END() RULE_CATEGORY(World) diff --git a/zone/command.cpp b/zone/command.cpp index ad0b1c989..c5181322c 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2784,7 +2784,7 @@ void command_level(Client *c, const Seperator *sep) if ((level <= 0) || ((level > RuleI(Character, MaxLevel)) && (c->Admin() < commandLevelAboveCap))) { c->Message(0, "Error: #Level: Invalid Level"); } - else if (c->Admin() < 100) { + else if (c->Admin() < RuleI(GM, MinStatusToLevelTarget)) { c->SetLevel(level, true); #ifdef BOTS if(RuleB(Bots, BotLevelsWithOwner)) From 5fdf976137d02e40a624919dcba9035459103a76 Mon Sep 17 00:00:00 2001 From: Justin Wienckowski Date: Fri, 21 Jun 2019 20:02:42 -0700 Subject: [PATCH 2/7] Add an optional SQL to add the new rule to the rule_values table. Default value is unchanged. --- utils/sql/git/optional/2019_06_21_new_rule_gm_level_target.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 utils/sql/git/optional/2019_06_21_new_rule_gm_level_target.sql diff --git a/utils/sql/git/optional/2019_06_21_new_rule_gm_level_target.sql b/utils/sql/git/optional/2019_06_21_new_rule_gm_level_target.sql new file mode 100644 index 000000000..c7c236df6 --- /dev/null +++ b/utils/sql/git/optional/2019_06_21_new_rule_gm_level_target.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) +VALUES (1, 'GM:MinStatusToLevelTarget', '100', 'GM status needed to use #level on your target'); From 460468224ed9bd4ffe9912841c60540e12109143 Mon Sep 17 00:00:00 2001 From: regneq Date: Sun, 14 Jul 2019 20:03:30 -0700 Subject: [PATCH 3/7] various changed to chatchannel and using language / language skills derived from takp code. --- common/eq_constants.h | 21 ++++++++++++ common/servertalk.h | 5 ++- world/zoneserver.cpp | 7 ++-- zone/client.cpp | 75 ++++++++++++++++++++++-------------------- zone/client.h | 1 - zone/client_packet.cpp | 7 +++- zone/command.cpp | 2 +- zone/entity.cpp | 30 +++++------------ zone/entity.h | 3 +- zone/groups.cpp | 4 +-- zone/net.cpp | 2 +- zone/raids.cpp | 10 ++++-- zone/raids.h | 4 +-- zone/worldserver.cpp | 31 ++++++++++------- zone/worldserver.h | 2 +- 15 files changed, 116 insertions(+), 88 deletions(-) diff --git a/common/eq_constants.h b/common/eq_constants.h index d20bfdf4a..faa0ab247 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -523,4 +523,25 @@ static const uint8 SkillDamageTypes[EQEmu::skills::HIGHEST_SKILL + 1] = // chang static const uint32 MAX_SPELL_DB_ID_VAL = 65535; +enum ChatChannelNames : uint16 +{ + ChatChannel_Guild = 0, + ChatChannel_Group = 2, + ChatChannel_Shout = 3, + ChatChannel_Auction = 4, + ChatChannel_OOC = 5, + ChatChannel_Broadcast = 6, + ChatChannel_Tell = 7, + ChatChannel_Say = 8, + ChatChannel_Petition = 10, + ChatChannel_GMSAY = 11, + ChatChannel_TellEcho = 14, + ChatChannel_Raid = 15, + + ChatChannel_UNKNOWN_Guild = 17, + ChatChannel_UNKNOWN_GMSAY = 18, + ChatChannel_UCSRelay = 20, + ChatChannel_Emotes = 22 +}; + #endif /*COMMON_EQ_CONSTANTS_H*/ diff --git a/common/servertalk.h b/common/servertalk.h index c5cd149d7..94eece8ca 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -352,7 +352,8 @@ struct ServerChannelMessage_Struct { bool noreply; uint16 chan_num; uint32 guilddbid; - uint16 language; + uint8 language; + uint8 lang_skill; uint8 queued; // 0 = not queued, 1 = queued, 2 = queue full, 3 = offline char message[0]; }; @@ -865,6 +866,8 @@ struct ServerRaidGroupAction_Struct { //add / remove depends on opcode. struct ServerRaidMessage_Struct { uint32 rid; uint32 gid; + uint8 language; + uint8 lang_skill; char from[64]; char message[0]; }; diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index f3c6aebfc..0d0ba2f5d 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -409,12 +409,12 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { if (pack->size < sizeof(ServerChannelMessage_Struct)) break; ServerChannelMessage_Struct* scm = (ServerChannelMessage_Struct*)pack->pBuffer; - if (scm->chan_num == 20) + if (scm->chan_num == ChatChannel_UCSRelay) { UCSLink.SendMessage(scm->from, scm->message); break; } - if (scm->chan_num == 7 || scm->chan_num == 14) { + if (scm->chan_num == ChatChannel_Tell || scm->chan_num == ChatChannel_TellEcho) { if (scm->deliverto[0] == '*') { if (console) { @@ -486,7 +486,8 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { cle->Server()->SendPacket(pack); } else { - if (scm->chan_num == 5 || scm->chan_num == 6 || scm->chan_num == 11) { + if (scm->chan_num == ChatChannel_OOC || scm->chan_num == ChatChannel_Broadcast + || scm->chan_num == ChatChannel_GMSAY) { if (console) { console->SendChannelMessage(scm, [&scm]() { auto pack = new ServerPacket(ServerOP_ChannelMessage, diff --git a/zone/client.cpp b/zone/client.cpp index 2d126598f..fd2df48bc 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -823,7 +823,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s { if(strcmp(targetname, "discard") != 0) { - if(chan_num == 3 || chan_num == 4 || chan_num == 5 || chan_num == 7) + if(chan_num == ChatChannel_Shout || chan_num == ChatChannel_Auction || chan_num == ChatChannel_OOC || chan_num == ChatChannel_Tell) { if(GlobalChatLimiterTimer) { @@ -869,7 +869,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s auto pack = new ServerPacket(ServerOP_Speech, sizeof(Server_Speech_Struct) + strlen(message) + 1); Server_Speech_Struct* sem = (Server_Speech_Struct*) pack->pBuffer; - if(chan_num == 0) + if(chan_num == ChatChannel_Guild) sem->guilddbid = GuildID(); else sem->guilddbid = 0; @@ -892,30 +892,38 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s if(!mod_client_message(message, chan_num)) { return; } // Garble the message based on drunkness - if (m_pp.intoxication > 0) { + if (m_pp.intoxication > 0 && !(RuleB(Chat, ServerWideOOC) && chan_num == ChatChannel_OOC) && !GetGM()) { GarbleMessage(message, (int)(m_pp.intoxication / 3)); language = 0; // No need for language when drunk + lang_skill = 100; + } + + // some channels don't use languages + if (chan_num == ChatChannel_OOC || chan_num == ChatChannel_GMSAY || chan_num == ChatChannel_Broadcast || chan_num == ChatChannel_Petition) + { + language = 0; + lang_skill = 100; } // Censor the message - if (EQEmu::ProfanityManager::IsCensorshipActive() && (chan_num != 8)) + if (EQEmu::ProfanityManager::IsCensorshipActive() && (chan_num != ChatChannel_Say)) EQEmu::ProfanityManager::RedactMessage(message); switch(chan_num) { - case 0: { /* Guild Chat */ + case ChatChannel_Guild: { /* Guild Chat */ if (!IsInAGuild()) Message_StringID(MT_DefaultText, GUILD_NOT_MEMBER2); //You are not a member of any guild. else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_SPEAK)) Message(0, "Error: You dont have permission to speak to the guild."); - else if (!worldserver.SendChannelMessage(this, targetname, chan_num, GuildID(), language, message)) + else if (!worldserver.SendChannelMessage(this, targetname, chan_num, GuildID(), language, lang_skill, message)) Message(0, "Error: World server disconnected"); break; } - case 2: { /* Group Chat */ + case ChatChannel_Group: { /* Group Chat */ Raid* raid = entity_list.GetRaidByClient(this); if(raid) { - raid->RaidGroupSay((const char*) message, this); + raid->RaidGroupSay((const char*) message, this, language, lang_skill); break; } @@ -925,14 +933,14 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } break; } - case 15: { /* Raid Say */ + case ChatChannel_Raid: { /* Raid Say */ Raid* raid = entity_list.GetRaidByClient(this); if(raid){ - raid->RaidSay((const char*) message, this); + raid->RaidSay((const char*) message, this, language, lang_skill); } break; } - case 3: { /* Shout */ + case ChatChannel_Shout: { /* Shout */ Mob *sender = this; if (GetPet() && GetTarget() == GetPet() && GetPet()->FindType(SE_VoiceGraft)) sender = GetPet(); @@ -940,13 +948,13 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s entity_list.ChannelMessage(sender, chan_num, language, lang_skill, message); break; } - case 4: { /* Auction */ + case ChatChannel_Auction: { /* Auction */ if(RuleB(Chat, ServerWideAuction)) { if(!global_channel_timer.Check()) { if(strlen(targetname) == 0) - ChannelMessageReceived(5, language, lang_skill, message, "discard"); //Fast typer or spammer?? + ChannelMessageReceived(chan_num, language, lang_skill, message, "discard"); //Fast typer or spammer?? else return; } @@ -966,7 +974,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } } - if (!worldserver.SendChannelMessage(this, 0, 4, 0, language, message)) + if (!worldserver.SendChannelMessage(this, 0, chan_num, 0, language, lang_skill, message)) Message(0, "Error: World server disconnected"); } else if(!RuleB(Chat, ServerWideAuction)) { @@ -975,17 +983,17 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s if (GetPet() && GetTarget() == GetPet() && GetPet()->FindType(SE_VoiceGraft)) sender = GetPet(); - entity_list.ChannelMessage(sender, chan_num, language, message); + entity_list.ChannelMessage(sender, chan_num, language, lang_skill, message); } break; } - case 5: { /* OOC */ + case ChatChannel_OOC: { /* OOC */ if(RuleB(Chat, ServerWideOOC)) { if(!global_channel_timer.Check()) { if(strlen(targetname) == 0) - ChannelMessageReceived(5, language, lang_skill, message, "discard"); //Fast typer or spammer?? + ChannelMessageReceived(chan_num, language, lang_skill, message, "discard"); //Fast typer or spammer?? else return; } @@ -1010,7 +1018,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } } - if (!worldserver.SendChannelMessage(this, 0, 5, 0, language, message)) + if (!worldserver.SendChannelMessage(this, 0, chan_num, 0, language, lang_skill, message)) { Message(0, "Error: World server disconnected"); } @@ -1022,19 +1030,19 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s if (GetPet() && GetTarget() == GetPet() && GetPet()->FindType(SE_VoiceGraft)) sender = GetPet(); - entity_list.ChannelMessage(sender, chan_num, language, message); + entity_list.ChannelMessage(sender, chan_num, language, lang_skill, message); } break; } - case 6: /* Broadcast */ - case 11: { /* GM Say */ + case ChatChannel_Broadcast: /* Broadcast */ + case ChatChannel_GMSAY: { /* GM Say */ if (!(admin >= 80)) Message(0, "Error: Only GMs can use this channel"); - else if (!worldserver.SendChannelMessage(this, targetname, chan_num, 0, language, message)) + else if (!worldserver.SendChannelMessage(this, targetname, chan_num, 0, language, lang_skill, message)) Message(0, "Error: World server disconnected"); break; } - case 7: { /* Tell */ + case ChatChannel_Tell: { /* Tell */ if(!global_channel_timer.Check()) { if(strlen(targetname) == 0) @@ -1078,11 +1086,11 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s target_name[x] = '\0'; } - if(!worldserver.SendChannelMessage(this, target_name, chan_num, 0, language, message)) + if(!worldserver.SendChannelMessage(this, target_name, chan_num, 0, language, lang_skill, message)) Message(0, "Error: World server disconnected"); break; } - case 8: { /* Say */ + case ChatChannel_Say: { /* Say */ if(message[0] == COMMAND_CHAR) { if(command_dispatch(this, message) == -2) { if(parse->PlayerHasQuestSub(EVENT_COMMAND)) { @@ -1158,14 +1166,14 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } break; } - case 20: + case ChatChannel_UCSRelay: { // UCS Relay for Underfoot and later. - if(!worldserver.SendChannelMessage(this, 0, chan_num, 0, language, message)) + if(!worldserver.SendChannelMessage(this, 0, chan_num, 0, language, lang_skill, message)) Message(0, "Error: World server disconnected"); break; } - case 22: + case ChatChannel_Emotes: { // Emotes for Underfoot and later. // crash protection -- cheater @@ -1189,11 +1197,6 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } } -// if no language skill is specified, call the function with a skill of 100. -void Client::ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, const char* message, ...) { - ChannelMessageSend(from, to, chan_num, language, 100, message); -} - void Client::ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...) { if ((chan_num==11 && !(this->GetGM())) || (chan_num==10 && this->Admin()<80)) // dont need to send /pr & /petition to everybody return; @@ -1218,7 +1221,7 @@ void Client::ChannelMessageSend(const char* from, const char* to, uint8 chan_num } if (to != 0) strcpy((char *) cm->targetname, to); - else if (chan_num == 7) + else if (chan_num == ChatChannel_Tell) strcpy(cm->targetname, m_pp.name); else cm->targetname[0] = 0; @@ -1227,7 +1230,7 @@ void Client::ChannelMessageSend(const char* from, const char* to, uint8 chan_num if (language < MAX_PP_LANGUAGE) { ListenerSkill = m_pp.languages[language]; - if (ListenerSkill == 0) { + if (ListenerSkill < 24) { cm->language = (MAX_PP_LANGUAGE - 1); // in an unknown tongue } else { @@ -1253,7 +1256,7 @@ void Client::ChannelMessageSend(const char* from, const char* to, uint8 chan_num bool weAreNotSender = strcmp(this->GetCleanName(), cm->sender); if (senderCanTrainSelf || weAreNotSender) { - if ((chan_num == 2) && (ListenerSkill < 100)) { // group message in unmastered language, check for skill up + if ((chan_num == ChatChannel_Group) && (ListenerSkill < 100)) { // group message in unmastered language, check for skill up if (m_pp.languages[language] <= lang_skill) CheckLanguageSkillIncrease(language, lang_skill); } diff --git a/zone/client.h b/zone/client.h index 2adc2ea21..a44e83087 100644 --- a/zone/client.h +++ b/zone/client.h @@ -338,7 +338,6 @@ public: void QueuePacket(const EQApplicationPacket* app, bool ack_req = true, CLIENT_CONN_STATUS = CLIENT_CONNECTINGALL, eqFilterType filter=FilterNone); void FastQueuePacket(EQApplicationPacket** app, bool ack_req = true, CLIENT_CONN_STATUS = CLIENT_CONNECTINGALL); void ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_skill, const char* orig_message, const char* targetname=nullptr); - void ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, const char* message, ...); void ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...); void Message(uint32 type, const char* message, ...); void FilteredMessage(Mob *sender, uint32 type, eqFilterType filter, const char* message, ...); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 5cf36c946..6cc05a65f 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4181,7 +4181,12 @@ void Client::Handle_OP_ChannelMessage(const EQApplicationPacket *app) return; } - ChannelMessageReceived(cm->chan_num, cm->language, cm->skill_in_language, cm->message, cm->targetname); + uint8 skill_in_language = 100; + if (cm->language < MAX_PP_LANGUAGE) + { + skill_in_language = m_pp.languages[cm->language]; + } + ChannelMessageReceived(cm->chan_num, cm->language, skill_in_language, cm->message, cm->targetname); return; } diff --git a/zone/command.cpp b/zone/command.cpp index 1675be8b8..debf0d90f 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -887,7 +887,7 @@ void command_chat(Client *c, const Seperator *sep) if (sep->arg[2][0] == 0) c->Message(0, "Usage: #chat [channum] [message]"); else - if (!worldserver.SendChannelMessage(0, 0, (uint8) atoi(sep->arg[1]), 0, 0, sep->argplus[2])) + if (!worldserver.SendChannelMessage(0, 0, (uint8) atoi(sep->arg[1]), 0, 0, 100, sep->argplus[2])) c->Message(0, "Error: World server disconnected"); } diff --git a/zone/entity.cpp b/zone/entity.cpp index d1ecfc990..0ed696327 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1235,33 +1235,19 @@ void EntityList::ChannelMessage(Mob *from, uint8 chan_num, uint8 language, while(it != client_list.end()) { Client *client = it->second; eqFilterType filter = FilterNone; - if (chan_num == 3) //shout + if (chan_num == ChatChannel_Shout) //shout filter = FilterShouts; - else if (chan_num == 4) //auction + else if (chan_num == ChatChannel_Auction) //auction filter = FilterAuctions; // // Only say is limited in range - if (chan_num != 8 || Distance(client->GetPosition(), from->GetPosition()) < 200) + if (chan_num != ChatChannel_Say || Distance(client->GetPosition(), from->GetPosition()) < 200) if (filter == FilterNone || client->GetFilter(filter) != FilterHide) client->ChannelMessageSend(from->GetName(), 0, chan_num, language, lang_skill, buffer); ++it; } } -void EntityList::ChannelMessageSend(Mob *to, uint8 chan_num, uint8 language, const char *message, ...) -{ - if (!to->IsClient()) - return; - va_list argptr; - char buffer[4096]; - - va_start(argptr, message); - vsnprintf(buffer, 4096, message, argptr); - va_end(argptr); - - to->CastToClient()->ChannelMessageSend(0, 0, chan_num, language, buffer); -} - void EntityList::SendZoneSpawns(Client *client) { EQApplicationPacket *app; @@ -1995,22 +1981,22 @@ Client *EntityList::GetClientByAccID(uint32 accid) } void EntityList::ChannelMessageFromWorld(const char *from, const char *to, - uint8 chan_num, uint32 guild_id, uint8 language, const char *message) + uint8 chan_num, uint32 guild_id, uint8 language, uint8 lang_skill, const char *message) { for (auto it = client_list.begin(); it != client_list.end(); ++it) { Client *client = it->second; - if (chan_num == 0) { + if (chan_num == ChatChannel_Guild) { if (!client->IsInGuild(guild_id)) continue; if (!guild_mgr.CheckPermission(guild_id, client->GuildRank(), GUILD_HEAR)) continue; if (client->GetFilter(FilterGuildChat) == FilterHide) continue; - } else if (chan_num == 5) { + } else if (chan_num == ChatChannel_OOC) { if (client->GetFilter(FilterOOC) == FilterHide) continue; } - client->ChannelMessageSend(from, to, chan_num, language, message); + client->ChannelMessageSend(from, to, chan_num, language, lang_skill, message); } } @@ -3880,7 +3866,7 @@ void EntityList::GroupMessage(uint32 gid, const char *from, const char *message) g = it->second->GetGroup(); if (g) { if (g->GetID() == gid) - it->second->ChannelMessageSend(from, it->second->GetName(), 2, 0, message); + it->second->ChannelMessageSend(from, it->second->GetName(), ChatChannel_Group, 0, 100, message); } } ++it; diff --git a/zone/entity.h b/zone/entity.h index fb9b1f09b..084cf6f01 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -323,10 +323,9 @@ public: void FilteredMessage_StringID(Mob *sender, bool skipsender, uint32 type, eqFilterType filter, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0); void MessageClose_StringID(Mob *sender, bool skipsender, float dist, uint32 type, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0); void FilteredMessageClose_StringID(Mob *sender, bool skipsender, float dist, uint32 type, eqFilterType filter, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0); - void ChannelMessageFromWorld(const char* from, const char* to, uint8 chan_num, uint32 guilddbid, uint8 language, const char* message); + void ChannelMessageFromWorld(const char* from, const char* to, uint8 chan_num, uint32 guilddbid, uint8 language, uint8 lang_skill, const char* message); void ChannelMessage(Mob* from, uint8 chan_num, uint8 language, const char* message, ...); void ChannelMessage(Mob* from, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...); - void ChannelMessageSend(Mob* to, uint8 chan_num, uint8 language, const char* message, ...); void SendZoneSpawns(Client*); void SendZonePVPUpdates(Client *); void SendZoneSpawnsBulk(Client* client); diff --git a/zone/groups.cpp b/zone/groups.cpp index a7e05bf49..2f0c5e854 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -910,7 +910,7 @@ void Group::GroupMessage(Mob* sender, uint8 language, uint8 lang_skill, const ch continue; if (members[i]->IsClient() && members[i]->CastToClient()->GetFilter(FilterGroupChat)!=0) - members[i]->CastToClient()->ChannelMessageSend(sender->GetName(),members[i]->GetName(),2,language,lang_skill,message); + members[i]->CastToClient()->ChannelMessageSend(sender->GetName(),members[i]->GetName(),ChatChannel_Group,language,lang_skill,message); } auto pack = @@ -2493,4 +2493,4 @@ void Group::QueueClients(Mob *sender, const EQApplicationPacket *app, bool ack_r } } } -} \ No newline at end of file +} diff --git a/zone/net.cpp b/zone/net.cpp index 9fe8a7d50..4b4859e5e 100644 --- a/zone/net.cpp +++ b/zone/net.cpp @@ -514,7 +514,7 @@ int main(int argc, char** argv) { } else { if (worldwasconnected && is_zone_loaded) { - entity_list.ChannelMessageFromWorld(0, 0, 6, 0, 0, "WARNING: World server connection lost"); + entity_list.ChannelMessageFromWorld(0, 0, ChatChannel_Broadcast, 0, 0, 100, "WARNING: World server connection lost"); worldwasconnected = false; } } diff --git a/zone/raids.cpp b/zone/raids.cpp index 2bbd83064..e4c5b3552 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -446,7 +446,7 @@ uint32 Raid::GetGroup(Client *c) return 0xFFFFFFFF; } -void Raid::RaidSay(const char *msg, Client *c) +void Raid::RaidSay(const char *msg, Client *c, uint8 language, uint8 lang_skill) { if(!c) return; @@ -455,6 +455,8 @@ void Raid::RaidSay(const char *msg, Client *c) ServerRaidMessage_Struct *rga = (ServerRaidMessage_Struct*)pack->pBuffer; rga->rid = GetID(); rga->gid = 0xFFFFFFFF; + rga->language = language; + rga->lang_skill = lang_skill; strn0cpy(rga->from, c->GetName(), 64); strcpy(rga->message, msg); // this is safe because we are allocating enough space for the entire msg above @@ -463,7 +465,7 @@ void Raid::RaidSay(const char *msg, Client *c) safe_delete(pack); } -void Raid::RaidGroupSay(const char *msg, Client *c) +void Raid::RaidGroupSay(const char *msg, Client *c, uint8 language, uint8 lang_skill) { if(!c) return; @@ -477,6 +479,8 @@ void Raid::RaidGroupSay(const char *msg, Client *c) ServerRaidMessage_Struct *rga = (ServerRaidMessage_Struct*)pack->pBuffer; rga->rid = GetID(); rga->gid = groupToUse; + rga->language = language; + rga->lang_skill = lang_skill; strn0cpy(rga->from, c->GetName(), 64); strcpy(rga->message, msg); // this is safe because we are allocating enough space for the entire msg above @@ -1821,4 +1825,4 @@ void Raid::QueueClients(Mob *sender, const EQApplicationPacket *app, bool ack_re } } } -} \ No newline at end of file +} diff --git a/zone/raids.h b/zone/raids.h index 9ed9bc11c..26f5a0880 100644 --- a/zone/raids.h +++ b/zone/raids.h @@ -177,8 +177,8 @@ public: void SendHPManaEndPacketsFrom(Mob *mob); void SendManaPacketFrom(Mob *mob); void SendEndurancePacketFrom(Mob *mob); - void RaidSay(const char *msg, Client *c); - void RaidGroupSay(const char *msg, Client *c); + void RaidSay(const char *msg, Client *c, uint8 language, uint8 lang_skill); + void RaidGroupSay(const char *msg, Client *c, uint8 language, uint8 lang_skill); //Packet Functions void SendRaidCreate(Client *to); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 497d6711a..94be0eb14 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -200,29 +200,35 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; ServerChannelMessage_Struct* scm = (ServerChannelMessage_Struct*)pack->pBuffer; if (scm->deliverto[0] == 0) { - entity_list.ChannelMessageFromWorld(scm->from, scm->to, scm->chan_num, scm->guilddbid, scm->language, scm->message); + entity_list.ChannelMessageFromWorld(scm->from, scm->to, scm->chan_num, scm->guilddbid, scm->language, scm->lang_skill, scm->message); } else { Client* client = entity_list.GetClientByName(scm->deliverto); - if (client) { - if (client->Connected()) { + if (client && client->Connected()) { + if (scm->chan_num == ChatChannel_TellEcho) { if (scm->queued == 1) // tell was queued client->Tell_StringID(QUEUED_TELL, scm->to, scm->message); else if (scm->queued == 2) // tell queue was full client->Tell_StringID(QUEUE_TELL_FULL, scm->to, scm->message); else if (scm->queued == 3) // person was offline client->Message_StringID(MT_TellEcho, TOLD_NOT_ONLINE, scm->to); - else // normal stuff - client->ChannelMessageSend(scm->from, scm->to, scm->chan_num, scm->language, scm->message); - if (!scm->noreply && scm->chan_num != 2) { //dont echo on group chat - // if it's a tell, echo back so it shows up - scm->noreply = true; - scm->chan_num = 14; + else // normal tell echo "You told Soanso, 'something'" + // tell echo doesn't use language, so it looks normal to you even if nobody can understand your tells + client->ChannelMessageSend(scm->from, scm->to, scm->chan_num, 0, 100, scm->message); + } + else if (scm->chan_num == ChatChannel_Tell) { + client->ChannelMessageSend(scm->from, scm->to, scm->chan_num, scm->language, scm->lang_skill, scm->message); + if (scm->queued == 0) { // this is not a queued tell + // if it's a tell, echo back to acknowledge it and make it show on the sender's client + scm->chan_num = ChatChannel_TellEcho; memset(scm->deliverto, 0, sizeof(scm->deliverto)); strcpy(scm->deliverto, scm->from); SendPacket(pack); } } + else { + client->ChannelMessageSend(scm->from, scm->to, scm->chan_num, scm->language, scm->lang_skill, scm->message); + } } } break; @@ -1352,7 +1358,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) if (r->members[x].GroupNumber == rmsg->gid) { if (r->members[x].member->GetFilter(FilterGroupChat) != 0) { - r->members[x].member->ChannelMessageSend(rmsg->from, r->members[x].member->GetName(), 2, 0, rmsg->message); + r->members[x].member->ChannelMessageSend(rmsg->from, r->members[x].member->GetName(), ChatChannel_Group, rmsg->language, rmsg->lang_skill, rmsg->message); } } } @@ -1377,7 +1383,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { if (r->members[x].member->GetFilter(FilterGroupChat) != 0) { - r->members[x].member->ChannelMessageSend(rmsg->from, r->members[x].member->GetName(), 15, 0, rmsg->message); + r->members[x].member->ChannelMessageSend(rmsg->from, r->members[x].member->GetName(), ChatChannel_Raid, rmsg->language, rmsg->lang_skill, rmsg->message); } } } @@ -1950,7 +1956,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } -bool WorldServer::SendChannelMessage(Client* from, const char* to, uint8 chan_num, uint32 guilddbid, uint8 language, const char* message, ...) { +bool WorldServer::SendChannelMessage(Client* from, const char* to, uint8 chan_num, uint32 guilddbid, uint8 language, uint8 lang_skill, const char* message, ...) { if (!worldserver.Connected()) return false; va_list argptr; @@ -1984,6 +1990,7 @@ bool WorldServer::SendChannelMessage(Client* from, const char* to, uint8 chan_nu scm->chan_num = chan_num; scm->guilddbid = guilddbid; scm->language = language; + scm->lang_skill = lang_skill; scm->queued = 0; strcpy(scm->message, buffer); diff --git a/zone/worldserver.h b/zone/worldserver.h index 7dc6b02f8..4c1b9ab12 100644 --- a/zone/worldserver.h +++ b/zone/worldserver.h @@ -38,7 +38,7 @@ public: void HandleMessage(uint16 opcode, const EQ::Net::Packet &p); - bool SendChannelMessage(Client* from, const char* to, uint8 chan_num, uint32 guilddbid, uint8 language, const char* message, ...); + bool SendChannelMessage(Client* from, const char* to, uint8 chan_num, uint32 guilddbid, uint8 language, uint8 lang_skill, const char* message, ...); bool SendEmoteMessage(const char* to, uint32 to_guilddbid, uint32 type, const char* message, ...); bool SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message, ...); bool SendVoiceMacro(Client* From, uint32 Type, char* Target, uint32 MacroNumber, uint32 GroupOrRaidID = 0); From 9fe17f4d464d6c2cd49e57e1fc90eb65bbcafd8d Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 18 Jul 2019 00:56:46 -0400 Subject: [PATCH 4/7] Fix issues with OP_SpecialMesg handling This should prevent any optimizations being done on the "1 char string" This also fully documents the packet and expands the uses of quest::say/QuestSay --- common/base_packet.h | 1 + common/eq_constants.h | 1 + common/eq_packet_structs.h | 14 ++++++ common/patches/rof.cpp | 50 +++++++++----------- common/patches/rof2.cpp | 50 +++++++++----------- common/patches/sod.cpp | 50 +++++++++----------- common/patches/sof.cpp | 50 +++++++++----------- common/patches/titanium.cpp | 50 +++++++++----------- common/patches/uf.cpp | 50 +++++++++----------- zone/client.cpp | 92 ++++++++++--------------------------- zone/client.h | 1 - zone/common.h | 25 ++++++++++ zone/embparser_api.cpp | 31 ++++++++++--- zone/entity.cpp | 34 +++++++------- zone/entity.h | 3 +- zone/lua_general.cpp | 26 +++++++++++ zone/lua_general.h | 2 + zone/lua_mob.cpp | 63 ++++++++++++++++++++++++- zone/lua_mob.h | 1 + zone/lua_parser.cpp | 4 +- zone/mob.cpp | 8 +++- zone/mob.h | 2 +- zone/questmgr.cpp | 23 +++------- zone/questmgr.h | 3 +- 24 files changed, 343 insertions(+), 291 deletions(-) diff --git a/common/base_packet.h b/common/base_packet.h index 4f47c919a..3b92e3e90 100644 --- a/common/base_packet.h +++ b/common/base_packet.h @@ -75,6 +75,7 @@ public: uint32 ReadUInt32() { uint32 value = *(uint32 *)(pBuffer + _rpos); _rpos += sizeof(uint32); return value; } uint32 ReadUInt32(uint32 Offset) const { uint32 value = *(uint32 *)(pBuffer + Offset); return value; } void ReadString(char *str) { uint32 len = static_cast(strlen((char *)(pBuffer + _rpos))) + 1; memcpy(str, pBuffer + _rpos, len); _rpos += len; } + void ReadString(std::string &str) { str = reinterpret_cast(pBuffer + _rpos); _rpos += str.length() + 1; } void ReadString(char *str, uint32 Offset, uint32 MaxLength) const; uint32 GetWritePosition() { return _wpos; } diff --git a/common/eq_constants.h b/common/eq_constants.h index faa0ab247..657db450e 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -87,6 +87,7 @@ typedef enum { _eaMaxAppearance } EmuAppearance; +#define MT_NPCQuestSay 10 // msg_type's for custom usercolors #define MT_Say 256 #define MT_Tell 257 diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index c80fdcc02..fb9260857 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -1188,6 +1188,20 @@ struct SpecialMesg_Struct /*24*/ char message[1]; // What is being said? }; +struct SpecialMesgHeader_Struct +{ +/*00*/ char SpeakMode; // 2 shouts, 4 %1 %2, 3 %2, 5 tells group, 0 copy, default says +/*01*/ char JournalMode; // 1 and 2 go to journal +/*02*/ char language; +/*03*/ uint32 msg_type; // Color of text (see MT_*** below) +/*07*/ uint32 target_spawn_id; // Who is it being said to? +/*11*/ // speaker's name +/*xx*/ // unknown, location, client doesn't care +/*xx*/ // unknown +/*xx*/ // unknown +/*xx*/ // message +}; + /* ** When somebody changes what they're wearing or give a pet a weapon (model changes) ** Length: 19 Bytes diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index d7fa21c41..322e187e4 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -3199,43 +3199,35 @@ namespace RoF EQApplicationPacket *in = *p; *p = nullptr; - SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + SerializeBuffer buf(in->size); + buf.WriteInt8(in->ReadUInt8()); // speak mode + buf.WriteInt8(in->ReadUInt8()); // journal mode + buf.WriteInt8(in->ReadUInt8()); // language + buf.WriteInt32(in->ReadUInt32()); // message type + buf.WriteInt32(in->ReadUInt32()); // target spawn id - unsigned char *__emu_buffer = in->pBuffer; - // break strlen optimizations! - char *message = emu->sayer; - auto sayer_length = std::char_traits::length(message); - message += sayer_length + 1 + 12; // skip over sayer name, null term, and 3 floats + std::string name; + in->ReadString(name); // NPC names max out at 63 chars - std::string old_message = message; + buf.WriteString(name); + + buf.WriteInt32(in->ReadUInt32()); // loc + buf.WriteInt32(in->ReadUInt32()); + buf.WriteInt32(in->ReadUInt32()); + + std::string old_message; std::string new_message; + in->ReadString(old_message); + ServerToRoFSayLink(new_message, old_message); - //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; - in->size = sayer_length + new_message.length() + 25; - in->pBuffer = new unsigned char[in->size]; + buf.WriteString(new_message); - char *OutBuffer = (char *)in->pBuffer; + auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); - - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); - - // TODO: figure this shit out - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - - VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); - - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); + dest->FastQueuePacket(&outapp, ack_req); + delete in; } ENCODE(OP_Stun) diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 66e699e2a..a6cf7a134 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -3266,43 +3266,35 @@ namespace RoF2 EQApplicationPacket *in = *p; *p = nullptr; - SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + SerializeBuffer buf(in->size); + buf.WriteInt8(in->ReadUInt8()); // speak mode + buf.WriteInt8(in->ReadUInt8()); // journal mode + buf.WriteInt8(in->ReadUInt8()); // language + buf.WriteInt32(in->ReadUInt32()); // message type + buf.WriteInt32(in->ReadUInt32()); // target spawn id - unsigned char *__emu_buffer = in->pBuffer; - // break strlen optimizations! - char *message = emu->sayer; - auto sayer_length = std::char_traits::length(message); - message += sayer_length + 1 + 12; // skip over sayer name, null term, and 3 floats + std::string name; + in->ReadString(name); // NPC names max out at 63 chars - std::string old_message = message; + buf.WriteString(name); + + buf.WriteInt32(in->ReadUInt32()); // loc + buf.WriteInt32(in->ReadUInt32()); + buf.WriteInt32(in->ReadUInt32()); + + std::string old_message; std::string new_message; + in->ReadString(old_message); + ServerToRoF2SayLink(new_message, old_message); - //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; - in->size = sayer_length + new_message.length() + 25; - in->pBuffer = new unsigned char[in->size]; + buf.WriteString(new_message); - char *OutBuffer = (char *)in->pBuffer; + auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); - - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); - - // TODO: figure this shit out - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - - VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); - - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); + dest->FastQueuePacket(&outapp, ack_req); + delete in; } ENCODE(OP_Stun) diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 2c9d70edf..11aae5cc9 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -2069,43 +2069,35 @@ namespace SoD EQApplicationPacket *in = *p; *p = nullptr; - SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + SerializeBuffer buf(in->size); + buf.WriteInt8(in->ReadUInt8()); // speak mode + buf.WriteInt8(in->ReadUInt8()); // journal mode + buf.WriteInt8(in->ReadUInt8()); // language + buf.WriteInt32(in->ReadUInt32()); // message type + buf.WriteInt32(in->ReadUInt32()); // target spawn id - unsigned char *__emu_buffer = in->pBuffer; - // break strlen optimizations! - char *message = emu->sayer; - auto sayer_length = std::char_traits::length(message); - message += sayer_length + 1 + 12; // skip over sayer name, null term, and 3 floats + std::string name; + in->ReadString(name); // NPC names max out at 63 chars - std::string old_message = message; + buf.WriteString(name); + + buf.WriteInt32(in->ReadUInt32()); // loc + buf.WriteInt32(in->ReadUInt32()); + buf.WriteInt32(in->ReadUInt32()); + + std::string old_message; std::string new_message; + in->ReadString(old_message); + ServerToSoDSayLink(new_message, old_message); - //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; - in->size = sayer_length + new_message.length() + 25; - in->pBuffer = new unsigned char[in->size]; + buf.WriteString(new_message); - char *OutBuffer = (char *)in->pBuffer; + auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); - - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); - - // TODO: figure this shit out - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - - VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); - - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); + dest->FastQueuePacket(&outapp, ack_req); + delete in; } ENCODE(OP_Stun) diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 0b3043c70..7569b4c5d 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -1720,43 +1720,35 @@ namespace SoF EQApplicationPacket *in = *p; *p = nullptr; - SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + SerializeBuffer buf(in->size); + buf.WriteInt8(in->ReadUInt8()); // speak mode + buf.WriteInt8(in->ReadUInt8()); // journal mode + buf.WriteInt8(in->ReadUInt8()); // language + buf.WriteInt32(in->ReadUInt32()); // message type + buf.WriteInt32(in->ReadUInt32()); // target spawn id - unsigned char *__emu_buffer = in->pBuffer; - // break strlen optimizations! - char *message = emu->sayer; - auto sayer_length = std::char_traits::length(message); - message += sayer_length + 1 + 12; // skip over sayer name, null term, and 3 floats + std::string name; + in->ReadString(name); - std::string old_message = message; + buf.WriteString(name); + + buf.WriteInt32(in->ReadUInt32()); // loc + buf.WriteInt32(in->ReadUInt32()); + buf.WriteInt32(in->ReadUInt32()); + + std::string old_message; std::string new_message; + in->ReadString(old_message); + ServerToSoFSayLink(new_message, old_message); - //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; - in->size = sayer_length + new_message.length() + 25; - in->pBuffer = new unsigned char[in->size]; + buf.WriteString(new_message); - char *OutBuffer = (char *)in->pBuffer; + auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); - - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); - - // TODO: figure this shit out - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - - VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); - - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); + dest->FastQueuePacket(&outapp, ack_req); + delete in; } ENCODE(OP_Stun) diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index c4bef5bea..d8a115a98 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -1420,43 +1420,35 @@ namespace Titanium EQApplicationPacket *in = *p; *p = nullptr; - SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + SerializeBuffer buf(in->size); + buf.WriteInt8(in->ReadUInt8()); // speak mode + buf.WriteInt8(in->ReadUInt8()); // journal mode + buf.WriteInt8(in->ReadUInt8()); // language + buf.WriteInt32(in->ReadUInt32()); // message type + buf.WriteInt32(in->ReadUInt32()); // target spawn id - unsigned char *__emu_buffer = in->pBuffer; - // break strlen optimizations! - char *message = emu->sayer; - auto sayer_length = std::char_traits::length(message); - message += sayer_length + 1 + 12; // skip over sayer name, null term, and 3 floats + std::string name; + in->ReadString(name); // NPC names max out at 63 chars - std::string old_message = message; + buf.WriteString(name); + + buf.WriteInt32(in->ReadUInt32()); // loc + buf.WriteInt32(in->ReadUInt32()); + buf.WriteInt32(in->ReadUInt32()); + + std::string old_message; std::string new_message; + in->ReadString(old_message); + ServerToTitaniumSayLink(new_message, old_message); - //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; - in->size = sayer_length + new_message.length() + 25; - in->pBuffer = new unsigned char[in->size]; + buf.WriteString(new_message); - char *OutBuffer = (char *)in->pBuffer; + auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); - - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); - - // TODO: figure this shit out - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - - VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); - - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); + dest->FastQueuePacket(&outapp, ack_req); + delete in; } ENCODE(OP_TaskDescription) diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 7db0cfb92..b644c9554 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -2369,43 +2369,35 @@ namespace UF EQApplicationPacket *in = *p; *p = nullptr; - SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + SerializeBuffer buf(in->size); + buf.WriteInt8(in->ReadUInt8()); // speak mode + buf.WriteInt8(in->ReadUInt8()); // journal mode + buf.WriteInt8(in->ReadUInt8()); // language + buf.WriteInt32(in->ReadUInt32()); // message type + buf.WriteInt32(in->ReadUInt32()); // target spawn id - unsigned char *__emu_buffer = in->pBuffer; - // break strlen optimizations! - char *message = emu->sayer; - auto sayer_length = std::char_traits::length(message); - message += sayer_length + 1 + 12; // skip over sayer name, null term, and 3 floats + std::string name; + in->ReadString(name); // NPC names max out at 63 chars - std::string old_message = message; + buf.WriteString(name); + + buf.WriteInt32(in->ReadUInt32()); // loc + buf.WriteInt32(in->ReadUInt32()); + buf.WriteInt32(in->ReadUInt32()); + + std::string old_message; std::string new_message; + in->ReadString(old_message); + ServerToUFSayLink(new_message, old_message); - //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; - in->size = sayer_length + new_message.length() + 25; - in->pBuffer = new unsigned char[in->size]; + buf.WriteString(new_message); - char *OutBuffer = (char *)in->pBuffer; + auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); - - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); - - // TODO: figure this shit out - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0.0f); - - VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); - - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); + dest->FastQueuePacket(&outapp, ack_req); + delete in; } ENCODE(OP_Stun) diff --git a/zone/client.cpp b/zone/client.cpp index fd2df48bc..e90dafa8a 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1277,21 +1277,19 @@ void Client::Message(uint32 type, const char* message, ...) { vsnprintf(buffer, 4096, message, argptr); va_end(argptr); - size_t len = strlen(buffer); + SerializeBuffer buf(sizeof(SpecialMesgHeader_Struct) + 12 + 64 + 64); + buf.WriteInt8(static_cast(Journal::SpeakMode::Raw)); + buf.WriteInt8(static_cast(Journal::Mode::None)); + buf.WriteInt8(0); // language + buf.WriteUInt32(type); + buf.WriteUInt32(0); // target spawn ID used for journal filtering, ignored here + buf.WriteString(""); // send name, not applicable here + buf.WriteInt32(0); // location, client seems to ignore + buf.WriteInt32(0); + buf.WriteInt32(0); + buf.WriteString(buffer); - //client dosent like our packet all the time unless - //we make it really big, then it seems to not care that - //our header is malformed. - //len = 4096 - sizeof(SpecialMesg_Struct); - - uint32 len_packet = sizeof(SpecialMesg_Struct)+len; - auto app = new EQApplicationPacket(OP_SpecialMesg, len_packet); - SpecialMesg_Struct* sm=(SpecialMesg_Struct*)app->pBuffer; - sm->header[0] = 0x00; // Header used for #emote style messages.. - sm->header[1] = 0x00; // Play around with these to see other types - sm->header[2] = 0x00; - sm->msg_type = type; - memcpy(sm->message, buffer, len+1); + auto app = new EQApplicationPacket(OP_SpecialMesg, buf); FastQueuePacket(&app); @@ -1308,67 +1306,25 @@ void Client::FilteredMessage(Mob *sender, uint32 type, eqFilterType filter, cons vsnprintf(buffer, 4096, message, argptr); va_end(argptr); - size_t len = strlen(buffer); + SerializeBuffer buf(sizeof(SpecialMesgHeader_Struct) + 12 + 64 + 64); + buf.WriteInt8(static_cast(Journal::SpeakMode::Raw)); + buf.WriteInt8(static_cast(Journal::Mode::None)); + buf.WriteInt8(0); // language + buf.WriteUInt32(type); + buf.WriteUInt32(0); // target spawn ID used for journal filtering, ignored here + buf.WriteString(""); // send name, not applicable here + buf.WriteInt32(0); // location, client seems to ignore + buf.WriteInt32(0); + buf.WriteInt32(0); + buf.WriteString(buffer); - //client dosent like our packet all the time unless - //we make it really big, then it seems to not care that - //our header is malformed. - //len = 4096 - sizeof(SpecialMesg_Struct); - - uint32 len_packet = sizeof(SpecialMesg_Struct) + len; - auto app = new EQApplicationPacket(OP_SpecialMesg, len_packet); - SpecialMesg_Struct* sm = (SpecialMesg_Struct*)app->pBuffer; - sm->header[0] = 0x00; // Header used for #emote style messages.. - sm->header[1] = 0x00; // Play around with these to see other types - sm->header[2] = 0x00; - sm->msg_type = type; - memcpy(sm->message, buffer, len + 1); + auto app = new EQApplicationPacket(OP_SpecialMesg, buf); FastQueuePacket(&app); safe_delete_array(buffer); } -void Client::QuestJournalledMessage(const char *npcname, const char* message) { - - // npcnames longer than 60 characters crash the client when they log back in - const int MaxNPCNameLength = 60; - // I assume there is an upper safe limit on the message length. Don't know what it is, but 4000 doesn't crash - // the client. - const int MaxMessageLength = 4000; - - char OutNPCName[MaxNPCNameLength+1]; - char OutMessage[MaxMessageLength+1]; - - // Apparently Visual C++ snprintf is not C99 compliant and doesn't put the null terminator - // in if the formatted string >= the maximum length, so we put it in. - // - snprintf(OutNPCName, MaxNPCNameLength, "%s", npcname); OutNPCName[MaxNPCNameLength]='\0'; - snprintf(OutMessage, MaxMessageLength, "%s", message); OutMessage[MaxMessageLength]='\0'; - - uint32 len_packet = sizeof(SpecialMesg_Struct) + strlen(OutNPCName) + strlen(OutMessage); - auto app = new EQApplicationPacket(OP_SpecialMesg, len_packet); - SpecialMesg_Struct* sm=(SpecialMesg_Struct*)app->pBuffer; - - sm->header[0] = 0; - sm->header[1] = 2; - sm->header[2] = 0; - sm->msg_type = 0x0a; - sm->target_spawn_id = GetID(); - - char *dest = &sm->sayer[0]; - - memcpy(dest, OutNPCName, strlen(OutNPCName) + 1); - - dest = dest + strlen(OutNPCName) + 13; - - memcpy(dest, OutMessage, strlen(OutMessage) + 1); - - QueuePacket(app); - - safe_delete(app); -} - void Client::SetMaxHP() { if(dead) return; diff --git a/zone/client.h b/zone/client.h index a44e83087..0272fb21b 100644 --- a/zone/client.h +++ b/zone/client.h @@ -341,7 +341,6 @@ public: void ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...); void Message(uint32 type, const char* message, ...); void FilteredMessage(Mob *sender, uint32 type, eqFilterType filter, const char* message, ...); - void QuestJournalledMessage(const char *npcname, const char* message); void VoiceMacroReceived(uint32 Type, char *Target, uint32 MacroNumber); void SendSound(); void LearnRecipe(uint32 recipeID); diff --git a/zone/common.h b/zone/common.h index 62f1d9771..0ffa67468 100644 --- a/zone/common.h +++ b/zone/common.h @@ -272,6 +272,31 @@ enum class LootRequestType : uint8 { AllowedPVPDefined, }; +namespace Journal { + enum class SpeakMode : uint8 { + Raw = 0, // this just uses the raw message + Say = 1, // prints with "%1 says,%2 '%3'" if in another language else "%1 says '%2'" + Shout = 2, // prints with "%1 shouts,%2 '%3'" if in another language else "%1 shouts '%2'" + EmoteAlt = 3, // prints "%2", this should just be the same as raw ... + Emote = 4, // prints "%1 %2" if message doesn't start with "\" or "@", else "%1%2" + Group = 5 // prints "%1 tells the group,%2 '%3'" + }; + + enum class Mode : uint8 { + None = 0, + Log1 = 1, // 1 and 2 log to journal + Log2 = 2, // our current code uses 2 + }; + + struct Options { + SpeakMode speak_mode; + Mode journal_mode; + int8 language; + uint32 message_type; + uint32 target_spawn_id; // who the message is talking to (limits journaling) + }; +}; + //this is our internal representation of the BUFF struct, can put whatever we want in it struct Buffs_Struct { uint16 spellid; diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 154e28566..bdb190e9c 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -155,12 +155,31 @@ XS(XS__say); // prototype to pass -Wmissing-prototypes XS(XS__say) { dXSARGS; - if (items == 1) - quest_manager.say(SvPV_nolen(ST(0))); - else if (items == 2) - quest_manager.say(SvPV_nolen(ST(0)), (int) SvIV(ST(1))); - else - Perl_croak(aTHX_ "Usage: quest::say(string message, int language_id])"); + Journal::Options opts; + // we currently default to these + opts.speak_mode = Journal::SpeakMode::Say; + opts.journal_mode = Journal::Mode::Log2; + opts.language = 0; + opts.message_type = MT_NPCQuestSay; + if (items == 0 || items > 5) { + Perl_croak(aTHX_ "Usage: quest::say(string message, [int language_id], [int message_type], [int speak_mode], [int journal_mode])"); + } else if (items == 2) { + opts.language = (int)SvIV(ST(1)); + } else if (items == 3) { + opts.language = (int)SvIV(ST(1)); + opts.message_type = (int)SvIV(ST(2)); + } else if (items == 4) { + opts.language = (int)SvIV(ST(1)); + opts.message_type = (int)SvIV(ST(2)); + opts.speak_mode = (Journal::SpeakMode)SvIV(ST(3)); + } else if (items == 5) { + opts.language = (int)SvIV(ST(1)); + opts.message_type = (int)SvIV(ST(2)); + opts.speak_mode = (Journal::SpeakMode)SvIV(ST(3)); + opts.journal_mode = (Journal::Mode)SvIV(ST(4)); + } + + quest_manager.say(SvPV_nolen(ST(0)), opts); XSRETURN_EMPTY; } diff --git a/zone/entity.cpp b/zone/entity.cpp index 0ed696327..f7fea532b 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -3733,24 +3733,26 @@ bool Entity::CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z, } void EntityList::QuestJournalledSayClose(Mob *sender, Client *QuestInitiator, - float dist, const char* mobname, const char* message) + float dist, const char* mobname, const char* message, Journal::Options &opts) { - Client *c = nullptr; - float dist2 = dist * dist; + SerializeBuffer buf(sizeof(SpecialMesgHeader_Struct) + 12 + 64 + 64); - // Send the message to the quest initiator such that the client will enter it into the NPC Quest Journal - if (QuestInitiator) { - auto buf = new char[strlen(mobname) + strlen(message) + 10]; - sprintf(buf, "%s says, '%s'", mobname, message); - QuestInitiator->QuestJournalledMessage(mobname, buf); - safe_delete_array(buf); - } - // Use the old method for all other nearby clients - for (auto it = client_list.begin(); it != client_list.end(); ++it) { - c = it->second; - if(c && (c != QuestInitiator) && DistanceSquared(c->GetPosition(), sender->GetPosition()) <= dist2) - c->Message_StringID(10, GENERIC_SAY, mobname, message); - } + buf.WriteInt8(static_cast(opts.speak_mode)); + buf.WriteInt8(static_cast(opts.journal_mode)); + buf.WriteInt8(opts.language); + buf.WriteInt32(opts.message_type); + buf.WriteInt32(opts.target_spawn_id); + buf.WriteString(mobname); + buf.WriteInt32(0); // location, client doesn't seem to do anything with this + buf.WriteInt32(0); + buf.WriteInt32(0); + buf.WriteString(message); + + auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf); + + // client only bothers logging if target spawn ID matches, safe to send to everyone + QueueCloseClients(sender, outapp, false, dist); + delete outapp; } Corpse *EntityList::GetClosestCorpse(Mob *sender, const char *Name) diff --git a/zone/entity.h b/zone/entity.h index 084cf6f01..c50575b5b 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -29,6 +29,7 @@ #include "position.h" #include "zonedump.h" +#include "common.h" class Encounter; class Beacon; @@ -337,7 +338,7 @@ public: void SendNimbusEffects(Client *c); void SendUntargetable(Client *c); void DuelMessage(Mob* winner, Mob* loser, bool flee); - void QuestJournalledSayClose(Mob *sender, Client *QuestIntiator, float dist, const char* mobname, const char* message); + void QuestJournalledSayClose(Mob *sender, Client *QuestIntiator, float dist, const char* mobname, const char* message, Journal::Options &opts); void GroupMessage(uint32 gid, const char *from, const char *message); void ExpeditionWarning(uint32 minutes_left); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 9dd1d8c9b..2a9ec5f8a 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -36,6 +36,8 @@ struct BodyTypes { }; struct Filters { }; struct MessageTypes { }; struct Rule { }; +struct Journal_SpeakMode { }; +struct Journal_Mode { }; struct lua_registered_event { std::string encounter_name; @@ -2232,6 +2234,7 @@ luabind::scope lua_register_message_types() { return luabind::class_("MT") .enum_("constants") [ + luabind::value("NPCQuestSay", MT_NPCQuestSay), luabind::value("Say", MT_Say), luabind::value("Tell", MT_Tell), luabind::value("Group", MT_Group), @@ -2362,4 +2365,27 @@ luabind::scope lua_register_ruleb() { ]; } +luabind::scope lua_register_journal_speakmode() { + return luabind::class_("Journal_SpeakMode") + .enum_("constants") + [ + luabind::value("Raw", static_cast(Journal::SpeakMode::Raw)), + luabind::value("Say", static_cast(Journal::SpeakMode::Say)), + luabind::value("Shout", static_cast(Journal::SpeakMode::Shout)), + luabind::value("EmoteAlt", static_cast(Journal::SpeakMode::EmoteAlt)), + luabind::value("Emote", static_cast(Journal::SpeakMode::Emote)), + luabind::value("Group", static_cast(Journal::SpeakMode::Group)) + ]; +} + +luabind::scope lua_register_journal_mode() { + return luabind::class_("Journal_Mode") + .enum_("constants") + [ + luabind::value("None", static_cast(Journal::Mode::None)), + luabind::value("Log1", static_cast(Journal::Mode::Log1)), + luabind::value("Log2", static_cast(Journal::Mode::Log2)) + ]; +} + #endif diff --git a/zone/lua_general.h b/zone/lua_general.h index 4c8b7f8d2..d5b741a76 100644 --- a/zone/lua_general.h +++ b/zone/lua_general.h @@ -19,6 +19,8 @@ luabind::scope lua_register_rules_const(); luabind::scope lua_register_rulei(); luabind::scope lua_register_ruler(); luabind::scope lua_register_ruleb(); +luabind::scope lua_register_journal_speakmode(); +luabind::scope lua_register_journal_mode(); #endif #endif diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index e801f07dc..8dfc37176 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -755,7 +755,65 @@ void Lua_Mob::Say(const char *message) { void Lua_Mob::QuestSay(Lua_Client client, const char *message) { Lua_Safe_Call_Void(); - self->QuestJournalledSay(client, message); + Journal::Options journal_opts; + journal_opts.speak_mode = Journal::SpeakMode::Say; + journal_opts.journal_mode = RuleB(NPC, EnableNPCQuestJournal) ? Journal::Mode::Log2 : Journal::Mode::None; + journal_opts.language = 0; + journal_opts.message_type = MT_NPCQuestSay; + journal_opts.target_spawn_id = 0; + self->QuestJournalledSay(client, message, journal_opts); +} + +void Lua_Mob::QuestSay(Lua_Client client, const char *message, luabind::adl::object opts) { + Lua_Safe_Call_Void(); + + Journal::Options journal_opts; + // defaults + journal_opts.speak_mode = Journal::SpeakMode::Say; + journal_opts.journal_mode = Journal::Mode::Log2; + journal_opts.language = 0; + journal_opts.message_type = MT_NPCQuestSay; + journal_opts.target_spawn_id = 0; + + if (luabind::type(opts) == LUA_TTABLE) { + auto cur = opts["speak_mode"]; + if (luabind::type(cur) != LUA_TNIL) { + try { + journal_opts.speak_mode = static_cast(luabind::object_cast(cur)); + } catch (luabind::cast_failed) { + } + } + + cur = opts["journal_mode"]; + if (luabind::type(cur) != LUA_TNIL) { + try { + journal_opts.journal_mode = static_cast(luabind::object_cast(cur)); + } catch (luabind::cast_failed) { + } + } + + cur = opts["language"]; + if (luabind::type(cur) != LUA_TNIL) { + try { + journal_opts.language = luabind::object_cast(cur); + } catch (luabind::cast_failed) { + } + } + + cur = opts["message_type"]; + if (luabind::type(cur) != LUA_TNIL) { + try { + journal_opts.message_type = luabind::object_cast(cur); + } catch (luabind::cast_failed) { + } + } + } + + // if rule disables it, we override provided + if (!RuleB(NPC, EnableNPCQuestJournal)) + journal_opts.journal_mode = Journal::Mode::None; + + self->QuestJournalledSay(client, message, journal_opts); } void Lua_Mob::Shout(const char *message) { @@ -2320,7 +2378,8 @@ luabind::scope lua_register_mob() { .def("Message", &Lua_Mob::Message) .def("Message_StringID", &Lua_Mob::Message_StringID) .def("Say", &Lua_Mob::Say) - .def("QuestSay", &Lua_Mob::QuestSay) + .def("QuestSay", (void(Lua_Mob::*)(Lua_Client,const char *))&Lua_Mob::QuestSay) + .def("QuestSay", (void(Lua_Mob::*)(Lua_Client,const char *,luabind::adl::object))&Lua_Mob::QuestSay) .def("Shout", &Lua_Mob::Shout) .def("Emote", &Lua_Mob::Emote) .def("InterruptSpell", (void(Lua_Mob::*)(void))&Lua_Mob::InterruptSpell) diff --git a/zone/lua_mob.h b/zone/lua_mob.h index e7dcc1ee3..3ea4f14f5 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -169,6 +169,7 @@ public: void Message_StringID(int type, int string_id, uint32 distance); void Say(const char *message); void QuestSay(Lua_Client client, const char *message); + void QuestSay(Lua_Client client, const char *message, luabind::adl::object opts); void Shout(const char *message); void Emote(const char *message); void InterruptSpell(); diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index dda4b2d23..2c7cfe53e 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -1102,7 +1102,9 @@ void LuaParser::MapFunctions(lua_State *L) { lua_register_rules_const(), lua_register_rulei(), lua_register_ruler(), - lua_register_ruleb() + lua_register_ruleb(), + lua_register_journal_speakmode(), + lua_register_journal_mode() ]; } catch(std::exception &ex) { diff --git a/zone/mob.cpp b/zone/mob.cpp index b068905a9..ca844d459 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2911,9 +2911,13 @@ void Mob::Emote(const char *format, ...) GENERIC_EMOTE, GetCleanName(), buf); } -void Mob::QuestJournalledSay(Client *QuestInitiator, const char *str) +void Mob::QuestJournalledSay(Client *QuestInitiator, const char *str, Journal::Options &opts) { - entity_list.QuestJournalledSayClose(this, QuestInitiator, 200, GetCleanName(), str); + // just in case + if (opts.target_spawn_id == 0 && QuestInitiator) + opts.target_spawn_id = QuestInitiator->GetID(); + + entity_list.QuestJournalledSayClose(this, QuestInitiator, 200, GetCleanName(), str, opts); } const char *Mob::GetCleanName() diff --git a/zone/mob.h b/zone/mob.h index ea71017c7..d2ebe3934 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -744,7 +744,7 @@ public: const char *message6 = 0, const char *message7 = 0, const char *message8 = 0, const char *message9 = 0); void Shout(const char *format, ...); void Emote(const char *format, ...); - void QuestJournalledSay(Client *QuestInitiator, const char *str); + void QuestJournalledSay(Client *QuestInitiator, const char *str, Journal::Options &opts); int32 GetItemStat(uint32 itemid, const char *identifier); int16 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 42cb39503..79f5af8a5 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -154,30 +154,19 @@ void QuestManager::echo(int colour, const char *str) { entity_list.MessageClose(initiator, false, 200, colour, str); } -void QuestManager::say(const char *str) { +void QuestManager::say(const char *str, Journal::Options &opts) { QuestManagerCurrentQuestVars(); if (!owner) { Log(Logs::General, Logs::Quests, "QuestManager::say called with nullptr owner. Probably syntax error in quest file."); return; } else { - if(RuleB(NPC, EnableNPCQuestJournal) && initiator) { - owner->QuestJournalledSay(initiator, str); + if (!RuleB(NPC, EnableNPCQuestJournal)) + opts.journal_mode = Journal::Mode::None; + if (initiator) { + opts.target_spawn_id = initiator->GetID(); + owner->QuestJournalledSay(initiator, str, opts); } - else { - owner->Say(str); - } - } -} - -void QuestManager::say(const char *str, uint8 language) { - QuestManagerCurrentQuestVars(); - if (!owner) { - Log(Logs::General, Logs::Quests, "QuestManager::say called with nullptr owner. Probably syntax error in quest file."); - return; - } - else { - entity_list.ChannelMessage(owner, 8, language, str); } } diff --git a/zone/questmgr.h b/zone/questmgr.h index 6517872d8..1b9ea9c4d 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -62,8 +62,7 @@ public: //quest functions void echo(int colour, const char *str); - void say(const char *str); - void say(const char *str, uint8 language); + void say(const char *str, Journal::Options &opts); void me(const char *str); void summonitem(uint32 itemid, int16 charges = -1); void write(const char *file, const char *str); From 31d0adbaccc0331a4ff89d9fa486e1e0d66613b6 Mon Sep 17 00:00:00 2001 From: regneq Date: Thu, 18 Jul 2019 18:20:22 -0700 Subject: [PATCH 5/7] added language paramter to Lua_Mob::Say() and Lua_Mob::Shout() (credit to Torven) --- zone/lua_mob.cpp | 16 ++++++++++++++-- zone/lua_mob.h | 2 ++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index e801f07dc..906684dfe 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -753,6 +753,11 @@ void Lua_Mob::Say(const char *message) { self->Say(message); } +void Lua_Mob::Say(const char* message, int language) { + Lua_Safe_Call_Void(); + entity_list.ChannelMessage(self, ChatChannel_Say, language, message); // these run through the client channels and probably shouldn't for NPCs, but oh well +} + void Lua_Mob::QuestSay(Lua_Client client, const char *message) { Lua_Safe_Call_Void(); self->QuestJournalledSay(client, message); @@ -763,6 +768,11 @@ void Lua_Mob::Shout(const char *message) { self->Shout(message); } +void Lua_Mob::Shout(const char* message, int language) { + Lua_Safe_Call_Void(); + entity_list.ChannelMessage(self, ChatChannel_Shout, language, message); +} + void Lua_Mob::Emote(const char *message) { Lua_Safe_Call_Void(); self->Emote(message); @@ -2319,9 +2329,11 @@ luabind::scope lua_register_mob() { .def("GetSize", &Lua_Mob::GetSize) .def("Message", &Lua_Mob::Message) .def("Message_StringID", &Lua_Mob::Message_StringID) - .def("Say", &Lua_Mob::Say) + .def("Say", (void(Lua_Mob::*)(const char*))& Lua_Mob::Say) + .def("Say", (void(Lua_Mob::*)(const char*, int))& Lua_Mob::Say) .def("QuestSay", &Lua_Mob::QuestSay) - .def("Shout", &Lua_Mob::Shout) + .def("Shout", (void(Lua_Mob::*)(const char*))& Lua_Mob::Shout) + .def("Shout", (void(Lua_Mob::*)(const char*, int))& Lua_Mob::Shout) .def("Emote", &Lua_Mob::Emote) .def("InterruptSpell", (void(Lua_Mob::*)(void))&Lua_Mob::InterruptSpell) .def("InterruptSpell", (void(Lua_Mob::*)(int))&Lua_Mob::InterruptSpell) diff --git a/zone/lua_mob.h b/zone/lua_mob.h index e7dcc1ee3..be8eb388f 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -168,8 +168,10 @@ public: void Message(int type, const char *message); void Message_StringID(int type, int string_id, uint32 distance); void Say(const char *message); + void Say(const char* message, int language); void QuestSay(Lua_Client client, const char *message); void Shout(const char *message); + void Shout(const char* message, int language); void Emote(const char *message); void InterruptSpell(); void InterruptSpell(int spell_id); From 222fd060a37a335644c5d920c4384a4e54052152 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 19 Jul 2019 13:04:27 -0400 Subject: [PATCH 6/7] Change lua constants to be less ugly --- zone/lua_general.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 2a9ec5f8a..ef9180b68 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -2366,7 +2366,7 @@ luabind::scope lua_register_ruleb() { } luabind::scope lua_register_journal_speakmode() { - return luabind::class_("Journal_SpeakMode") + return luabind::class_("SpeakMode") .enum_("constants") [ luabind::value("Raw", static_cast(Journal::SpeakMode::Raw)), @@ -2379,7 +2379,7 @@ luabind::scope lua_register_journal_speakmode() { } luabind::scope lua_register_journal_mode() { - return luabind::class_("Journal_Mode") + return luabind::class_("JournalMode") .enum_("constants") [ luabind::value("None", static_cast(Journal::Mode::None)), From 780f8f85155a1efddb62008eb3db0eaabc2cdb66 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 20 Jul 2019 22:12:48 -0700 Subject: [PATCH 7/7] Mark old cle entries as stale when you login --- world/clientlist.cpp | 12 ++++++++++++ world/clientlist.h | 1 + world/login_server.cpp | 6 +++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/world/clientlist.cpp b/world/clientlist.cpp index 1264957a7..2c3a958d2 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -1277,7 +1277,19 @@ void ClientList::UpdateClientGuild(uint32 char_id, uint32 guild_id) { } } +void ClientList::RemoveCLEByLSID(uint32 iLSID) +{ + LinkedListIterator iterator(clientlist); + iterator.Reset(); + while (iterator.MoreElements()) { + if (iterator.GetData()->LSAccountID() == iLSID) { + iterator.RemoveCurrent(); + } + else + iterator.Advance(); + } +} int ClientList::GetClientCount() { return(numplayers); diff --git a/world/clientlist.h b/world/clientlist.h index 5b3bad297..19b8adc05 100644 --- a/world/clientlist.h +++ b/world/clientlist.h @@ -66,6 +66,7 @@ public: void CLEKeepAlive(uint32 numupdates, uint32* wid); void CLEAdd(uint32 iLSID, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin = 0, uint32 ip = 0, uint8 local=0); void UpdateClientGuild(uint32 char_id, uint32 guild_id); + void RemoveCLEByLSID(uint32 iLSID); int GetClientCount(); void GetClients(const char *zone_name, std::vector &into); diff --git a/world/login_server.cpp b/world/login_server.cpp index 61cc53bc2..73cb8a3eb 100644 --- a/world/login_server.cpp +++ b/world/login_server.cpp @@ -109,11 +109,15 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p) { auto cle = client_list.FindCLEByLSID(utwr->lsaccountid); if (cle != nullptr) { auto status = cle->GetOnline(); - if (CLE_Status_Never != status && CLE_Status_Offline != status) { + if (CLE_Status_Zoning == status || CLE_Status_InZone == status) { utwrs->response = UserToWorldStatusAlreadyOnline; SendPacket(&outpack); return; } + else { + //our existing cle is in a state we can login to, mark the old as stale and remove it. + client_list.RemoveCLEByLSID(utwr->lsaccountid); + } } }