mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-31 09:06:46 +00:00
Added component-based patch system (#5070)
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
/* EQEmu: EQEmulator
|
||||
|
||||
Copyright (C) 2001-2026 EQEmu Development Team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "client_version.h"
|
||||
|
||||
// Migration path: replace string_ids.h usage with ID enum values one call site at a time.
|
||||
|
||||
class Client;
|
||||
class Mob;
|
||||
class EQApplicationPacket;
|
||||
|
||||
namespace Message {
|
||||
|
||||
template<typename... Args>
|
||||
concept AllConstChar = (std::is_convertible_v<Args, const char*> && ...);
|
||||
|
||||
class IMessage
|
||||
{
|
||||
public:
|
||||
IMessage() = default;
|
||||
virtual ~IMessage() = default;
|
||||
|
||||
// these two are the basic string message packets
|
||||
virtual std::unique_ptr<EQApplicationPacket> Simple(uint32_t color, uint32_t id) const = 0;
|
||||
virtual std::unique_ptr<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 std::unique_ptr<EQApplicationPacket> InterruptSpell(uint32_t message, uint32_t spawn_id,
|
||||
const char* spell_link) const = 0;
|
||||
virtual std::unique_ptr<EQApplicationPacket> InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
|
||||
const char* name, const char* spell_link) const = 0;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
@@ -0,0 +1,86 @@
|
||||
/* EQEmu: EQEmulator
|
||||
|
||||
Copyright (C) 2001-2026 EQEmu Development Team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "client_version.h"
|
||||
|
||||
#include "common/patches/titanium.h"
|
||||
#include "common/patches/sof.h"
|
||||
#include "common/patches/sod.h"
|
||||
#include "common/patches/uf.h"
|
||||
#include "common/patches/rof.h"
|
||||
#include "common/patches/rof2.h"
|
||||
#include "common/patches/tob.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
using Version = EQ::versions::ClientVersion;
|
||||
|
||||
struct ClientComponents
|
||||
{
|
||||
explicit ClientComponents(Version version) : version(version)
|
||||
{
|
||||
switch (version) {
|
||||
case Version::TOB:
|
||||
messageComponent = std::make_unique<Message::TOB>();
|
||||
break;
|
||||
case Version::RoF2:
|
||||
messageComponent = std::make_unique<Message::RoF2>();
|
||||
break;
|
||||
case Version::RoF:
|
||||
messageComponent = std::make_unique<Message::RoF>();
|
||||
break;
|
||||
case Version::UF:
|
||||
messageComponent = std::make_unique<Message::UF>();
|
||||
break;
|
||||
case Version::SoD:
|
||||
messageComponent = std::make_unique<Message::SoD>();
|
||||
break;
|
||||
case Version::SoF:
|
||||
messageComponent = std::make_unique<Message::SoF>();
|
||||
break;
|
||||
case Version::Titanium:
|
||||
messageComponent = std::make_unique<Message::Titanium>();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const Version version;
|
||||
std::unique_ptr<Message::IMessage> messageComponent;
|
||||
};
|
||||
|
||||
// this array must be in the same order as the Version enum because it converts Version to index directly
|
||||
static const std::array<ClientComponents, EQ::versions::ClientVersionCount> s_patches = {
|
||||
{
|
||||
ClientComponents(Version::Unknown), // empty
|
||||
ClientComponents(Version::Client62), // empty
|
||||
ClientComponents(Version::Titanium),
|
||||
ClientComponents(Version::SoF),
|
||||
ClientComponents(Version::SoD),
|
||||
ClientComponents(Version::UF),
|
||||
ClientComponents(Version::RoF),
|
||||
ClientComponents(Version::RoF2),
|
||||
ClientComponents(Version::TOB),
|
||||
}
|
||||
};
|
||||
|
||||
const std::unique_ptr<Message::IMessage>& GetMessageComponent(Version version)
|
||||
{
|
||||
return s_patches.at(static_cast<uint32_t>(version)).messageComponent;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// Created by dannu on 4/21/2026.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/emu_versions.h"
|
||||
#include <memory>
|
||||
|
||||
namespace Message { class IMessage; }
|
||||
|
||||
// store all static functions for the different patches here, this can return nullptr for unsupported patches
|
||||
const std::unique_ptr<Message::IMessage>& GetMessageComponent(EQ::versions::ClientVersion version);
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "uf.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@@ -48,3 +49,14 @@ namespace RoF
|
||||
};
|
||||
|
||||
} /*RoF*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
class RoF : public UF
|
||||
{
|
||||
public:
|
||||
RoF() = default;
|
||||
~RoF() override = default;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "rof.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@@ -48,3 +49,14 @@ namespace RoF2
|
||||
};
|
||||
|
||||
}; /*RoF2*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
class RoF2 : public RoF
|
||||
{
|
||||
public:
|
||||
RoF2() = default;
|
||||
~RoF2() override = default;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sof.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@@ -48,3 +49,14 @@ namespace SoD
|
||||
};
|
||||
|
||||
} /*SoD*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
class SoD : public SoF
|
||||
{
|
||||
public:
|
||||
SoD() = default;
|
||||
~SoD() override = default;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "titanium.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@@ -48,3 +49,14 @@ namespace SoF
|
||||
};
|
||||
|
||||
} /*SoF*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
class SoF : public Titanium
|
||||
{
|
||||
public:
|
||||
SoF() = default;
|
||||
~SoF() override = default;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "common/raid.h"
|
||||
#include "common/rulesys.h"
|
||||
#include "common/strings.h"
|
||||
#include "zone/string_ids.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@@ -3919,3 +3920,98 @@ namespace Titanium
|
||||
return index; // as long as we guard against bad slots server side, we should be fine
|
||||
}
|
||||
} /*Titanium*/
|
||||
|
||||
namespace Message {
|
||||
std::unique_ptr<EQApplicationPacket> Titanium::Simple(uint32_t color, uint32_t id) const
|
||||
{
|
||||
uint32_t string_id = ResolveID(id);
|
||||
if (string_id > 0) {
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_SimpleMessage, sizeof(SimpleMessage_Struct));
|
||||
auto* sms = reinterpret_cast<SimpleMessage_Struct*>(outapp->pBuffer);
|
||||
sms->string_id = string_id;
|
||||
sms->color = color;
|
||||
sms->unknown8 = 0;
|
||||
|
||||
return outapp;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> Titanium::Formatted(
|
||||
uint32_t color, uint32_t id, const std::array<const char*, 9>& args) 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 buf(20);
|
||||
buf.WriteUInt32(0);
|
||||
buf.WriteUInt32(string_id);
|
||||
buf.WriteUInt32(color);
|
||||
|
||||
for (const auto* a : resolved_args) {
|
||||
if (a != nullptr)
|
||||
buf.WriteString(a);
|
||||
}
|
||||
|
||||
buf.WriteUInt8(0);
|
||||
|
||||
return std::make_unique<EQApplicationPacket>(OP_FormattedMessage, std::move(buf));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> Titanium::InterruptSpell(uint32_t message, uint32_t spawn_id,
|
||||
const char* spell_link) const
|
||||
{
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_InterruptCast, sizeof(InterruptCast_Struct));
|
||||
auto ic = reinterpret_cast<InterruptCast_Struct*>(outapp->pBuffer);
|
||||
ic->messageid = ResolveID(message);
|
||||
ic->spawnid = spawn_id;
|
||||
outapp->priority = 5;
|
||||
|
||||
return outapp;
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> Titanium::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
|
||||
const char* name,
|
||||
const char* spell_link) const
|
||||
{
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(name) + 1);
|
||||
auto ic = reinterpret_cast<InterruptCast_Struct*>(outapp->pBuffer);
|
||||
ic->messageid = ResolveID(message);
|
||||
ic->spawnid = spawn_id;
|
||||
fmt::format_to_n(ic->message, strlen(name) + 1, "{}\0", name);
|
||||
return outapp;
|
||||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
// passthrough — string IDs are defined at the base client level;
|
||||
// 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 Message
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "IMessage.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@@ -48,3 +49,30 @@ namespace Titanium
|
||||
};
|
||||
|
||||
} /*Titanium*/
|
||||
|
||||
// out-going message packets
|
||||
namespace Message {
|
||||
|
||||
class Titanium : public IMessage
|
||||
{
|
||||
public:
|
||||
Titanium() = default;
|
||||
~Titanium() override = default;
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> Simple(uint32_t color, uint32_t id) const override;
|
||||
std::unique_ptr<EQApplicationPacket> Formatted(uint32_t color, uint32_t id,
|
||||
const std::array<const char*, 9>& args) const override;
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> InterruptSpell(uint32_t message, uint32_t spawn_id,
|
||||
const char* spell_link) const override;
|
||||
std::unique_ptr<EQApplicationPacket> InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
|
||||
const char* name,
|
||||
const char* spell_link) const override;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] virtual uint32_t ResolveID(uint32_t id) const;
|
||||
virtual void ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
|
||||
+177
-79
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "common/packet_dump.h"
|
||||
#include "world/sof_char_create_data.h"
|
||||
#include "zone/string_ids.h"
|
||||
|
||||
namespace TOB
|
||||
{
|
||||
@@ -37,8 +38,8 @@ namespace TOB
|
||||
void SerializeItem(SerializeBuffer &buffer, const EQ::ItemInstance* inst, int16 slot_id, uint8 depth, ItemPacketType packet_type);
|
||||
|
||||
// message link converters
|
||||
static inline void ServerToTOBConvertLinks(std::string& message_out, const std::string& message_in);
|
||||
static inline void TOBToServerConvertLinks(std::string& message_out, const std::string& message_in);
|
||||
static void ServerToTOBConvertLinks(std::string& message_out, const std::string& message_in);
|
||||
static void TOBToServerConvertLinks(std::string& message_out, const std::string& message_in);
|
||||
|
||||
// SpawnAppearance
|
||||
static inline uint32 ServerToTOBSpawnAppearanceType(uint32 server_type);
|
||||
@@ -388,8 +389,7 @@ namespace TOB
|
||||
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sender);
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, emu->targetname);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint64, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->language);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->chan_num);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown
|
||||
@@ -397,11 +397,13 @@ namespace TOB
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language);
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str());
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0);// Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0);// Unknown
|
||||
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, "");
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0);// Unknown
|
||||
|
||||
delete[] __emu_buffer;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
@@ -651,43 +653,6 @@ namespace TOB
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_FormattedMessage)
|
||||
{
|
||||
EQApplicationPacket* in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
FormattedMessage_Struct* emu = (FormattedMessage_Struct*)in->pBuffer;
|
||||
|
||||
char* old_message_ptr = (char*)in->pBuffer;
|
||||
old_message_ptr += sizeof(FormattedMessage_Struct);
|
||||
|
||||
std::string old_message_array[9];
|
||||
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
if (*old_message_ptr == 0) { break; }
|
||||
old_message_array[i] = old_message_ptr;
|
||||
old_message_ptr += old_message_array[i].length() + 1;
|
||||
}
|
||||
|
||||
SerializeBuffer buffer;
|
||||
buffer.WriteUInt32(0); // This is a string written like the message arrays
|
||||
buffer.WriteUInt8(emu->unknown0);
|
||||
buffer.WriteUInt32(emu->string_id);
|
||||
buffer.WriteUInt32(emu->type);
|
||||
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
std::string new_message;
|
||||
ServerToTOBConvertLinks(new_message, old_message_array[i]);
|
||||
buffer.WriteLengthString(new_message);
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_FormattedMessage, buffer.size());
|
||||
outapp->WriteData(buffer.buffer(), buffer.size());
|
||||
dest->FastQueuePacket(&outapp, ack_req);
|
||||
|
||||
delete in;
|
||||
}
|
||||
|
||||
ENCODE(OP_GMTraining) {
|
||||
ENCODE_LENGTH_EXACT(GMTrainee_Struct);
|
||||
SETUP_DIRECT_ENCODE(GMTrainee_Struct, structs::GMTrainee_Struct);
|
||||
@@ -3676,10 +3641,13 @@ namespace TOB
|
||||
|
||||
uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer);
|
||||
|
||||
// this has a size limit of 11k in the client
|
||||
std::string old_message = InBuffer;
|
||||
std::string new_message;
|
||||
TOBToServerConvertLinks(new_message, old_message);
|
||||
|
||||
// there are 15 bytes after this, part of which is an unk string, check the ENCODE for the layout
|
||||
|
||||
__packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1;
|
||||
__packet->pBuffer = new unsigned char[__packet->size];
|
||||
ChannelMessage_Struct* emu = (ChannelMessage_Struct*)__packet->pBuffer;
|
||||
@@ -4780,60 +4748,59 @@ namespace TOB
|
||||
buffer.WriteInt32(0); //unsupported atm
|
||||
}
|
||||
|
||||
static inline void ServerToTOBConvertLinks(std::string& message_out, const std::string& message_in)
|
||||
static void ServerToTOBConvertLinks(std::string& message_out, const std::string& message_in)
|
||||
{
|
||||
if (message_in.find('\x12') == std::string::npos) {
|
||||
message_out = message_in;
|
||||
return;
|
||||
}
|
||||
|
||||
auto segments = Strings::Split(message_in, '\x12');
|
||||
std::vector<std::string> segments = Strings::Split(message_in, '\x12');
|
||||
for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) {
|
||||
if (segment_iter & 1) {
|
||||
auto etag = std::stoi(segments[segment_iter].substr(0, 1));
|
||||
|
||||
switch (etag) {
|
||||
case 0:
|
||||
{
|
||||
case 0: {
|
||||
size_t index = 1;
|
||||
auto item_id = segments[segment_iter].substr(index, 5);
|
||||
std::string item_id = segments[segment_iter].substr(index, 5);
|
||||
index += 5;
|
||||
|
||||
auto aug1 = segments[segment_iter].substr(index, 5);
|
||||
|
||||
std::string aug1 = segments[segment_iter].substr(index, 5);
|
||||
index += 5;
|
||||
|
||||
auto aug2 = segments[segment_iter].substr(index, 5);
|
||||
|
||||
std::string aug2 = segments[segment_iter].substr(index, 5);
|
||||
index += 5;
|
||||
|
||||
auto aug3 = segments[segment_iter].substr(index, 5);
|
||||
|
||||
std::string aug3 = segments[segment_iter].substr(index, 5);
|
||||
index += 5;
|
||||
|
||||
auto aug4 = segments[segment_iter].substr(index, 5);
|
||||
|
||||
std::string aug4 = segments[segment_iter].substr(index, 5);
|
||||
index += 5;
|
||||
|
||||
auto aug5 = segments[segment_iter].substr(index, 5);
|
||||
|
||||
std::string aug5 = segments[segment_iter].substr(index, 5);
|
||||
index += 5;
|
||||
|
||||
auto aug6 = segments[segment_iter].substr(index, 5);
|
||||
|
||||
std::string aug6 = segments[segment_iter].substr(index, 5);
|
||||
index += 5;
|
||||
|
||||
auto is_evolving = segments[segment_iter].substr(index, 1);
|
||||
|
||||
std::string is_evolving = segments[segment_iter].substr(index, 1);
|
||||
index += 1;
|
||||
|
||||
auto evolutionGroup = segments[segment_iter].substr(index, 4);
|
||||
|
||||
std::string evolutionGroup = segments[segment_iter].substr(index, 4);
|
||||
index += 4;
|
||||
|
||||
auto evolutionLevel = segments[segment_iter].substr(index, 2);
|
||||
|
||||
std::string evolutionLevel = segments[segment_iter].substr(index, 2);
|
||||
index += 2;
|
||||
|
||||
auto ornamentationIconID = segments[segment_iter].substr(index, 5);
|
||||
|
||||
std::string ornamentationIconID = segments[segment_iter].substr(index, 5);
|
||||
index += 5;
|
||||
|
||||
auto itemHash = segments[segment_iter].substr(index, 8);
|
||||
|
||||
std::string itemHash = segments[segment_iter].substr(index, 8);
|
||||
index += 8;
|
||||
|
||||
auto text = segments[segment_iter].substr(index);
|
||||
|
||||
|
||||
std::string text = segments[segment_iter].substr(index);
|
||||
|
||||
message_out.push_back('\x12');
|
||||
message_out.push_back('0'); //etag item
|
||||
message_out.append(item_id);
|
||||
@@ -4867,14 +4834,13 @@ namespace TOB
|
||||
message_out.push_back('\x12');
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
message_out.append(segments[segment_iter]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void TOBToServerConvertLinks(std::string& message_out, const std::string& message_in) {
|
||||
static void TOBToServerConvertLinks(std::string& message_out, const std::string& message_in) {
|
||||
message_out = message_in;
|
||||
}
|
||||
|
||||
@@ -5598,3 +5564,135 @@ namespace TOB
|
||||
}
|
||||
} /*TOB*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
struct TOBStringIDs
|
||||
{
|
||||
static constexpr uint32_t DisarmedTrap = 1458; // You successfully disarmed the trap
|
||||
};
|
||||
|
||||
uint32_t TOB::ResolveID(uint32_t id) const
|
||||
{
|
||||
switch (id) {
|
||||
case YOU_FLURRY:
|
||||
case BOW_DOUBLE_DAMAGE:
|
||||
case NO_INSTRUMENT_SKILL:
|
||||
case DISCIPLINE_CONLOST:
|
||||
case TGB_ON:
|
||||
case TGB_OFF:
|
||||
case DISCIPLINE_RDY:
|
||||
case SONG_NEEDS_DRUM:
|
||||
case SONG_NEEDS_WIND:
|
||||
case SONG_NEEDS_STRINGS:
|
||||
case SONG_NEEDS_BRASS:
|
||||
case YOU_CRIT_HEAL:
|
||||
case YOU_CRIT_BLAST:
|
||||
case SPELL_WORN_OFF:
|
||||
case PET_TAUNTING:
|
||||
case DISC_LEVEL_ERROR:
|
||||
case MALE_SLAYUNDEAD:
|
||||
case FEMALE_SLAYUNDEAD:
|
||||
case FINISHING_BLOW:
|
||||
case ASSASSINATES:
|
||||
case CRIPPLING_BLOW:
|
||||
case CRITICAL_HIT:
|
||||
case DEADLY_STRIKE:
|
||||
case OTHER_CRIT_HEAL:
|
||||
case OTHER_CRIT_BLAST:
|
||||
case NPC_RAMPAGE:
|
||||
case NPC_FLURRY:
|
||||
case DISCIPLINE_FEARLESS:
|
||||
case CORPSE_ITEM_LOST:
|
||||
case FATAL_BOW_SHOT:
|
||||
case CURRENT_SPELL_EFFECTS:
|
||||
case NOT_DELEGATED_MARKER:
|
||||
case STRIKETHROUGH_STRING:
|
||||
case AE_RAMPAGE:
|
||||
case DISC_LEVEL_USE_ERROR:
|
||||
case SPLIT_FAIL:
|
||||
// removed from the client
|
||||
return 0;
|
||||
case DISARMED_TRAP:
|
||||
return TOBStringIDs::DisarmedTrap;
|
||||
default:
|
||||
return RoF2::ResolveID(id);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> TOB::Formatted(uint32_t color, uint32_t id,
|
||||
const std::array<const char*, 9>& args) 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);
|
||||
// This is a string written like the message arrays, but it seems to be discarded by the client
|
||||
buffer.WriteUInt8(0); // 0 is a zone packet, 1 is a world packet -- these are always sent from zone from here
|
||||
buffer.WriteUInt32(string_id);
|
||||
buffer.WriteUInt32(color);
|
||||
|
||||
for (auto a : resolved_args) {
|
||||
if (a != nullptr) {
|
||||
std::string new_message;
|
||||
::TOB::ServerToTOBConvertLinks(new_message, a);
|
||||
buffer.WriteLengthString(new_message);
|
||||
} else
|
||||
buffer.WriteUInt32(0);
|
||||
}
|
||||
|
||||
return std::make_unique<EQApplicationPacket>(OP_FormattedMessage, std::move(buffer));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> TOB::InterruptSpell(uint32_t message, uint32_t spawn_id,
|
||||
const char* spell_link) const
|
||||
{
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(spell_link) + 1);
|
||||
auto ic = reinterpret_cast<InterruptCast_Struct*>(outapp->pBuffer);
|
||||
ic->messageid = ResolveID(message);
|
||||
ic->spawnid = spawn_id;
|
||||
fmt::format_to_n(ic->message, strlen(spell_link) + 1, "{}\0", spell_link);
|
||||
outapp->priority = 5;
|
||||
|
||||
return outapp;
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> TOB::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
|
||||
const char* name,
|
||||
const char* spell_link) const
|
||||
{
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_InterruptCast,
|
||||
sizeof(InterruptCast_Struct) + strlen(name) + strlen(spell_link) + 2);
|
||||
auto ic = reinterpret_cast<InterruptCast_Struct*>(outapp->pBuffer);
|
||||
ic->messageid = ResolveID(message);
|
||||
ic->spawnid = spawn_id;
|
||||
fmt::format_to_n(ic->message, strlen(name) + strlen(spell_link) + 2, "{}\0{}\0", name, spell_link);
|
||||
|
||||
return outapp;
|
||||
}
|
||||
|
||||
} // namespace Message
|
||||
|
||||
|
||||
+24
-3
@@ -1,6 +1,6 @@
|
||||
#ifndef COMMON_LAURION_H
|
||||
#define COMMON_LAURION_H
|
||||
#pragma once
|
||||
|
||||
#include "rof2.h"
|
||||
#include "../struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@@ -34,4 +34,25 @@ namespace TOB
|
||||
|
||||
}; /*TOB*/
|
||||
|
||||
#endif /*COMMON_LAURION_H*/
|
||||
namespace Message {
|
||||
|
||||
class TOB : public RoF2
|
||||
{
|
||||
public:
|
||||
TOB() {}
|
||||
~TOB() override {}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> Formatted(uint32_t color, uint32_t id,
|
||||
const std::array<const char*, 9>& args) const override;
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> InterruptSpell(uint32_t message, uint32_t spawn_id,
|
||||
const char* spell_link) const override;
|
||||
std::unique_ptr<EQApplicationPacket> InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
|
||||
const char* name, const char* spell_link) const override;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] uint32_t ResolveID(uint32_t id) const override;
|
||||
void ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const override;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@@ -24,7 +24,6 @@ E(OP_DeleteSpawn)
|
||||
E(OP_DisciplineUpdate)
|
||||
E(OP_ExpansionInfo)
|
||||
E(OP_ExpUpdate)
|
||||
E(OP_FormattedMessage)
|
||||
E(OP_GMTraining)
|
||||
E(OP_GMTrainSkillConfirm)
|
||||
E(OP_GroundSpawn)
|
||||
|
||||
@@ -741,8 +741,8 @@ namespace TOB {
|
||||
/*132*/ float y;
|
||||
/*136*/ float x;
|
||||
/*140*/ float z;
|
||||
/*144*/ uint8 level;
|
||||
/*145*/ uint8 type;
|
||||
/*144*/ uint8 type;
|
||||
/*145*/ uint8 level;
|
||||
/*146*/ uint8 charges; //no idea if these are right; eqlib doesn't seem to know either
|
||||
/*147*/ uint8 activatable;
|
||||
/*148*/ uint32 unknown1; //might be some timer, not sure though
|
||||
@@ -754,7 +754,7 @@ namespace TOB {
|
||||
/*004*/ int32 unknown004;
|
||||
/*008*/ EQAffect_Struct affect;
|
||||
/*160*/ uint32 slot_id;
|
||||
/*164*/ uint32 buff_fade;
|
||||
/*164*/ uint32 buff_fade; // 1: remove, 2: modify, 3: add new
|
||||
/*168*/
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sod.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@@ -48,3 +49,14 @@ namespace UF
|
||||
};
|
||||
|
||||
}; /*UF*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
class UF : public SoD
|
||||
{
|
||||
public:
|
||||
UF() = default;
|
||||
~UF() override = default;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
Reference in New Issue
Block a user