Found and added task packets (#5101)
Build / Linux (push) Waiting to run
Build / Windows (push) Waiting to run

This commit is contained in:
dannuic
2026-06-17 22:22:09 -06:00
committed by GitHub
parent dc559710ed
commit 974dbcd6ff
38 changed files with 586 additions and 79 deletions
+8
View File
@@ -74,10 +74,18 @@ public:
uint8 ReadUInt8() { uint8 value = *(uint8 *)(pBuffer + _rpos); _rpos += sizeof(uint8); return value; }
uint8 ReadUInt8(uint32 Offset) const { uint8 value = *(uint8 *)(pBuffer + Offset); return value; }
int8 ReadSInt8() { int8 value = *(int8 *)(pBuffer + _rpos); _rpos += sizeof(int8); return value; }
int8 ReadSint8(uint32 Offset) const { int8 value = *(int8 *)(pBuffer + Offset); return value; }
uint16 ReadUInt16() { uint16 value = *(uint16 *)(pBuffer + _rpos); _rpos += sizeof(uint16); return value; }
uint16 ReadUInt16(uint32 Offset) const { uint16 value = *(uint16 *)(pBuffer + Offset); return value; }
int16 ReadSInt16() { int16 value = *(int16 *)(pBuffer + _rpos); _rpos += sizeof(int16); return value; }
int16 ReadSInt16(uint32 Offset) const { int16 value = *(int16 *)(pBuffer + Offset); return value; }
uint32 ReadUInt32() { uint32 value = *(uint32 *)(pBuffer + _rpos); _rpos += sizeof(uint32); return value; }
uint32 ReadUInt32(uint32 Offset) const { uint32 value = *(uint32 *)(pBuffer + Offset); return value; }
int32 ReadSInt32() { int32 value = *(int32 *)(pBuffer + _rpos); _rpos += sizeof(int32); return value; }
int32 ReadSint32(uint32 Offset) const { int32 value = *(int32 *)(pBuffer + Offset); return value; }
float ReadFloat() { float value = *(float *)(pBuffer + _rpos); _rpos += sizeof(float); return value; }
float ReadFloat(uint32 Offset) const { float value = *(float *)(pBuffer + Offset); return value; }
void ReadString(char *str) { uint32 len = static_cast<uint32>(strlen((char *)(pBuffer + _rpos))) + 1; memcpy(str, pBuffer + _rpos, len); _rpos += len; }
void ReadString(std::string &str) { str = reinterpret_cast<char *>(pBuffer + _rpos); _rpos += str.length() + 1; }
void ReadString(char *str, uint32 Offset, uint32 MaxLength) const;
+410
View File
@@ -3164,6 +3164,416 @@ namespace TOB
FINISH_ENCODE();
}
ENCODE(OP_CompletedTasks)
{
EQApplicationPacket *in = *p;
*p = nullptr;
uint32 count = in->ReadUInt32();
uint32 out_size = sizeof(uint32); // count
std::vector<structs::CompletedTask_Struct> tasks;
tasks.reserve(count);
for (uint32 i = 0; i < count; ++i) {
structs::CompletedTask_Struct task;
task.task_id = in->ReadUInt32();
out_size += sizeof(task.task_id);
in->ReadString(task.title);
out_size += task.title.size() + 1;
task.completed_time = in->ReadUInt32();
out_size += sizeof(uint64); // zero-extended to uint64
tasks.emplace_back(std::move(task));
}
SerializeBuffer buf(out_size);
buf.WriteUInt32(count);
for (const auto& task : tasks) {
buf.WriteUInt32(task.task_id);
buf.WriteString(task.title);
buf.WriteUInt64(static_cast<uint64>(task.completed_time));
}
uchar *emu_buffer = in->pBuffer;
in->size = buf.size();
in->pBuffer = new uint8[buf.size()];
memcpy(in->pBuffer, buf.buffer(), buf.size());
delete[] emu_buffer;
dest->FastQueuePacket(&in, ack_req);
}
ENCODE(OP_TaskActivity)
{
EQApplicationPacket *in = *p;
*p = nullptr;
// Short (25-byte) and reset (activity_type==0) packets are format-compatible
if (in->size <= 25) {
dest->FastQueuePacket(&in, ack_req);
return;
}
structs::TaskActivity_Struct task;
task.task_slot_index = in->ReadUInt32();
task.task_type = in->ReadUInt32();
task.task_id = in->ReadUInt32();
task.activity_id = in->ReadUInt32();
task.list_group = in->ReadUInt32();
task.activity_type = in->ReadSInt32();
task.optional_byte = in->ReadUInt8();
in->SetReadPosition(in->GetReadPosition() + sizeof(int32)); // skip request_type
in->ReadString(task.target_name);
in->ReadLengthString(task.item_list);
task.goal_count = in->ReadUInt32();
in->ReadLengthString(task.skill_list);
in->ReadLengthString(task.spell_list);
in->ReadString(task.zones); // null-terminated in RoF+; TOB expects length-prefixed
task.dz_switch_id = in->ReadUInt32();
in->ReadString(task.description_override);
task.done_count = in->ReadUInt32();
task.flag = in->ReadUInt8();
in->ReadString(task.zones2);
uint32 out_size = 5 * sizeof(uint32) // task_slot_index..list_group
+ sizeof(int32) // activity_type
+ sizeof(uint8) // optional_byte
+ task.target_name.size() + 1
+ sizeof(uint32) + task.item_list.size()
+ sizeof(uint32) // goal_count
+ sizeof(uint32) + task.skill_list.size()
+ sizeof(uint32) + task.spell_list.size()
+ sizeof(uint32) + task.zones.size() // length-prefixed
+ 1 // empty string (new TOB field)
+ sizeof(uint32) // dz_switch_id
+ task.description_override.size() + 1
+ sizeof(uint32) // done_count
+ sizeof(uint8) // flag
+ task.zones2.size() + 1;
SerializeBuffer buf(out_size);
buf.WriteUInt32(task.task_slot_index);
buf.WriteUInt32(task.task_type);
buf.WriteUInt32(task.task_id);
buf.WriteUInt32(task.activity_id);
buf.WriteUInt32(task.list_group);
buf.WriteInt32(task.activity_type);
buf.WriteUInt8(task.optional_byte);
buf.WriteString(task.target_name);
buf.WriteLengthString(task.item_list);
buf.WriteUInt32(task.goal_count);
buf.WriteLengthString(task.skill_list);
buf.WriteLengthString(task.spell_list);
buf.WriteLengthString(task.zones); // converted null-term → length-prefixed
buf.WriteString(""); // new TOB field
buf.WriteUInt32(task.dz_switch_id);
buf.WriteString(task.description_override);
buf.WriteUInt32(task.done_count);
buf.WriteUInt8(task.flag);
buf.WriteString(task.zones2);
uchar *emu_buffer = in->pBuffer;
in->size = buf.size();
in->pBuffer = new uint8[buf.size()];
memcpy(in->pBuffer, buf.buffer(), buf.size());
delete[] emu_buffer;
dest->FastQueuePacket(&in, ack_req);
}
ENCODE(OP_TaskDescription)
{
EQApplicationPacket *in = *p;
*p = nullptr;
structs::TaskDescription_Struct task;
task.sequence_number = in->ReadUInt32();
task.task_id = in->ReadUInt32();
task.open_window = in->ReadUInt8();
task.task_type = in->ReadUInt32();
task.reward_type = in->ReadUInt32();
in->ReadString(task.title);
task.duration = in->ReadUInt32();
in->SetReadPosition(in->GetReadPosition() + sizeof(uint32)); // skip dur_code (not in TOB wire format)
task.start_time = in->ReadUInt32();
in->ReadString(task.description);
task.has_rewards = in->ReadUInt8();
task.coin_reward = in->ReadUInt32();
task.xp_reward = in->ReadUInt32();
task.faction_reward = in->ReadUInt32();
in->ReadString(task.reward_text);
in->ReadString(task.item_link);
task.points = in->ReadUInt32();
task.has_reward_selection = in->ReadUInt8();
std::string new_item_link;
ServerToTOBConvertLinks(new_item_link, task.item_link);
uint32 out_size = sizeof(uint32) + sizeof(uint32) + sizeof(uint8) + sizeof(uint32) + sizeof(uint32) // header
+ sizeof(uint32) + task.title.size() // length-prefixed title
+ sizeof(uint32) + sizeof(uint32) // duration, start_time
+ sizeof(uint32) + task.description.size() // length-prefixed description
+ sizeof(uint8) // has_rewards
+ sizeof(uint8) // has_reward_selection
+ 4 * sizeof(uint32) // player_levels
+ sizeof(uint32) + task.reward_text.size(); // length-prefixed reward_text
if (task.has_rewards) {
out_size += sizeof(uint32) // coin_reward
+ sizeof(uint8) // xp_reward as flag
+ sizeof(uint32) // faction_reward
+ sizeof(uint32) // new unknown field
+ sizeof(uint32) + new_item_link.size() // length-prefixed item_link
+ sizeof(uint32); // points
}
SerializeBuffer buf(out_size);
buf.WriteUInt32(task.sequence_number);
buf.WriteUInt32(task.task_id);
buf.WriteUInt8(task.open_window);
buf.WriteUInt32(task.task_type);
buf.WriteUInt32(task.reward_type);
buf.WriteLengthString(task.title);
buf.WriteUInt32(task.duration);
buf.WriteUInt32(task.start_time);
buf.WriteLengthString(task.description);
buf.WriteUInt8(task.has_rewards);
if (task.has_rewards) {
buf.WriteUInt32(task.coin_reward);
buf.WriteUInt8(task.xp_reward > 0 ? 1 : 0);
buf.WriteUInt32(task.faction_reward);
buf.WriteUInt32(0); // new field, no emu equivalent
buf.WriteLengthString(new_item_link);
buf.WriteUInt32(task.points);
}
buf.WriteUInt8(task.has_reward_selection);
buf.WriteUInt32(0); // player_level1
buf.WriteUInt32(0); // player_level2
buf.WriteUInt32(0); // player_level3
buf.WriteUInt32(0); // player_level4
buf.WriteLengthString(task.reward_text);
uchar *emu_buffer = in->pBuffer;
in->size = buf.size();
in->pBuffer = new uint8[buf.size()];
memcpy(in->pBuffer, buf.buffer(), buf.size());
delete[] emu_buffer;
dest->FastQueuePacket(&in, ack_req);
}
ENCODE(OP_TaskHistoryReply)
{
EQApplicationPacket *in = *p;
*p = nullptr;
in->SetReadPosition(0);
uint32 TaskIndex = in->ReadUInt32();
uint32 ActivityCount = in->ReadUInt32();
if (ActivityCount > 20)
ActivityCount = 20;
struct Act {
uint32 type;
std::string target_name;
std::string item_list;
uint32 goal_count;
uint32 unknown04;
uint32 unknown08;
uint32 zone_id;
uint32 unknown16;
std::string desc;
};
std::vector<Act> acts;
acts.reserve(ActivityCount);
for (uint32 i = 0; i < ActivityCount; ++i) {
Act a;
a.type = in->ReadUInt32();
while (uint8 c = in->ReadUInt8()) a.target_name += c;
while (uint8 c = in->ReadUInt8()) a.item_list += c;
a.goal_count = in->ReadUInt32();
a.unknown04 = in->ReadUInt32();
a.unknown08 = in->ReadUInt32();
a.zone_id = in->ReadUInt32();
a.unknown16 = in->ReadUInt32();
while (uint8 c = in->ReadUInt8()) a.desc += c;
acts.push_back(std::move(a));
}
// TOB wire format per activity (sub_1401841E0 @ 0x1401841E0):
// wire[1] uint32 ActivityType → act-1
// wire[2] null-term target_name → act+0 (max 63 stored)
// wire[3] uint32 (CSB) GoalCount assumed → act+340 // TODO: confirm field order via live capture
// wire[4] uint32 (direct) ZoneID assumed → act+48 // TODO: confirm field order via live capture
// wire[5] uint32 (CSB) unknown → act+348 // TODO: identify field
// wire[6] uint32 (CSB) unknown → act+356 // TODO: identify field
// wire[7] null-term item_list → act+364 (CXStr, max 1023)
// wire[8] null-term unknown → act+64 // TODO: identify field; sending empty
// wire[9] uint32 (direct) unknown → act+51 // TODO: identify field
// wire[10] null-term description_override assumed → act+208 (max 127) // TODO: confirm
// wire[11] null-term unknown → act+128 // TODO: identify field; sending empty
uint32 OutSize = 8;
for (const auto& a : acts) {
OutSize += 4; // wire[1]: ActivityType
OutSize += a.target_name.size() + 1; // wire[2]: target_name
OutSize += 16; // wire[3-6]: 4x uint32
OutSize += a.item_list.size() + 1; // wire[7]: item_list
OutSize += 1; // wire[8]: empty string
OutSize += 4; // wire[9]: uint32
OutSize += a.desc.size() + 1; // wire[10]: description_override
OutSize += 1; // wire[11]: empty string
}
auto outapp = new EQApplicationPacket(OP_TaskHistoryReply, OutSize);
outapp->WriteUInt32(TaskIndex);
outapp->WriteUInt32(static_cast<uint32>(acts.size()));
for (const auto& a : acts) {
outapp->WriteUInt32(a.type); // wire[1]
for (char c : a.target_name) outapp->WriteUInt8(c);
outapp->WriteUInt8(0); // wire[2]
outapp->WriteUInt32(a.goal_count); // wire[3] TODO: confirm order
outapp->WriteUInt32(a.zone_id); // wire[4] TODO: confirm order
outapp->WriteUInt32(0); // wire[5] TODO: identify
outapp->WriteUInt32(0); // wire[6] TODO: identify
for (char c : a.item_list) outapp->WriteUInt8(c);
outapp->WriteUInt8(0); // wire[7]
outapp->WriteUInt8(0); // wire[8] TODO: identify; sending empty
outapp->WriteUInt32(0); // wire[9] TODO: identify
for (char c : a.desc) outapp->WriteUInt8(c);
outapp->WriteUInt8(0); // wire[10] TODO: confirm desc here
outapp->WriteUInt8(0); // wire[11] TODO: identify; sending empty
}
delete in;
dest->FastQueuePacket(&outapp, ack_req);
}
ENCODE(OP_TaskSelectWindow)
{
EQApplicationPacket* in = *p;
*p = nullptr;
// need to calculate the size of the buffer, which can only be done after reading all the strings stored
uint32 task_count = in->ReadUInt32();
uint32 type = in->ReadUInt32();
uint32 task_giver_id = in->ReadUInt32();
uint32 out_size = 3 * sizeof(uint32); // initial size is the header here
std::vector<structs::TaskSelectWindow_Struct> tasks;
tasks.reserve(task_count);
for (int i = 0; i < task_count ; i++) {
structs::TaskSelectWindow_Struct task;
task.task_id = in->ReadUInt32();
out_size += sizeof(task.task_id);
task.reward_multiplier = in->ReadFloat();
out_size += sizeof(task.reward_multiplier);
task.duration = in->ReadUInt32();
out_size += sizeof(task.duration);
in->SetReadPosition(in->GetReadPosition() + sizeof(uint32)); // skip duration_code
in->ReadString(task.title);
out_size += task.title.size() + 1;
in->ReadString(task.description);
out_size += task.description.size() + 1;
task.preview_enable = static_cast<bool>(in->ReadUInt8());
out_size += sizeof(task.preview_enable);
uint32 selector_count = in->ReadUInt32();
out_size += sizeof(selector_count);
std::vector<structs::TaskSelectWindowSelector_Struct> selectors;
selectors.reserve(selector_count);
for (int j = 0; j < selector_count; j++) {
structs::TaskSelectWindowSelector_Struct selector;
selector.selector_id = in->ReadSInt32();
out_size += sizeof(selector.selector_id);
selector.activity_type = in->ReadSInt32();
out_size += sizeof(selector.activity_type);
in->SetReadPosition(in->GetReadPosition() + sizeof(int32)); // skip request_type
in->ReadString(selector.target_name);
out_size += selector.target_name.size() + 1;
in->ReadLengthString(selector.item_list);
out_size += sizeof(uint32) + selector.item_list.size();
selector.goal_count = in->ReadSInt32();
out_size += sizeof(selector.goal_count);
in->ReadLengthString(selector.skill_list);
out_size += sizeof(uint32) + selector.skill_list.size();
in->ReadLengthString(selector.spell_list);
out_size += sizeof(uint32) + selector.spell_list.size();
in->ReadString(selector.zones);
out_size += selector.zones.size() + 1;
in->ReadString(selector.description_override);
out_size += selector.description_override.size() + 1;
in->ReadString(selector.zones_internal);
out_size += selector.zones_internal.size() + 1;
selectors.push_back(std::move(selector));
}
task.selectors = std::move(selectors);
tasks.push_back(std::move(task));
}
SerializeBuffer buf(out_size);
buf.WriteUInt32(task_count);
buf.WriteUInt32(type);
buf.WriteUInt32(task_giver_id);
for (const auto& task : tasks) {
buf.WriteUInt32(task.task_id);
buf.WriteFloat(task.reward_multiplier);
buf.WriteUInt32(task.duration);
buf.WriteString(task.title);
buf.WriteString(task.description);
buf.WriteUInt8(task.preview_enable);
buf.WriteUInt32(task.selectors.size());
for (const auto& selector : task.selectors) {
buf.WriteUInt32(selector.selector_id);
buf.WriteUInt32(selector.activity_type);
buf.WriteString(selector.target_name);
buf.WriteLengthString(selector.item_list);
buf.WriteUInt32(selector.goal_count);
buf.WriteLengthString(selector.skill_list);
buf.WriteLengthString(selector.spell_list);
buf.WriteString(selector.zones);
buf.WriteString(selector.description_override);
buf.WriteString(selector.zones_internal);
}
}
// will need to delete this after we swap, or it will leak
uchar* emu_buffer = in->pBuffer;
// swap into in
in->size = buf.size();
in->pBuffer = new uint8[buf.size()];
memcpy(in->pBuffer, buf.buffer(), buf.size());
delete[] emu_buffer;
dest->FastQueuePacket(&in, ack_req);
}
ENCODE(OP_SharedTaskSelectWindow)
{
ENCODE_FORWARD(OP_TaskSelectWindow);
}
ENCODE(OP_Track)
{
SETUP_VAR_ENCODE(Track_Struct);
+6
View File
@@ -15,6 +15,7 @@ E(OP_CharInventory)
E(OP_Charm)
E(OP_ClickObjectAction)
E(OP_ClientUpdate)
E(OP_CompletedTasks)
E(OP_Consider)
E(OP_Damage)
E(OP_Death)
@@ -60,6 +61,7 @@ E(OP_SendMaxCharacters)
E(OP_SendMembership)
E(OP_SendMembershipDetails)
E(OP_SendZonepoints)
E(OP_SharedTaskSelectWindow)
E(OP_ShopPlayerBuy)
E(OP_ShopPlayerSell)
E(OP_ShopRequest)
@@ -68,6 +70,10 @@ E(OP_SpecialMesg)
E(OP_SpawnAppearance)
E(OP_SpawnDoor)
E(OP_Stun)
E(OP_TaskActivity)
E(OP_TaskDescription)
E(OP_TaskHistoryReply)
E(OP_TaskSelectWindow)
E(OP_Track)
E(OP_WearChange)
E(OP_Weather)
+80
View File
@@ -1304,5 +1304,85 @@ namespace TOB {
#pragma pack()
// These task structs are not directly serialized because they have variable sized members. They are used
// as intermediate structs to deserialize server values
struct CompletedTask_Struct
{
uint32 task_id;
std::string title;
uint32 completed_time;
};
struct TaskActivity_Struct
{
uint32 task_slot_index;
uint32 task_type;
uint32 task_id;
uint32 activity_id;
uint32 list_group;
int32 activity_type;
uint8 optional_byte;
std::string target_name;
std::string item_list;
uint32 goal_count;
std::string skill_list;
std::string spell_list;
std::string zones;
uint32 dz_switch_id;
std::string description_override;
uint32 done_count;
uint8 flag;
std::string zones2;
};
struct TaskDescription_Struct
{
uint32 sequence_number;
uint32 task_id;
uint8 open_window;
uint32 task_type;
uint32 reward_type;
std::string title;
uint32 duration;
uint32 start_time;
std::string description;
uint8 has_rewards;
uint32 coin_reward;
uint32 xp_reward;
uint32 faction_reward;
std::string reward_text;
std::string item_link;
uint32 points;
uint8 has_reward_selection;
};
struct TaskSelectWindowSelector_Struct
{
int32 selector_id;
int32 activity_type;
int32 request_type; // deprecated
std::string target_name;
std::string item_list;
int32 goal_count;
std::string skill_list;
std::string spell_list;
std::string zones;
std::string description_override;
std::string zones_internal;
};
struct TaskSelectWindow_Struct
{
uint32 task_id;
float reward_multiplier;
uint32 duration;
std::string title;
std::string description;
bool preview_enable;
std::vector<TaskSelectWindowSelector_Struct> selectors;
};
}; //end namespace structs
}; //end namespace tob