[Tasks] Add task accept packet validation (#2354)

This makes it so clients can only accept tasks that have been offered
This commit is contained in:
hg 2022-07-31 18:54:42 -04:00 committed by GitHub
parent 6068085de4
commit 7c27c4350d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 66 additions and 3 deletions

View File

@ -1835,7 +1835,13 @@ void Client::Handle_OP_AcceptNewTask(const EQApplicationPacket *app)
AcceptNewTask_Struct *ant = (AcceptNewTask_Struct*)app->pBuffer;
if (ant->task_id > 0 && RuleB(TaskSystem, EnableTaskSystem) && task_state)
task_state->AcceptNewTask(this, ant->task_id, ant->task_master_id, std::time(nullptr));
{
if (task_state->CanAcceptNewTask(this, ant->task_id, ant->task_master_id))
{
task_state->AcceptNewTask(this, ant->task_id, ant->task_master_id, std::time(nullptr));
}
task_state->ClearLastOffers();
}
}
void Client::Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app)
@ -15552,8 +15558,13 @@ void Client::Handle_OP_SharedTaskAccept(const EQApplicationPacket* app)
buf->task_id
);
if (buf->task_id > 0 && RuleB(TaskSystem, EnableTaskSystem) && task_state) {
task_state->AcceptNewTask(this, buf->task_id, buf->npc_entity_id, std::time(nullptr));
if (buf->task_id > 0 && RuleB(TaskSystem, EnableTaskSystem) && task_state)
{
if (task_state->CanAcceptNewTask(this, buf->task_id, buf->npc_entity_id))
{
task_state->AcceptNewTask(this, buf->task_id, buf->npc_entity_id, std::time(nullptr));
}
task_state->ClearLastOffers();
}
}

View File

@ -2747,3 +2747,39 @@ void ClientTaskState::LockSharedTask(Client* client, bool lock)
worldserver.SendPacket(&pack);
}
}
bool ClientTaskState::CanAcceptNewTask(Client* client, int task_id, int npc_entity_id) const
{
auto it = std::find_if(m_last_offers.begin(), m_last_offers.end(),
[&](const TaskOffer& offer) { return offer.task_id == task_id; });
if (it == m_last_offers.end())
{
LogTasks("Client [{}] accepted unoffered task [{}]", client->GetName(), task_id);
return false;
}
if (npc_entity_id != it->npc_entity_id)
{
LogTasks("Client [{}] accepted task [{}] with wrong npc entity id [{}] vs offered [{}]",
client->GetName(), task_id, npc_entity_id, it->npc_entity_id);
return false;
}
NPC* npc = entity_list.GetID(it->npc_entity_id)->CastToNPC();
if (!npc) // client window disappears in this case
{
LogTasks("Client [{}] accepted task [{}] from missing npc", client->GetName(), task_id);
return false;
}
auto dist = npc->CalculateDistance(client->GetX(), client->GetY(), client->GetZ());
if (dist > MAX_TASK_SELECT_DISTANCE)
{
LogTasks("Client [{}] accepted task [{}] from npc [{}] out of range [{}]",
client->GetName(), task_id, npc->GetCleanName(), dist);
return false;
}
return true;
}

View File

@ -8,6 +8,14 @@
#include <string>
#include <algorithm>
constexpr float MAX_TASK_SELECT_DISTANCE = 60.0f; // client closes window at this distance
struct TaskOffer
{
int task_id;
uint16_t npc_entity_id;
};
class ClientTaskState {
public:
@ -57,6 +65,8 @@ public:
void ListTaskTimers(Client* client);
void KickPlayersSharedTask(Client* client);
void LockSharedTask(Client* client, bool lock);
void ClearLastOffers() { m_last_offers.clear(); }
bool CanAcceptNewTask(Client* client, int task_id, int npc_entity_id) const;
inline bool HasFreeTaskSlot() { return m_active_task.task_id == TASKSLOTEMPTY; }
@ -77,6 +87,7 @@ public:
private:
void AddReplayTimer(Client *client, ClientTaskInformation& client_task, TaskInformation& task);
void DispatchEventTaskComplete(Client* client, ClientTaskInformation& client_task, int activity_id);
void AddOffer(int task_id, uint16_t npc_entity_id) { m_last_offers.push_back({task_id, npc_entity_id}); };
void IncrementDoneCount(
Client *client,
@ -130,6 +141,7 @@ private:
std::vector<int> m_enabled_tasks;
std::vector<CompletedTaskInformation> m_completed_tasks;
int m_last_completed_task_loaded;
std::vector<TaskOffer> m_last_offers;
static void ShowClientTaskInfoMessage(ClientTaskInformation *task, Client *c);

View File

@ -761,6 +761,7 @@ void TaskManager::SendTaskSelector(Client *client, Mob *mob, int task_count, int
{
LogTasks("TaskSelector for [{}] Tasks", task_count);
int player_level = client->GetLevel();
client->GetTaskState()->ClearLastOffers();
// Check if any of the tasks exist
for (int i = 0; i < task_count; i++) {
@ -809,6 +810,7 @@ void TaskManager::SendTaskSelector(Client *client, Mob *mob, int task_count, int
buf.WriteUInt32(task_list[i]); // task_id
m_task_data[task_list[i]]->SerializeSelector(buf, client->ClientVersion());
client->GetTaskState()->AddOffer(task_list[i], mob->GetID());
}
auto outapp = std::make_unique<EQApplicationPacket>(OP_TaskSelectWindow, buf);
@ -821,6 +823,7 @@ void TaskManager::SendSharedTaskSelector(Client *client, Mob *mob, int task_coun
// request timer is only set when shared task selection shown (not for failed validations)
client->StartTaskRequestCooldownTimer();
client->GetTaskState()->ClearLastOffers();
SerializeBuffer buf;
@ -833,6 +836,7 @@ void TaskManager::SendSharedTaskSelector(Client *client, Mob *mob, int task_coun
int task_id = task_list[i];
buf.WriteUInt32(task_id);
m_task_data[task_id]->SerializeSelector(buf, client->ClientVersion());
client->GetTaskState()->AddOffer(task_id, mob->GetID());
}
auto outapp = std::make_unique<EQApplicationPacket>(OP_SharedTaskSelectWindow, buf);