From 1351f147f432d016541b2ce76465757f1f00350d Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Sat, 30 Jul 2022 21:49:57 -0400 Subject: [PATCH] [Shared Tasks] Enforce task reqs on player removal (#2342) This verifies a shared task's minimum players requirement is still met when a member is removed and schedules it for termination if not --- common/ruletypes.h | 1 + common/shared_tasks.h | 3 ++ common/tasks.h | 2 +- world/main.cpp | 1 + world/shared_task_manager.cpp | 57 +++++++++++++++++++++++++++++++++++ world/shared_task_manager.h | 8 +++++ 6 files changed, 71 insertions(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index e90383631..9bbabab02 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -555,6 +555,7 @@ RULE_BOOL(TaskSystem, RecordCompletedOptionalActivities, false, "Record complete RULE_BOOL(TaskSystem, KeepOneRecordPerCompletedTask, true, "Keep only one record per completed task") RULE_BOOL(TaskSystem, EnableTaskProximity, true, "Enable task proximity system") RULE_INT(TaskSystem, RequestCooldownTimerSeconds, 15, "Seconds between allowing characters to request tasks (live-like default: 15 seconds)") +RULE_INT(TaskSystem, SharedTasksWorldProcessRate, 6000, "Timer interval (milliseconds) that shared tasks are processed in world") RULE_CATEGORY_END() RULE_CATEGORY(Range) diff --git a/common/shared_tasks.h b/common/shared_tasks.h index e4840ea86..b300268d0 100644 --- a/common/shared_tasks.h +++ b/common/shared_tasks.h @@ -2,6 +2,7 @@ #define EQEMU_SHARED_TASKS_H #include "database.h" +#include "timer.h" #include "types.h" #include "repositories/character_data_repository.h" #include "repositories/tasks_repository.h" @@ -203,6 +204,8 @@ public: std::vector member_id_history; // past and present members for replay timers std::vector dynamic_zone_ids; + Timer terminate_timer; + protected: SharedTasksRepository::SharedTasks m_db_shared_task; diff --git a/common/tasks.h b/common/tasks.h index fe5ce193a..65986b50c 100644 --- a/common/tasks.h +++ b/common/tasks.h @@ -389,7 +389,7 @@ namespace TaskStr { constexpr uint16 NO_LONGER_MEMBER = 8942; // You are no longer a member of the shared task. constexpr uint16 YOU_MAY_NOT_REQUEST_EXPANSION = 8943; // You may not request this shared task because you do not have the required expansion. constexpr uint16 PLAYER_MAY_NOT_REQUEST_EXPANSION = 8944; // You may not request this shared task because %1 does not have the required expansion. - constexpr uint16 TWO_MIN_REQ_TASK_TERMINATED = 8945; // If your party does not meet the requirements in two minutes, the shared task will be terminated. + constexpr uint16 REQS_TWO_MIN = 8945; // If your party does not meet the requirements in two minutes, the shared task will be terminated. constexpr uint16 YOU_REPLAY_TIMER = 8946; // You may not request this shared task because you must wait %1d:%2h:%3m before you can do another task of this type. constexpr uint16 PLAYER_REPLAY_TIMER = 8947; // You may not request this shared task because %1 must wait %2d:%3h:%4m before they can do another task of this type. constexpr uint16 PLAYER_NOW_LEADER = 8948; // %1 is now the leader of your shared task, '%2'. diff --git a/world/main.cpp b/world/main.cpp index 73056e7d7..badfa24f7 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -427,6 +427,7 @@ int main(int argc, char **argv) launcher_list.Process(); LFPGroupList.Process(); adventure_manager.Process(); + shared_task_manager.Process(); dynamic_zone_manager.Process(); if (InterserverTimer.Check()) { diff --git a/world/shared_task_manager.cpp b/world/shared_task_manager.cpp index 6cdcee09b..e28e86c08 100644 --- a/world/shared_task_manager.cpp +++ b/world/shared_task_manager.cpp @@ -6,6 +6,7 @@ #include "zonelist.h" #include "zoneserver.h" #include "shared_task_world_messaging.h" +#include "../common/rulesys.h" #include "../common/repositories/character_data_repository.h" #include "../common/repositories/character_task_timers_repository.h" #include "../common/repositories/shared_task_members_repository.h" @@ -20,6 +21,11 @@ extern ClientList client_list; extern ZSList zoneserver_list; +SharedTaskManager::SharedTaskManager() + : m_process_timer{ static_cast(RuleI(TaskSystem, SharedTasksWorldProcessRate)) } +{ +} + SharedTaskManager *SharedTaskManager::SetDatabase(Database *db) { SharedTaskManager::m_database = db; @@ -196,6 +202,13 @@ void SharedTaskManager::RemoveMember(SharedTask* s, const SharedTaskMember& memb if (member.is_leader) { ChooseNewLeader(s); } + + // todo: filter requirements + if (s->GetMembers().size() < s->GetTaskData().min_players) + { + SendMembersMessageID(s, Chat::Red, TaskStr::MIN_PLAYERS); + StartTerminateTimer(s); + } } void SharedTaskManager::RemoveEveryoneFromSharedTask(SharedTask *t, uint32 requested_character_id) @@ -226,6 +239,20 @@ void SharedTaskManager::RemoveEveryoneFromSharedTask(SharedTask *t, uint32 reque PrintSharedTaskState(); } +void SharedTaskManager::Terminate(SharedTask* s) +{ + LogTasksDetail("[Process] Terminating shared task [{}]", s->GetDbSharedTask().id); + + for (const auto& member : s->GetMembers()) + { + SendRemovePlayerFromSharedTaskPacket(member.character_id, s->GetTaskData().id, true); + client_list.SendCharacterMessageID(member.character_id, Chat::Yellow, TaskStr::HAS_ENDED, {s->GetTaskData().title}); + } + + RemoveAllMembersFromDynamicZones(s); + DeleteSharedTask(s->GetDbSharedTask().id); +} + void SharedTaskManager::DeleteSharedTask(int64 shared_task_id) { LogTasksDetail( @@ -995,6 +1022,12 @@ void SharedTaskManager::AddPlayerByCharacterIdAndName( SendSharedTaskMemberList(character_id, s->GetMembers()); // new member gets full member list s->AddCharacterToMemberHistory(character_id); + // min players should be the only thing to check, other failed reqs prevent player add + if (s->GetMembers().size() >= s->GetTaskData().min_players) + { + s->terminate_timer.Disable(); + } + // add to dzs tied to shared task for (const auto &dz_id : s->dynamic_zone_ids) { auto dz = DynamicZone::FindDynamicZoneByID(dz_id); @@ -1339,6 +1372,7 @@ bool SharedTaskManager::CanAddPlayer(SharedTask *s, uint32_t character_id, std:: player_name = cle->name(); // check if player is already in a shared task + // todo: do a memory search instead of query here auto shared_task_members = SharedTaskMembersRepository::GetWhere( *m_database, fmt::format("character_id = {} LIMIT 1", character_id) @@ -1399,6 +1433,7 @@ bool SharedTaskManager::CanAddPlayer(SharedTask *s, uint32_t character_id, std:: } // check if task would exceed max level spread + // todo: cache low/high levels on SharedTask to avoid query for offline members (also usable by RemoveMember) if (s->GetTaskData().level_spread > 0) { auto characters = CharacterDataRepository::GetWhere( *m_database, @@ -1703,3 +1738,25 @@ void SharedTaskManager::HandleCompletedTask(SharedTask* s) AddReplayTimers(s); } + +void SharedTaskManager::StartTerminateTimer(SharedTask* s) +{ + s->terminate_timer.Start(120000); // 2min + SendMembersMessageID(s, Chat::Red, TaskStr::REQS_TWO_MIN); +} + +void SharedTaskManager::Process() +{ + if (!m_process_timer.Check()) + { + return; + } + + for (auto& shared_task : m_shared_tasks) + { + if (shared_task.terminate_timer.Check()) + { + Terminate(&shared_task); + } + } +} diff --git a/world/shared_task_manager.h b/world/shared_task_manager.h index d324e3a11..2f0164c7f 100644 --- a/world/shared_task_manager.h +++ b/world/shared_task_manager.h @@ -3,6 +3,7 @@ #include "../common/database.h" #include "../common/shared_tasks.h" +#include "../common/timer.h" #include "../common/repositories/character_task_timers_repository.h" class DynamicZone; @@ -20,6 +21,8 @@ struct SharedTaskActiveInvitation { class SharedTaskManager { public: + SharedTaskManager(); + SharedTaskManager *SetDatabase(Database *db); SharedTaskManager *SetContentDatabase(Database *db); @@ -65,6 +68,7 @@ public: ); void RemovePlayerFromSharedTask(SharedTask *s, uint32 character_id); void PrintSharedTaskState(); + void Process(); void RemoveMember(SharedTask* s, const SharedTaskMember& member, bool remove_from_db); void RemoveEveryoneFromSharedTask(SharedTask *s, uint32 requested_character_id); @@ -108,6 +112,8 @@ protected: // store a reference of active invitations that have been sent to players std::vector m_active_invitations{}; + Timer m_process_timer; + std::vector GetCharacterTimers( const std::vector& character_ids, const TasksRepository::Tasks& task); @@ -124,6 +130,8 @@ protected: void SendSharedTaskInvitePacket(SharedTask *s, int64 invited_character_id); void RecordSharedTaskCompletion(SharedTask *s); void RemoveAllMembersFromDynamicZones(SharedTask *s); + void StartTerminateTimer(SharedTask* s); + void Terminate(SharedTask* s); // memory search std::vector FindCharactersInSharedTasks(const std::vector &find_characters);