Simplified user interface

This commit is contained in:
dannuic
2026-04-23 16:16:29 -06:00
parent 83e29f96d7
commit 63c33ea4d6
8 changed files with 182 additions and 163 deletions
+64 -17
View File
@@ -17,7 +17,9 @@
*/
#pragma once
#include <stdint.h>
#include "patch/client_version.h"
#include <cstdint>
// Migration path: replace string_ids.h usage with ID enum values one call site at a time.
@@ -26,28 +28,73 @@ class Mob;
class EQApplicationPacket;
namespace ZoneClient::Message {
template<typename... Args>
concept AllConstChar = (std::is_convertible_v<Args, const char*> && ...);
class IMessage
{
public:
IMessage() {}
virtual ~IMessage() {}
IMessage() = default;
virtual ~IMessage() = default;
// these two are the basic string message packets
virtual EQApplicationPacket* Simple(uint32_t color, uint32_t id) const = 0;
virtual EQApplicationPacket* Formatted(uint32_t color, uint32_t id,
const char* a1 = nullptr, const char* a2 = nullptr, const char* a3 = nullptr,
const char* a4 = nullptr, const char* a5 = nullptr, const char* a6 = nullptr,
const char* a7 = nullptr, const char* a8 = nullptr, const char* a9 = nullptr) const = 0;
[[nodiscard]] virtual EQApplicationPacket* Simple(uint32_t color, uint32_t id) const = 0;
[[nodiscard]] virtual EQApplicationPacket* Formatted(uint32_t color, uint32_t id, const std::array<const char*, 9>& args) const = 0;
// These aren't technically messages, but they use the same format and are similar enough to include here
virtual EQApplicationPacket* InterruptSpell(uint32_t message, uint32_t spawn_id, uint32_t spell_id,
const char* spell_name_override = "") const = 0;
virtual EQApplicationPacket* InterruptSpell(uint32_t message, uint32_t spawn_id, const char* spell_link) const = 0;
virtual EQApplicationPacket* InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
uint32_t spell_id, const char* spell_name_override = "") const = 0;
// Everything else is specializations of logic needed to build strings that differ between patches
virtual EQApplicationPacket* Fizzle(uint32_t type, uint32_t message, uint32_t spell_id) const = 0;
virtual EQApplicationPacket* FizzleOther(uint32_t type, uint32_t message, uint32_t spell_id,
const char* caster) const = 0;
const char* name, const char* spell_link) const = 0;
};
} // namespace Zone::Message
static std::function GetComponent = [](const Client* c) -> IMessage* {
return ClientPatch::GetMessageComponent(c->GetClientVersion()).get();
};
// Helper functions to wrap the packet construction in sends
template <AllConstChar... Args>
requires (sizeof...(Args) <= 9)
void MessageString(Client* c, uint32_t type, uint32_t id, Args&&... args)
{
if constexpr (sizeof...(Args) == 0) {
ClientPatch::QueuePacket(c, &IMessage::Simple, c->GetMessageComponent().get(), type, id);
} else {
std::array<const char*, 9> a = {args...};
ClientPatch::QueuePacket(c, &IMessage::Formatted, c->GetMessageComponent().get(), type, id, a);
}
}
static auto CloseMessageString(
Mob* sender, bool ignore_sender = false, float distance = 200.f,
Mob* skipped_mob = nullptr, bool is_ack_required = true,
eqFilterType filter = FilterNone)
{
return [=]<AllConstChar... Args>(uint32_t type, uint32_t id, Args&&... args) {
static_assert(sizeof...(Args) <= 9, "Too many arguments");
auto queue_close_clients = ClientPatch::QueueCloseClients(sender, ignore_sender, distance, skipped_mob,
is_ack_required, filter);
if constexpr (sizeof...(Args) == 0) {
return queue_close_clients(&IMessage::Simple, GetComponent, type, id);
} else {
std::array<const char*, 9> a = {args...};
return queue_close_clients(&IMessage::Formatted, GetComponent, type, id, a);
}
};
}
inline void InterruptSpell(Client* c, uint32_t message, uint32_t spawn_id, const char* spell_link)
{
ClientPatch::QueuePacket(c, &IMessage::InterruptSpell, c->GetMessageComponent().get(), message, spawn_id, spell_link);
}
inline void InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name, const char* spell_link)
{
ClientPatch::QueueCloseClients(sender, true, RuleI(Range, SongMessages), nullptr, true,
sender->IsClient() ? FilterPCSpells : FilterNPCSpells)(
&IMessage::InterruptSpellOther, GetComponent, sender, message, spawn_id, name, spell_link);
}
} // namespace ZoneClient::Message
+29 -27
View File
@@ -24,6 +24,7 @@
#include "common/serialize_buffer.h"
namespace ZoneClient::Message {
EQApplicationPacket* Titanium::Simple(uint32_t color, uint32_t id) const
{
uint32_t string_id = ResolveID(id);
@@ -41,27 +42,26 @@ EQApplicationPacket* Titanium::Simple(uint32_t color, uint32_t id) const
}
EQApplicationPacket* Titanium::Formatted(
uint32_t color, uint32_t id,
const char* a1, const char* a2, const char* a3,
const char* a4, const char* a5, const char* a6,
const char* a7, const char* a8, const char* a9) const
uint32_t color, uint32_t id, const std::array<const char*, 9>& args) const
{
uint32_t string_id = ResolveID(id);
if (string_id > 0) {
if (!a1)
std::array<const char*, 9> resolved_args = args;
ResolveArguments(id, resolved_args);
if (!resolved_args[0])
return Simple(color, id);
SerializeBuffer buf(20);
buf.WriteInt32(0);
buf.WriteInt32(string_id);
buf.WriteInt32(color);
buf.WriteUInt32(0);
buf.WriteUInt32(string_id);
buf.WriteUInt32(color);
for (const auto* a : {a1, a2, a3, a4, a5, a6, a7, a8, a9}) {
for (const auto* a : resolved_args) {
if (a != nullptr)
buf.WriteString(a);
}
buf.WriteInt8(0);
buf.WriteUInt8(0);
return new EQApplicationPacket(OP_FormattedMessage, std::move(buf));
}
@@ -69,9 +69,7 @@ EQApplicationPacket* Titanium::Formatted(
return nullptr;
}
EQApplicationPacket* Titanium::InterruptSpell(
uint32_t message, uint32_t spawn_id, uint32_t spell_id,
const char* spell_name_override) const
EQApplicationPacket* Titanium::InterruptSpell(uint32_t message, uint32_t spawn_id, const char* spell_link) const
{
auto outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct));
auto ic = reinterpret_cast<InterruptCast_Struct*>(outapp->pBuffer);
@@ -82,11 +80,9 @@ EQApplicationPacket* Titanium::InterruptSpell(
return outapp;
}
EQApplicationPacket* Titanium::InterruptSpellOther(
Mob* sender, uint32_t message, uint32_t spawn_id, uint32_t spell_id,
const char* spell_name_override) const
EQApplicationPacket* Titanium::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name,
const char* spell_link) const
{
const char* name = sender->GetCleanName();
auto outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(name) + 1);
auto ic = reinterpret_cast<InterruptCast_Struct*>(outapp->pBuffer);
ic->messageid = ResolveID(message);
@@ -95,16 +91,6 @@ EQApplicationPacket* Titanium::InterruptSpellOther(
return outapp;
}
EQApplicationPacket* Titanium::Fizzle(uint32_t type, uint32_t message, uint32_t spell_id) const
{
return Simple(type, message);
}
EQApplicationPacket* Titanium::FizzleOther(uint32_t type, uint32_t message, uint32_t spell_id, const char* caster) const
{
return Formatted(type, message, caster);
}
// A value of 0 means that the string isn't mapped in this client, valid string ids start at 1
uint32_t Titanium::ResolveID(uint32_t id) const
{
@@ -112,4 +98,20 @@ uint32_t Titanium::ResolveID(uint32_t id) const
// override in patches where IDs need remapping
return id;
}
void Titanium::ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const
{
switch (id) {
case SPELL_FIZZLE:
case MISS_NOTE:
args[0] = nullptr; // drop spell link
break;
case SPELL_FIZZLE_OTHER:
case MISSED_NOTE_OTHER:
args[1] = nullptr; // drop spell link
break;
default:
break;
}
}
} // namespace ZoneClient::Message
+10 -18
View File
@@ -23,26 +23,18 @@ namespace ZoneClient::Message {
class Titanium : public IMessage
{
public:
Titanium() {}
~Titanium() override {}
Titanium() = default;
~Titanium() override = default;
EQApplicationPacket* Simple(uint32_t color, uint32_t id) const override;
[[nodiscard]] EQApplicationPacket* Simple(uint32_t color, uint32_t id) const override;
[[nodiscard]] EQApplicationPacket* Formatted(uint32_t color, uint32_t id, const std::array<const char*, 9>& args) const override;
EQApplicationPacket* Formatted(uint32_t color, uint32_t id,
const char* a1 = nullptr, const char* a2 = nullptr, const char* a3 = nullptr,
const char* a4 = nullptr, const char* a5 = nullptr, const char* a6 = nullptr,
const char* a7 = nullptr, const char* a8 = nullptr, const char* a9 = nullptr) const override;
EQApplicationPacket* InterruptSpell(uint32_t message, uint32_t spawn_id, uint32_t spell_id,
const char* spell_name_override = "") const override;
EQApplicationPacket* InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, uint32_t spell_id,
const char* spell_name_override = "") const override;
EQApplicationPacket* Fizzle(uint32_t type, uint32_t message, uint32_t spell_id) const override;
EQApplicationPacket* FizzleOther(uint32_t type, uint32_t message,
uint32_t spell_id, const char* caster) const override;
EQApplicationPacket* InterruptSpell(uint32_t message, uint32_t spawn_id, const char* spell_link) const override;
EQApplicationPacket* InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name,
const char* spell_link) const override;
protected:
virtual uint32_t ResolveID(uint32_t id) const;
[[nodiscard]] virtual uint32_t ResolveID(uint32_t id) const;
virtual void ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const;
};
} // namespace Zone::Message
} // namespace ZoneClient::Message
+29 -42
View File
@@ -73,6 +73,21 @@ uint32_t TOB::ResolveID(uint32_t id) const
}
}
void TOB::ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const
{
switch (id) {
case SPELL_FIZZLE:
case MISS_NOTE:
case SPELL_FIZZLE_OTHER:
case MISSED_NOTE_OTHER:
// take all arguments (spell link)
break;
default:
RoF2::ResolveArguments(id, args);
break;
}
}
// TOB is the first patch to fully support links in the client. This helper function is therefore internal to TOB
// because any future patches would default to the TOB message strings
static void ServerToTOBConvertLinks(std::string& message_out, const std::string& message_in)
@@ -167,11 +182,18 @@ static void ServerToTOBConvertLinks(std::string& message_out, const std::string&
}
}
EQApplicationPacket* TOB::Formatted(uint32_t color, uint32_t id, const char* a1, const char* a2, const char* a3,
const char* a4, const char* a5, const char* a6, const char* a7, const char* a8, const char* a9) const
EQApplicationPacket* TOB::Formatted(uint32_t color, uint32_t id, const std::array<const char*, 9>& args) const
// const char* a1, const char* a2, const char* a3,
// const char* a4, const char* a5, const char* a6,
// const char* a7, const char* a8, const char* a9) const
{
uint32_t string_id = ResolveID(id);
if (string_id > 0) {
std::array<const char*, 9> resolved_args = args;
ResolveArguments(id, resolved_args);
if (!resolved_args[0])
return Simple(color, id);
SerializeBuffer buffer(49);
// 49 is the minimum size needed for this packet since each arg writes at least 4 bytes
buffer.WriteUInt32(0);
@@ -180,7 +202,7 @@ EQApplicationPacket* TOB::Formatted(uint32_t color, uint32_t id, const char* a1,
buffer.WriteUInt32(string_id);
buffer.WriteUInt32(color);
for (auto a : {a1, a2, a3, a4, a5, a6, a7, a8, a9}) {
for (auto a : resolved_args) {
if (a != nullptr) {
std::string new_message;
ServerToTOBConvertLinks(new_message, a);
@@ -195,16 +217,8 @@ EQApplicationPacket* TOB::Formatted(uint32_t color, uint32_t id, const char* a1,
return nullptr;
}
EQApplicationPacket* TOB::InterruptSpell(uint32_t message, uint32_t spawn_id, uint32_t spell_id,
const char* spell_name_override) const
EQApplicationPacket* TOB::InterruptSpell(uint32_t message, uint32_t spawn_id, const char* spell_link) const
{
std::string spell_name = spell_name_override == nullptr || *spell_name_override == '\0'
? GetSpellName(spell_id)
: spell_name_override;
char spell_link[Links::MAX_LINK_SIZE];
Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id, spell_name_override);
auto outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(spell_link) + 1);
auto ic = reinterpret_cast<InterruptCast_Struct*>(outapp->pBuffer);
ic->messageid = ResolveID(message);
@@ -215,17 +229,9 @@ EQApplicationPacket* TOB::InterruptSpell(uint32_t message, uint32_t spawn_id, ui
return outapp;
}
EQApplicationPacket* TOB::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, uint32_t spell_id,
const char* spell_name_override) const
EQApplicationPacket* TOB::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name,
const char* spell_link) const
{
std::string spell_name = spell_name_override == nullptr || *spell_name_override == '\0'
? GetSpellName(spell_id)
: spell_name_override;
char spell_link[Links::MAX_LINK_SIZE];
Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id);
const char* name = sender->GetCleanName();
auto outapp = new EQApplicationPacket(OP_InterruptCast,
sizeof(InterruptCast_Struct) + strlen(name) + strlen(spell_link) + 2);
auto ic = reinterpret_cast<InterruptCast_Struct*>(outapp->pBuffer);
@@ -236,23 +242,4 @@ EQApplicationPacket* TOB::InterruptSpellOther(Mob* sender, uint32_t message, uin
return outapp;
}
EQApplicationPacket* TOB::Fizzle(uint32_t type, uint32_t message, uint32_t spell_id) const
{
std::string spell_name(GetSpellName(spell_id));
char spell_link[Links::MAX_LINK_SIZE];
Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id);
return Formatted(type, message, spell_link);
}
EQApplicationPacket* TOB::FizzleOther(uint32_t type, uint32_t message, uint32_t spell_id, const char* caster) const
{
std::string spell_name(GetSpellName(spell_id));
char spell_link[Links::MAX_LINK_SIZE];
Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id);
return Formatted(type, message, caster, spell_link);
}
} // namespace Zone::Message
} // namespace ZoneClient::Message
+6 -14
View File
@@ -26,21 +26,13 @@ public:
TOB() {}
~TOB() override {}
EQApplicationPacket* Formatted(uint32_t color, uint32_t id,
const char* a1 = nullptr, const char* a2 = nullptr, const char* a3 = nullptr,
const char* a4 = nullptr, const char* a5 = nullptr, const char* a6 = nullptr,
const char* a7 = nullptr, const char* a8 = nullptr, const char* a9 = nullptr) const override;
[[nodiscard]] EQApplicationPacket* Formatted(uint32_t color, uint32_t id, const std::array<const char*, 9>& args) const override;
EQApplicationPacket* InterruptSpell(uint32_t message, uint32_t spawn_id, uint32_t spell_id,
const char* spell_name_override) const override;
EQApplicationPacket* InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, uint32_t spell_id,
const char* spell_name_override) const override;
EQApplicationPacket* Fizzle(uint32_t type, uint32_t message, uint32_t spell_id) const override;
EQApplicationPacket*
FizzleOther(uint32_t type, uint32_t message, uint32_t spell_id, const char* caster) const override;
EQApplicationPacket* InterruptSpell(uint32_t message, uint32_t spawn_id, const char* spell_link) const override;
EQApplicationPacket* InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name, const char* spell_link) const override;
protected:
uint32_t ResolveID(uint32_t id) const override;
[[nodiscard]] uint32_t ResolveID(uint32_t id) const override;
void ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const override;
};
} // namespace Zone::Message
} // namespace ZoneClient::Message