From ea0a54ed609c8eb7159f85c875d69791abe2ad90 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 2 Sep 2018 23:14:15 -0400 Subject: [PATCH] More work on shared tasks --- utils/sql/git/required/sharedtasks.sql | 27 ++++++++ world/CMakeLists.txt | 2 + world/net.cpp | 4 +- world/shared_tasks.cpp | 85 ++++++++++++++++++++++++++ world/shared_tasks.h | 46 ++++++++++++++ world/zoneserver.cpp | 7 +++ zone/client.h | 6 +- zone/client_process.cpp | 3 +- zone/tasks.cpp | 15 +++++ zone/tasks.h | 2 +- zone/worldserver.cpp | 38 ++++++++++++ 11 files changed, 230 insertions(+), 5 deletions(-) create mode 100644 world/shared_tasks.cpp create mode 100644 world/shared_tasks.h diff --git a/utils/sql/git/required/sharedtasks.sql b/utils/sql/git/required/sharedtasks.sql index af61a7218..3c14cdcb6 100644 --- a/utils/sql/git/required/sharedtasks.sql +++ b/utils/sql/git/required/sharedtasks.sql @@ -20,3 +20,30 @@ CREATE TABLE `task_replay_groups` ( `name` VARCHAR(128) NOT NULL DEFAULT '', PRIMARY KEY(`id`) ); +CREATE TABLE `character_task_lockouts` ( + `charid` INT NOT NULL, + `replay_group` INT NOT NULL, + `timestamp` INT NOT NULL, + PRIMARY KEY(`charid`, `replay_group`) +); +CREATE TABLE `shared_task_state` ( + `id` INT NOT NULL, + `taskid` INT NOT NULL, + `acceptedtime` INT NOT NULL, + `locked` TINYINT NOT NULL DEFAULT '0', + PRIMARY KEY(`id`) +); +CREATE TABLE `shared_task_activities` ( + `shared_id` INT NOT NULL, + `activity_id` INT NOT NULL, + `done_count` INT NOT NULL, + `completed` TINYINT, + PRIMARY KEY(`shared_id`, `activity_id`) +); +CREATE TABLE `shared_task_members` ( + `shared_id` INT NOT NULL, + `charid` INT NOT NULL, + `name` VARCHAR(64) NOT NULL, + `leader` TINYINT, + PRIMARY KEY(`charid`) +); diff --git a/world/CMakeLists.txt b/world/CMakeLists.txt index e8b999107..41b621af7 100644 --- a/world/CMakeLists.txt +++ b/world/CMakeLists.txt @@ -15,6 +15,7 @@ SET(world_sources login_server_list.cpp net.cpp queryserv.cpp + shared_tasks.cpp ucs.cpp web_interface.cpp web_interface_eqw.cpp @@ -42,6 +43,7 @@ SET(world_headers login_server_list.h net.h queryserv.h + shared_tasks.h sof_char_create_data.h ucs.h web_interface.h diff --git a/world/net.cpp b/world/net.cpp index 7065ea6c3..b7872d951 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -83,6 +83,7 @@ union semun { #include "queryserv.h" #include "web_interface.h" #include "console.h" +#include "shared_tasks.h" #include "../common/net/servertalk_server.h" #include "../zone/data_bucket.h" @@ -103,6 +104,7 @@ bool holdzones = false; const WorldConfig *Config; EQEmuLogSys LogSys; WebInterfaceList web_interface; +SharedTaskManager shared_tasks; void CatchSignal(int sig_num); void CheckForServerScript(bool force_download = false); @@ -622,4 +624,4 @@ void CheckForServerScript(bool force_download) { system("wget -N --no-check-certificate --quiet -O eqemu_server.pl https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/eqemu_server.pl"); #endif } -} \ No newline at end of file +} diff --git a/world/shared_tasks.cpp b/world/shared_tasks.cpp new file mode 100644 index 000000000..abe341d3d --- /dev/null +++ b/world/shared_tasks.cpp @@ -0,0 +1,85 @@ +#include "shared_tasks.h" +#include "clientlist.h" +#include "cliententry.h" +#include "zonelist.h" + +extern ClientList client_list; +extern ZSList zoneserver_list; + +void SharedTaskManager::HandleTaskRequest(ServerPacket *pack) +{ + if (!pack) + return; + + char tmp_str[64] = { 0 }; + int task_id = pack->ReadUInt32(); + pack->ReadString(tmp_str); + std::string leader_name = tmp_str; + int missing_count = pack->ReadUInt32(); + std::vector missing_players; + for (int i = 0; i < missing_count; ++i) { + pack->ReadString(tmp_str); + missing_players.push_back(tmp_str); + } + + int id = GetNextID(); + auto ret = tasks.insert({id, {id, task_id}}); + if (!ret.second) { + auto pc = client_list.FindCharacter(leader_name.c_str()); + if (pc) { + auto pack = new ServerPacket(ServerOP_TaskReject, leader_name.size() + 1 + 4); + pack->WriteUInt32(0); // string ID or just generic fail message + pack->WriteString(leader_name.c_str()); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + safe_delete(pack); + } // oh well + } + + auto &task = ret.first->second; + task.AddMember(leader_name, true); + + if (missing_players.empty()) { + // send instant success to leader + auto pc = client_list.FindCharacter(leader_name.c_str()); + if (pc) { + SerializeBuffer buf(10); + buf.WriteInt32(id); // task's ID + buf.WriteString(leader_name); // leader's name + + auto pack = new ServerPacket(ServerOP_TaskGrant, buf); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + safe_delete(pack); + } else { // well fuck + tasks.erase(ret.first); + } + return; + } + + task.SetMissingCount(missing_count); + for (auto &&name : missing_players) { + // look up CLEs by name, tell them we need to know if they can be added + auto pc = client_list.FindCharacter(name.c_str()); + if (pc) { + SerializeBuffer buf(10); + buf.WriteInt32(id); + buf.WriteInt32(task_id); + buf.WriteString(name); + + auto pack = new ServerPacket(ServerOP_TaskRequest, buf); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + safe_delete(pack); + } else { // asked for a toon we couldn't find ABORT! + auto pc = client_list.FindCharacter(leader_name.c_str()); + if (pc) { + auto pack = new ServerPacket(ServerOP_TaskReject, leader_name.size() + 1 + 4); + pack->WriteUInt32(0); // string ID or just generic fail message + pack->WriteString(leader_name.c_str()); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + safe_delete(pack); + } // oh well + tasks.erase(ret.first); + break; + } + } +} + diff --git a/world/shared_tasks.h b/world/shared_tasks.h new file mode 100644 index 000000000..48722c377 --- /dev/null +++ b/world/shared_tasks.h @@ -0,0 +1,46 @@ +#ifndef SHARED_TASKS_H +#define SHARED_TASKS_H +#include +#include +#include + +#include "../common/servertalk.h" + +struct SharedTaskMember { + std::string name; + bool leader; + SharedTaskMember() : leader(false) {} + SharedTaskMember(std::string name, bool leader) : name(name), leader(leader) {} +}; + +class SharedTask { +public: + SharedTask() : id(0), task_id(0), missing_count(0) {} + SharedTask(int id, int task_id) : id(id), task_id(task_id), missing_count(0) {} + ~SharedTask() {} + + void AddMember(std::string name, bool leader = false) { members.push_back({name, leader}); } + inline void SetMissingCount(int in) { missing_count = in; } + +private: + int id; // id we have in our map + int task_id; // ID of the task we're on + int missing_count; // other toons waiting to verify (out of zone, etc) + std::vector members; +}; + +class SharedTaskManager { +public: + SharedTaskManager() {} + ~SharedTaskManager() {} + + // IPC packet processing + void HandleTaskRequest(ServerPacket *pack); + +private: + inline int GetNextID() { return ++next_id; } + int next_id; + std::unordered_map tasks; +}; + +#endif /* !SHARED_TASKS_H */ diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 263aeee2f..a99a99e1b 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -35,6 +35,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "adventure_manager.h" #include "ucs.h" #include "queryserv.h" +#include "shared_tasks.h" extern ClientList client_list; extern GroupLFPList LFPGroupList; @@ -45,6 +46,7 @@ extern volatile bool UCSServerAvailable_; extern AdventureManager adventure_manager; extern UCSConnection UCSLink; extern QueryServConnection QSLink; +extern SharedTaskManager shared_tasks; void CatchSignal(int sig_num); ZoneServer::ZoneServer(std::shared_ptr connection, EQ::Net::ConsoleServer *console) @@ -1344,6 +1346,11 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { cle->ProcessTellQueue(); break; } + case ServerOP_TaskRequest: + { + shared_tasks.HandleTaskRequest(pack); + break; + } default: { Log(Logs::Detail, Logs::World_Server, "Unknown ServerOPcode from zone 0x%04x, size %d", pack->opcode, pack->size); diff --git a/zone/client.h b/zone/client.h index 015cc1670..e0183cd4c 100644 --- a/zone/client.h +++ b/zone/client.h @@ -234,6 +234,7 @@ struct PendingSharedTask { int id; int task_master_id; Timer timeout; // so if we take a very long time to get messages back from world, we time out and fail + std::vector members; // members ahh I guess if verification takes a long time raid could change? PendingSharedTask() : id(0), task_master_id(0) {} }; @@ -1035,6 +1036,7 @@ public: inline bool IsTaskEnabled(int TaskID) { return (taskstate ? taskstate->IsTaskEnabled(TaskID) : false); } inline void ProcessTaskProximities(float X, float Y, float Z) { if(taskstate) taskstate->ProcessTaskProximities(this, X, Y, Z); } inline void AssignTask(int TaskID, int NPCID, bool enforce_level_requirement = false) { if (taskstate) taskstate->AcceptNewTask(this, TaskID, NPCID, enforce_level_requirement); } + inline void AssignSharedTask(int TaskID, int NPCID, int id) { if (taskstate) taskstate->AcceptNewSharedTask(this, TaskID, NPCID, id); } inline int ActiveSpeakTask(int NPCID) { if(taskstate) return taskstate->ActiveSpeakTask(NPCID); else return 0; } inline int ActiveSpeakActivity(int NPCID, int TaskID) { if(taskstate) return taskstate->ActiveSpeakActivity(NPCID, TaskID); else return 0; } inline void FailTask(int TaskID) { if(taskstate) taskstate->FailTask(this, TaskID); } @@ -1054,10 +1056,12 @@ public: inline int GetTaskLockoutExpire(int id) { return 0; } // stub inline void SetPendingTask(int id, int task_master_id) { pending_task.id = id; pending_task.task_master_id = task_master_id; } - inline bool HasPendingTask() const { return pending_task.id == 0; } + inline bool HasPendingTask() const { return pending_task.id != 0; } inline int GetPendingTaskID() const { return pending_task.id; } inline int GetPendingTaskMasterID() const { return pending_task.task_master_id; } inline void StartPendingTimer() { pending_task.timeout.Start(PENDING_TASK_TIMEOUT); } + inline void ResetPendingTask() { pending_task.id = 0; pending_task.task_master_id = 0; pending_task.timeout.Disable(); pending_task.members.clear(); } + inline void PendingTaskAddMember(const char *name) { pending_task.members.push_back(name); } inline const EQEmu::versions::ClientVersion ClientVersion() const { return m_ClientVersion; } inline const uint32 ClientVersionBit() const { return m_ClientVersionBit; } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index a87529665..19f1cbdc4 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -161,8 +161,7 @@ bool Client::Process() { if (pending_task.timeout.Check(false)) { Message(13, "Shared task timed out."); - pending_task.id = 0; - pending_task.task_master_id = 0; + ResetPendingTask(); } if (linkdead_timer.Check()) { diff --git a/zone/tasks.cpp b/zone/tasks.cpp index acffb0ee6..5a68f1fc7 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -3444,6 +3444,11 @@ void ClientTaskState::PendSharedTask(Client *c, int TaskID, int NPCID, bool enfo bool task_failed = false; if (group) { for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) { + if (group->members[i] == c) + continue; + + c->PendingTaskAddMember(group->membername[i]); + if (group->members[i] && group->members[i]->IsClient()) { auto *client = group->members[i]->CastToClient(); auto *task_state = client->GetTaskState(); @@ -3476,6 +3481,11 @@ void ClientTaskState::PendSharedTask(Client *c, int TaskID, int NPCID, bool enfo } } else if (raid) { for (int i = 0; i < MAX_RAID_MEMBERS; ++i) { + if (raid->members[i].member == c) + continue; + + c->PendingTaskAddMember(raid->members[i].membername); + if (raid->members[i].member) { auto *client = raid->members[i].member; auto *task_state = client->GetTaskState(); @@ -3527,6 +3537,11 @@ void ClientTaskState::PendSharedTask(Client *c, int TaskID, int NPCID, bool enfo delete pack; } +void ClientTaskState::AcceptNewSharedTask(Client *c, int TaskID, int NPCID, int id) +{ + +} + void ClientTaskState::ProcessTaskProximities(Client *c, float X, float Y, float Z) { float LastX = c->ProximityX(); diff --git a/zone/tasks.h b/zone/tasks.h index 97792bcd2..b0a640353 100644 --- a/zone/tasks.h +++ b/zone/tasks.h @@ -265,7 +265,7 @@ public: int GetTaskActivityDoneCountFromTaskID(int TaskID, int ActivityID); int GetTaskStartTime(TaskType type, int index); void AcceptNewTask(Client *c, int TaskID, int NPCID, bool enforce_level_requirement = false); -// void AcceptNewSharedTask(Client *c, int TaskID, int NPCID, bool enforce_level_requirement = false); + void AcceptNewSharedTask(Client *c, int TaskID, int NPCID, int id); void PendSharedTask(Client *c, int TaskID, int NPCID, bool enforce_level_requirement = false); void FailTask(Client *c, int TaskID); int TaskTimeLeft(int TaskID); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 7b5524605..ebd37ff78 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1936,6 +1936,44 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } break; } + case ServerOP_TaskGrant: + { + int id = pack->ReadUInt32(); + char name[64] = { 0 }; + pack->ReadString(name); + auto client = entity_list.GetClientByName(name); + if (client && client->HasPendingTask()) // if they don't have it, ignore it I guess :P + client->AssignSharedTask(client->GetPendingTaskID(), client->GetPendingTaskMasterID(), id); + break; + } + case ServerOP_TaskReject: + { + int message = pack->ReadUInt32(); + char name[64] = { 0 }; + pack->ReadString(name); + auto client = entity_list.GetClientByName(name); + if (client && client->HasPendingTask()) { + client->ResetPendingTask(); + if (message == 0) + client->Message(13, "Shared task assignment has failed."); + else if (message > 0) + client->Message_StringID(13, message); + // negative nothing I guess + } + break; + } + case ServerOP_TaskRequest: + { + int id = pack->ReadUInt32(); // we need the ID when reply to world so we know which shared task we're going to + int task_id = pack->ReadUInt32(); + char name[64] = { 0 }; + pack->ReadString(name); + auto client = entity_list.GetClientByName(name); + if (client) { + // do check + } + break; + } default: { std::cout << " Unknown ZSopcode:" << (int)pack->opcode; std::cout << " size:" << pack->size << std::endl;