Bunch more work on fixing up DB and packets

This commit is contained in:
Michael Cook (mackal) 2018-07-02 22:01:17 -04:00
parent 1b8736188d
commit 0ea82b5d88
8 changed files with 360 additions and 162 deletions

View File

@ -61,6 +61,7 @@ SET(common_sources
rdtsc.cpp
rulesys.cpp
say_link.cpp
serialize_buffer.cpp
serverinfo.cpp
shareddb.cpp
skills.cpp
@ -192,6 +193,7 @@ SET(common_headers
ruletypes.h
say_link.h
seperator.h
serialize_buffer.h
serverinfo.h
servertalk.h
shareddb.h

View File

@ -63,6 +63,8 @@ public:
void WriteFloat(float value) { *(float *)(pBuffer + _wpos) = value; _wpos += sizeof(float); }
void WriteDouble(double value) { *(double *)(pBuffer + _wpos) = value; _wpos += sizeof(double); }
void WriteString(const char * str) { uint32 len = static_cast<uint32>(strlen(str)) + 1; memcpy(pBuffer + _wpos, str, len); _wpos += len; }
// this is used in task system a lot, it is NOT null-termed
void WriteLengthString(uint32 len, const char *str) { *(uint32 *)(pBuffer + _wpos) = len; _wpos += sizeof(uint32); memcpy(pBuffer + _wpos, str, len); _wpos += len; }
void WriteData(const void *ptr, size_t n) { memcpy(pBuffer + _wpos, ptr, n); _wpos += n; }
uint8 ReadUInt8() { uint8 value = *(uint8 *)(pBuffer + _rpos); _rpos += sizeof(uint8); return value; }

View File

@ -20,6 +20,7 @@
#include "base_packet.h"
#include "platform.h"
#include "serialize_buffer.h"
#include <iostream>
#ifdef STATIC_OPCODE

View File

@ -0,0 +1,23 @@
#include "serialize_buffer.h"
void SerializeBuffer::Grow(size_t new_size)
{
assert(new_size > m_capacity);
auto new_buffer = new unsigned char[new_size * 2];
memset(new_buffer, 0, new_size * 2);
if (m_buffer)
memcpy(new_buffer, m_buffer, m_capacity);
m_capacity = new_size * 2;
delete[] m_buffer;
m_buffer = new_buffer;
}
void SerializeBuffer::Reset()
{
delete[] m_buffer;
m_buffer = nullptr;
m_capacity = 0;
m_pos = 0;
}

186
common/serialize_buffer.h Normal file
View File

@ -0,0 +1,186 @@
#ifndef SERIALIZE_BUFFER_H
#define SERIALIZE_BUFFER_H
#include <cstring>
#include <cassert>
#include <cstdint>
#include <string>
class SerializeBuffer
{
public:
SerializeBuffer() : m_buffer(nullptr), m_capacity(0), m_pos(0) {}
explicit SerializeBuffer(size_t size) : m_capacity(size), m_pos(0)
{
m_buffer = new unsigned char[size];
memset(m_buffer, 0, size);
}
SerializeBuffer(const SerializeBuffer &rhs)
: m_buffer(new unsigned char[rhs.m_capacity]), m_capacity(rhs.m_capacity), m_pos(rhs.m_pos)
{
memcpy(m_buffer, rhs.m_buffer, rhs.m_capacity);
}
SerializeBuffer &operator=(const SerializeBuffer &rhs)
{
if (this != &rhs) {
delete[] m_buffer;
m_buffer = new unsigned char[rhs.m_capacity];
m_capacity = rhs.m_capacity;
m_pos = rhs.m_pos;
memcpy(m_buffer, rhs.m_buffer, m_capacity);
}
return *this;
}
SerializeBuffer(SerializeBuffer &&rhs) : m_buffer(rhs.m_buffer), m_capacity(rhs.m_capacity), m_pos(rhs.m_pos)
{
rhs.m_buffer = nullptr;
rhs.m_capacity = 0;
rhs.m_pos = 0;
}
SerializeBuffer &operator=(SerializeBuffer &&rhs)
{
if (this != &rhs) {
delete[] m_buffer;
m_buffer = rhs.m_buffer;
m_capacity = rhs.m_capacity;
m_pos = rhs.m_pos;
rhs.m_buffer = nullptr;
rhs.m_capacity = 0;
rhs.m_pos = 0;
}
return *this;
}
~SerializeBuffer() { delete[] m_buffer; }
void WriteUInt8(uint8_t v)
{
if (m_pos + sizeof(uint8_t) > m_capacity)
Grow(m_capacity + sizeof(uint8_t));
*(uint8_t *)(m_buffer + m_pos) = v;
m_pos += sizeof(uint8_t);
}
void WriteUInt16(uint16_t v)
{
if (m_pos + sizeof(uint16_t) > m_capacity)
Grow(m_capacity + sizeof(uint16_t));
*(uint16_t *)(m_buffer + m_pos) = v;
m_pos += sizeof(uint16_t);
}
void WriteUInt32(uint32_t v)
{
if (m_pos + sizeof(uint32_t) > m_capacity)
Grow(m_capacity + sizeof(uint32_t));
*(uint32_t *)(m_buffer + m_pos) = v;
m_pos += sizeof(uint32_t);
}
void WriteUInt64(uint64_t v)
{
if (m_pos + sizeof(uint64_t) > m_capacity)
Grow(m_capacity + sizeof(uint64_t));
*(uint64_t *)(m_buffer + m_pos) = v;
m_pos += sizeof(uint64_t);
}
void WriteInt8(int8_t v)
{
if (m_pos + sizeof(int8_t) > m_capacity)
Grow(m_capacity + sizeof(int8_t));
*(int8_t *)(m_buffer + m_pos) = v;
m_pos += sizeof(int8_t);
}
void WriteInt16(int16_t v)
{
if (m_pos + sizeof(int16_t) > m_capacity)
Grow(m_capacity + sizeof(int16_t));
*(int16_t *)(m_buffer + m_pos) = v;
m_pos += sizeof(int16_t);
}
void WriteInt32(int32_t v)
{
if (m_pos + sizeof(int32_t) > m_capacity)
Grow(m_capacity + sizeof(int32_t));
*(int32_t *)(m_buffer + m_pos) = v;
m_pos += sizeof(int32_t);
}
void WriteInt64(int64_t v)
{
if (m_pos + sizeof(int64_t) > m_capacity)
Grow(m_capacity + sizeof(int64_t));
*(int64_t *)(m_buffer + m_pos) = v;
m_pos += sizeof(int64_t);
}
void WriteFloat(float v)
{
if (m_pos + sizeof(float) > m_capacity)
Grow(m_capacity + sizeof(float));
*(float *)(m_buffer + m_pos) = v;
m_pos += sizeof(float);
}
void WriteDouble(double v)
{
if (m_pos + sizeof(double) > m_capacity)
Grow(m_capacity + sizeof(double));
*(double *)(m_buffer + m_pos) = v;
m_pos += sizeof(double);
}
void WriteString(const char *str)
{
assert(str != nullptr);
auto len = strlen(str) + 1;
if (m_pos + len > m_capacity)
Grow(m_capacity + len);
memcpy(m_buffer + m_pos, str, len);
m_pos += len;
}
void WriteString(const std::string &str)
{
auto len = str.length() + 1;
if (m_pos + len > m_capacity)
Grow(m_capacity + len);
memcpy(m_buffer + m_pos, str.c_str(), len);
m_pos += len;
}
void WriteLengthString(uint32_t len, const char *str)
{
assert(str != nullptr);
if (m_pos + len + sizeof(uint32_t) > m_capacity)
Grow(m_capacity + len + sizeof(uint32_t));
*(uint32_t *)(m_buffer + m_pos) = len;
m_pos += sizeof(uint32_t);
memcpy(m_buffer + m_pos, str, len);
m_pos += len;
}
size_t size() const { return m_pos; }
size_t length() const { return size(); }
size_t capacity() const { return m_capacity; }
const unsigned char *buffer() const { return m_buffer; }
private:
void Grow(size_t new_size);
void Reset();
unsigned char *m_buffer;
size_t m_capacity;
size_t m_pos;
};
#endif /* !SERIALIZE_BUFFER_H */

View File

@ -3,3 +3,17 @@ ALTER TABLE `tasks` ADD `duration_code` TINYINT NOT NULL DEFAULT '0' AFTER `dura
UPDATE `tasks` SET `type` = '2'; -- we were treating them all as quests
ALTER TABLE `character_tasks` ADD `type` TINYINT NOT NULL DEFAULT '0' AFTER `slot`;
UPDATE `character_tasks` SET `type` = '2'; -- we were treating them all as quests
ALTER TABLE `activities` ADD `target_name` VARCHAR(64) NOT NULL DEFAULT '' AFTER `activitytype`;
ALTER TABLE `activities` ADD `item_list` VARCHAR(128) NOT NULL DEFAULT '' AFTER `target_name`;
ALTER TABLE `activities` ADD `skill_list` VARCHAR(64) NOT NULL DEFAULT '-1' AFTER `item_list`;
ALTER TABLE `activities` ADD `spell_list` VARCHAR(64) NOT NULL DEFAULT '0' AFTER `skill_list`;
ALTER TABLE `activities` ADD `description_override` VARCHAR(128) NOT NULL DEFAULT '' AFTER `spell_list`;
ALTER TABLE `activities` ADD `zones` VARCHAR(64) NOT NULL DEFAULT '' AFTER `zoneid`;
UPDATE `activities` SET `description_override` = `text3`;
UPDATE `activities` SET `target_name` = `text1`;
UPDATE `activities` SET `item_list` = `text2`;
UPDATE `activities` SET `zones` = `zoneid`; -- should be safe for us ...
ALTER TABLE `activities` DROP COLUMN `text1`;
ALTER TABLE `activities` DROP COLUMN `text2`;
ALTER TABLE `activities` DROP COLUMN `text3`;
ALTER TABLE `activities` DROP COLUMN `zoneid`;

View File

@ -172,21 +172,19 @@ bool TaskManager::LoadTasks(int singleTask)
}
if (singleTask == 0)
query = StringFormat("SELECT `taskid`, `step`, `activityid`, `activitytype`, "
"`text1`, `text2`, `text3`, `goalid`, `goalmethod`, "
"`goalcount`, `delivertonpc`, `zoneid`, `optional` "
"FROM `activities` "
"WHERE `taskid` < %i AND `activityid` < %i "
"ORDER BY taskid, activityid ASC",
MAXTASKS, MAXACTIVITIESPERTASK);
query =
StringFormat("SELECT `taskid`, `step`, `activityid`, `activitytype`, `target_name`, `item_list`, "
"`skill_list`, `spell_list`, `description_override`, `goalid`, `goalmethod`, "
"`goalcount`, `delivertonpc`, `zones`, `optional` FROM `activities` WHERE `taskid` < "
"%i AND `activityid` < %i ORDER BY taskid, activityid ASC",
MAXTASKS, MAXACTIVITIESPERTASK);
else
query = StringFormat("SELECT `taskid`, `step`, `activityid`, `activitytype`, "
"`text1`, `text2`, `text3`, `goalid`, `goalmethod`, "
"`goalcount`, `delivertonpc`, `zoneid`, `optional` "
"FROM `activities` "
"WHERE `taskid` = %i AND `activityid` < %i "
"ORDER BY taskid, activityid ASC",
singleTask, MAXACTIVITIESPERTASK);
query =
StringFormat("SELECT `taskid`, `step`, `activityid`, `activitytype`, `target_name`, `item_list`, "
"`skill_list`, `spell_list`, `description_override`, `goalid`, `goalmethod`, "
"`goalcount`, `delivertonpc`, `zones`, `optional` FROM `activities` WHERE `taskid` = "
"%i AND `activityid` < %i ORDER BY taskid, activityid ASC",
singleTask, MAXACTIVITIESPERTASK);
results = database.QueryDatabase(query);
if (!results.Success()) {
Log(Logs::General, Logs::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str());
@ -237,38 +235,42 @@ bool TaskManager::LoadTasks(int singleTask)
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Type = atoi(row[3]);
if (row[4][0])
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text1 = row[4];
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].target_name = row[4];
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].item_list = row[5];
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].skill_list = row[6];
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].skill_id = atoi(row[6]); // for older clients
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].spell_list = row[7];
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].spell_id = atoi(row[7]); // for older clients
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].desc_override = row[8];
if (row[5][0])
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text2 = row[5];
if (row[6][0])
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text3 = row[6];
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalID = atoi(row[7]);
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalMethod = (TaskMethodType)atoi(row[8]);
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalCount = atoi(row[9]);
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].DeliverToNPC = atoi(row[10]);
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].ZoneID = atoi(row[11]);
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Optional = atoi(row[12]);
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalID = atoi(row[9]);
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalMethod = (TaskMethodType)atoi(row[10]);
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalCount = atoi(row[11]);
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].DeliverToNPC = atoi(row[12]);
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].zones = row[13];
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].ZoneID = atoi(row[13]); // for older clients
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Optional = atoi(row[14]);
Log(Logs::General, Logs::Tasks,
"[GLOBALLOAD] Activity Slot %2i: ID %i for Task %5i. Type: %3i, GoalID: %8i, "
"GoalMethod: %i, GoalCount: %3i, ZoneID:%3i",
"GoalMethod: %i, GoalCount: %3i, Zones:%s",
Tasks[taskID]->ActivityCount, activityID, taskID,
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Type,
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalID,
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalMethod,
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalCount,
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].ZoneID);
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].zones.c_str());
Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] Text1: %s",
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text1.c_str());
Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] Text2: %s",
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text2.c_str());
Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] Text3: %s",
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text3.c_str());
Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] target_name: %s",
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].target_name.c_str());
Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] item_list: %s",
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].item_list.c_str());
Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] skill_list: %s",
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].skill_list.c_str());
Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] spell_list: %s",
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].spell_list.c_str());
Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] description_override: %s",
Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].desc_override.c_str());
Tasks[taskID]->ActivityCount++;
}
@ -1155,110 +1157,88 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task
}
void TaskManager::SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *TaskList) {
void TaskManager::SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *TaskList)
{
Log(Logs::General, Logs::Tasks, "[UPDATE] TaskSelector for %i Tasks", TaskCount);
int PlayerLevel = c->GetLevel();
// Check if any of the tasks exist
for(int i=0; i<TaskCount; i++)
{
if(Tasks[TaskList[i]] != nullptr) break;
}
int PacketLength = 12; // Header
for (int i = 0; i < TaskCount; i++)
if (Tasks[TaskList[i]] != nullptr)
break;
int ValidTasks = 0;
char StartZone[10];
for(int i=0; i<TaskCount; i++) {
if(!AppropriateLevel(TaskList[i], PlayerLevel)) continue;
if(c->IsTaskActive(TaskList[i])) continue;
if(!IsTaskRepeatable(TaskList[i]) && c->IsTaskCompleted(TaskList[i])) continue;
for (int i = 0; i < TaskCount; i++) {
if (!AppropriateLevel(TaskList[i], PlayerLevel))
continue;
if (c->IsTaskActive(TaskList[i]))
continue;
if (!IsTaskRepeatable(TaskList[i]) && c->IsTaskCompleted(TaskList[i]))
continue;
ValidTasks++;
PacketLength += 21; // Task Data - strings
PacketLength += Tasks[TaskList[i]]->Title.size() + 1 +
Tasks[TaskList[i]]->Description.size() + 1;
sprintf(StartZone, "%i", Tasks[TaskList[i]]->StartZone);
/*
PacketLength += strlen(Tasks[TaskList[i]]->Activity[ActivityID].Text1) + 1 +
strlen(Tasks[TaskList[i]]->Activity[ActivityID].Text2) +
strlen(Tasks[TaskList[i]]->Activity[ActivityID].Text3) + 1 +
strlen(itoa(Tasks[TaskList[i]]->Activity[ActivityID].ZoneID)) + 1 +
3 + 3 + 5; // Other strings (Hard set for now)
*/
PacketLength += 11 + 11 + 11 + 3 + 3 + (strlen(StartZone) * 2) + 2; // Other strings (Hard set for now)
PacketLength += 28; // Activity Data - strings (Hard set for 1 activity per task for now)
}
if(ValidTasks == 0) return;
if (ValidTasks == 0)
return;
auto outapp = new EQApplicationPacket(OP_OpenNewTasksWindow, PacketLength);
SerializeBuffer buf(50 * ValidTasks);
outapp->WriteUInt32(ValidTasks); // TaskCount
outapp->WriteUInt32(2); // Type, valid values: 0-3. 0 = Task, 1 = Shared Task, 2 = Quest, 3 = ???
buf.WriteUInt32(ValidTasks); // TaskCount
buf.WriteUInt32(2); // Type, valid values: 0-3. 0 = Task, 1 = Shared Task, 2 = Quest, 3 = ??? -- should fix maybe some day, but we let more than 1 type through :P
// so I guess an NPC can only offer one type of quests or we can only open a selection with one type :P (so quest call can tell us I guess)
// this is also sent in OP_TaskDescription
outapp->WriteUInt32(mob->GetID()); // TaskGiver
buf.WriteUInt32(mob->GetID()); // TaskGiver
for(int i=0; i<TaskCount;i++) { // max 40
for (int i = 0; i < TaskCount; i++) { // max 40
if (!AppropriateLevel(TaskList[i], PlayerLevel))
continue;
if (c->IsTaskActive(TaskList[i]))
continue;
if (!IsTaskRepeatable(TaskList[i]) && c->IsTaskCompleted(TaskList[i]))
continue;
if(!AppropriateLevel(TaskList[i], PlayerLevel)) continue;
buf.WriteUInt32(TaskList[i]); // TaskID
buf.WriteFloat(1.0f); // affects color, difficulty?
buf.WriteUInt32(Tasks[TaskList[i]]->Duration);
buf.WriteUInt32(static_cast<int>(Tasks[TaskList[i]]->dur_code)); // 1 = Short, 2 = Medium, 3 = Long, anything else Unlimited
if(c->IsTaskActive(TaskList[i])) continue;
buf.WriteString(Tasks[TaskList[i]]->Title.c_str()); // max 64 with null
buf.WriteString(Tasks[TaskList[i]]->Description.c_str()); // max 4000 with null
if(!IsTaskRepeatable(TaskList[i]) && c->IsTaskCompleted(TaskList[i])) continue;
buf.WriteUInt8(0); // Has reward set flag
buf.WriteUInt32(Tasks[TaskList[i]]->ActivityCount); // ActivityCount
outapp->WriteUInt32(TaskList[i]); // TaskID
outapp->WriteFloat(1.0f); // affects color, difficulty?
outapp->WriteUInt32(Tasks[TaskList[i]]->Duration);
outapp->WriteUInt32(static_cast<int>(Tasks[TaskList[i]]->dur_code)); // 1 = Short, 2 = Medium, 3 = Long, anything else Unlimited
for (int j = 0; j < Tasks[TaskList[i]]->ActivityCount; ++j) {
buf.WriteUInt32(j); // ActivityNumber
auto &activity = Tasks[TaskList[i]]->Activity[j];
buf.WriteUInt32(activity.Type); // ActivityType
buf.WriteUInt32(0); // solo, group, raid?
buf.WriteString(activity.target_name); // max length 64, "target name" so like loot x foo from bar (this is bar)
outapp->WriteString(Tasks[TaskList[i]]->Title.c_str()); // max 64 with null
outapp->WriteString(Tasks[TaskList[i]]->Description.c_str()); // max 4000 with null
// this string is item names
buf.WriteLengthString(activity.item_list.length(), activity.item_list.c_str());
outapp->WriteUInt8(0); // Has reward set flag
outapp->WriteUInt32(1); // ActivityCount - Hard set to 1 for now
buf.WriteUInt32(activity.GoalCount); // GoalCount
// Activity stuff below - Will need to iterate through each task
// Currently hard set for testing
// this string is skill IDs? probably one of the "use on" tasks
buf.WriteLengthString(activity.skill_list.length(), activity.skill_list.c_str());
// this string is spell IDs? probably one of the "use on" tasks
buf.WriteLengthString(activity.spell_list.length(), activity.spell_list.c_str());
sprintf(StartZone, "%i", Tasks[TaskList[i]]->StartZone);
outapp->WriteUInt32(0); // ActivityNumber
outapp->WriteUInt32(1); // ActivityType
outapp->WriteUInt32(0); // solo, group, raid?
outapp->WriteString("Text1 Test"); // max length 64 -- affects what string ID it will use for the description, should be a name here?
// this string is item names
outapp->WriteUInt32(11); // Text2Len
outapp->WriteString("Text2 Test");
outapp->WriteUInt32(1); // GoalCount
// this string is skill IDs? probably one of the "use on" tasks
outapp->WriteUInt32(3); // NumString1Len
outapp->WriteString("-1");
// this string is spell IDs? probably one of the "use on" tasks
outapp->WriteUInt32(3); // NumString2Len
outapp->WriteString("-1");
//outapp->WriteString(itoa(Tasks[TaskList[i]]->Activity[ActivityID].ZoneID));
outapp->WriteString(StartZone); // Zone number in ascii max length 64, can be multiple with separated by ;
outapp->WriteString("Text3 Test"); // max length 128 -- overrides the automatic descriptions
outapp->WriteString(StartZone); // Zone number in ascii max length 64, probably can be separated by ; too, haven't found it used
//buf.WriteString(itoa(Tasks[TaskList[i]]->Activity[ActivityID].ZoneID));
buf.WriteString(activity.zones); // Zone number in ascii max length 64, can be multiple with separated by ;
buf.WriteString(activity.desc_override); // max length 128 -- overrides the automatic descriptions
// this doesn't appear to be shown to the client at all and isn't the same as zones ... defaults to '0' though
buf.WriteString(activity.zones); // Zone number in ascii max length 64, probably can be separated by ; too, haven't found it used
}
}
auto outapp = new EQApplicationPacket(OP_OpenNewTasksWindow, buf.buffer(), buf.length());
c->QueuePacket(outapp);
safe_delete(outapp);
@ -2609,12 +2589,12 @@ void TaskManager::SendTaskActivityLong(Client *c, int TaskID, int ActivityID, in
tah->ActivityID = ActivityID;
tah->unknown3 = 0x00000000;
// We send our 'internal' types as ActivityUse1. text3 should be set to the activity description, so it makes
// We send our 'internal' types as ActivityCastOn. text3 should be set to the activity description, so it makes
// no difference to the client. All activity updates will be done based on our interal activity types.
if((Tasks[TaskID]->Activity[ActivityID].Type > 0) && Tasks[TaskID]->Activity[ActivityID].Type < 100)
tah->ActivityType = Tasks[TaskID]->Activity[ActivityID].Type;
else
tah->ActivityType = ActivityUse1;
tah->ActivityType = ActivityCastOn;
tah->Optional = Optional;
tah->unknown5 = 0x00000000;
@ -2667,77 +2647,59 @@ void TaskManager::SendTaskActivityLong(Client *c, int TaskID, int ActivityID, in
}
// Used only by RoF+ Clients
void TaskManager::SendTaskActivityNew(Client *c, int TaskID, int ActivityID, int ClientTaskIndex, bool Optional, bool TaskComplete) {
void TaskManager::SendTaskActivityNew(Client *c, int TaskID, int ActivityID, int ClientTaskIndex, bool Optional, bool TaskComplete)
{
SerializeBuffer buf(100);
uint32 String2Len = 3;
if(TaskComplete)
String2Len = 4;
buf.WriteUInt32(ClientTaskIndex); // TaskSequenceNumber
buf.WriteUInt32(static_cast<uint32>(Tasks[TaskID]->type)); // task type
buf.WriteUInt32(TaskID);
buf.WriteUInt32(ActivityID);
buf.WriteUInt32(0); // unknown3
long PacketLength = 29 + 4 + 8 + 4 + 4 + 5;
PacketLength = PacketLength + Tasks[TaskID]->Activity[ActivityID].Text1.size() + 1 +
Tasks[TaskID]->Activity[ActivityID].Text2.size() + 1 +
Tasks[TaskID]->Activity[ActivityID].Text3.size() + 1 +
((strlen(itoa(Tasks[TaskID]->Activity[ActivityID].ZoneID)) + 1) * 2) +
3 + String2Len;
auto outapp = new EQApplicationPacket(OP_TaskActivity, PacketLength);
outapp->WriteUInt32(ClientTaskIndex); // TaskSequenceNumber
outapp->WriteUInt32(static_cast<uint32>(Tasks[TaskID]->type)); // task type
outapp->WriteUInt32(TaskID);
outapp->WriteUInt32(ActivityID);
outapp->WriteUInt32(0); // unknown3
// We send our 'internal' types as ActivityUse1. text3 should be set to the activity description, so it makes
// We send our 'internal' types as ActivityCastOn. text3 should be set to the activity description, so it makes
// no difference to the client. All activity updates will be done based on our interal activity types.
if((Tasks[TaskID]->Activity[ActivityID].Type > 0) && Tasks[TaskID]->Activity[ActivityID].Type < 100)
outapp->WriteUInt32(Tasks[TaskID]->Activity[ActivityID].Type);
buf.WriteUInt32(Tasks[TaskID]->Activity[ActivityID].Type);
else
outapp->WriteUInt32(ActivityUse1);
buf.WriteUInt32(ActivityCastOn); // w/e!
outapp->WriteUInt32(Optional);
outapp->WriteUInt8(0); // unknown5
buf.WriteUInt8(Optional);
buf.WriteUInt32(0); // solo, group, raid
// One of these unknown fields maybe related to the 'Use On' activity types
outapp->WriteString(Tasks[TaskID]->Activity[ActivityID].Text1.c_str());
buf.WriteString(Tasks[TaskID]->Activity[ActivityID].target_name); // target name string
outapp->WriteUInt32(Tasks[TaskID]->Activity[ActivityID].Text2.length() + 1); // String Length - Add in null terminator
outapp->WriteString(Tasks[TaskID]->Activity[ActivityID].Text2.c_str());
buf.WriteLengthString(Tasks[TaskID]->Activity[ActivityID].item_list.length(), Tasks[TaskID]->Activity[ActivityID].item_list.c_str()); // item name list
// Goal Count
if(Tasks[TaskID]->Activity[ActivityID].Type != ActivityGiveCash)
outapp->WriteUInt32(Tasks[TaskID]->Activity[ActivityID].GoalCount);
buf.WriteUInt32(Tasks[TaskID]->Activity[ActivityID].GoalCount);
else
outapp->WriteUInt32(1); // GoalCount
buf.WriteUInt32(1); // GoalCount
outapp->WriteUInt32(3); // String Length - Add in null terminator
outapp->WriteString("-1");
// skill ID list ; separated
buf.WriteLengthString(Tasks[TaskID]->Activity[ActivityID].skill_list.length(), Tasks[TaskID]->Activity[ActivityID].skill_list.c_str());
if(!TaskComplete) {
outapp->WriteUInt32(3); // String Length - Add in null terminator
outapp->WriteString("-1");
}
else
{
outapp->WriteUInt32(4); // String Length - Add in null terminator
outapp->WriteString("-54");
}
// spelll ID list ; separated -- unsure wtf we're doing here
buf.WriteLengthString(Tasks[TaskID]->Activity[ActivityID].spell_list.length(), Tasks[TaskID]->Activity[ActivityID].spell_list.c_str());
outapp->WriteString(itoa(Tasks[TaskID]->Activity[ActivityID].ZoneID));
outapp->WriteUInt32(0); // unknown7
buf.WriteString(Tasks[TaskID]->Activity[ActivityID].zones);
buf.WriteUInt32(0); // unknown7
outapp->WriteString(Tasks[TaskID]->Activity[ActivityID].Text3.c_str());
buf.WriteString(Tasks[TaskID]->Activity[ActivityID].desc_override); // description override
if(Tasks[TaskID]->Activity[ActivityID].Type != ActivityGiveCash)
outapp->WriteUInt32(c->GetTaskActivityDoneCount(ClientTaskIndex, ActivityID)); // DoneCount
buf.WriteUInt32(c->GetTaskActivityDoneCount(ClientTaskIndex, ActivityID)); // DoneCount
else
// For internal activity types, DoneCount is either 1 if the activity is complete, 0 otherwise.
outapp->WriteUInt32((c->GetTaskActivityDoneCount(ClientTaskIndex, ActivityID) >= Tasks[TaskID]->Activity[ActivityID].GoalCount));
buf.WriteUInt32((c->GetTaskActivityDoneCount(ClientTaskIndex, ActivityID) >= Tasks[TaskID]->Activity[ActivityID].GoalCount));
outapp->WriteUInt8(1); // unknown9
buf.WriteUInt8(1); // unknown9
outapp->WriteString(itoa(Tasks[TaskID]->Activity[ActivityID].ZoneID));
buf.WriteString(Tasks[TaskID]->Activity[ActivityID].zones);
auto outapp = new EQApplicationPacket(OP_TaskActivity, buf.buffer(), buf.length());
c->QueuePacket(outapp);
safe_delete(outapp);

View File

@ -105,11 +105,19 @@ struct ActivityInformation {
std::string Text1;
std::string Text2;
std::string Text3;
std::string target_name; // name mob, location -- default empty
std::string item_list; // likely defaults to empty
std::string skill_list; // IDs ; separated -- default -1
std::string spell_list; // IDs ; separated -- default 0
std::string desc_override; // overrides auto generated description -- default empty
int skill_id; // older clients, first id from above
int spell_id; // older clients, first id from above
int GoalID;
TaskMethodType GoalMethod;
int GoalCount;
int DeliverToNPC;
int ZoneID;
std::string zones; // IDs ; searated, ZoneID is the first in this list for older clients -- default empty string
bool Optional;
};
@ -154,7 +162,7 @@ struct TaskInformation {
typedef enum { ActivityHidden = 0, ActivityActive = 1, ActivityCompleted = 2 } ActivityState;
typedef enum { ActivityDeliver = 1, ActivityKill = 2, ActivityLoot = 3, ActivitySpeakWith = 4, ActivityExplore = 5,
ActivityTradeSkill = 6, ActivityFish = 7, ActivityForage = 8, ActivityUse1 = 9, ActivityUse2 = 10,
ActivityTradeSkill = 6, ActivityFish = 7, ActivityForage = 8, ActivityCastOn = 9, ActivitySkillOn = 10,
ActivityTouch = 11, ActivityCollect = 13, ActivityGiveCash = 100 } ActivityType;