mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 21:01:29 +00:00
[Shared Tasks] Implement Activity Locking (#2339)
* Add shared task element locking This adds the `lock_activity_id` field to the tasks table which will automatically lock a shared task when that element becomes active. A method was added to world analogous to zone's UnlockActivities to determine when an activity is active with respect to task steps. Also adds quest apis to manually lock or unlock a client's shared task * Add comment
This commit is contained in:
parent
ea878ed27f
commit
f64d072af7
@ -42,6 +42,7 @@ public:
|
||||
std::string completion_emote;
|
||||
int replay_timer_seconds;
|
||||
int request_timer_seconds;
|
||||
int lock_activity_id;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@ -75,6 +76,7 @@ public:
|
||||
"completion_emote",
|
||||
"replay_timer_seconds",
|
||||
"request_timer_seconds",
|
||||
"lock_activity_id",
|
||||
};
|
||||
}
|
||||
|
||||
@ -104,6 +106,7 @@ public:
|
||||
"completion_emote",
|
||||
"replay_timer_seconds",
|
||||
"request_timer_seconds",
|
||||
"lock_activity_id",
|
||||
};
|
||||
}
|
||||
|
||||
@ -167,6 +170,7 @@ public:
|
||||
entry.completion_emote = "";
|
||||
entry.replay_timer_seconds = 0;
|
||||
entry.request_timer_seconds = 0;
|
||||
entry.lock_activity_id = -1;
|
||||
|
||||
return entry;
|
||||
}
|
||||
@ -225,6 +229,7 @@ public:
|
||||
entry.completion_emote = row[20] ? row[20] : "";
|
||||
entry.replay_timer_seconds = atoi(row[21]);
|
||||
entry.request_timer_seconds = atoi(row[22]);
|
||||
entry.lock_activity_id = atoi(row[23]);
|
||||
|
||||
return entry;
|
||||
}
|
||||
@ -281,6 +286,7 @@ public:
|
||||
update_values.push_back(columns[20] + " = '" + Strings::Escape(tasks_entry.completion_emote) + "'");
|
||||
update_values.push_back(columns[21] + " = " + std::to_string(tasks_entry.replay_timer_seconds));
|
||||
update_values.push_back(columns[22] + " = " + std::to_string(tasks_entry.request_timer_seconds));
|
||||
update_values.push_back(columns[23] + " = " + std::to_string(tasks_entry.lock_activity_id));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@ -325,6 +331,7 @@ public:
|
||||
insert_values.push_back("'" + Strings::Escape(tasks_entry.completion_emote) + "'");
|
||||
insert_values.push_back(std::to_string(tasks_entry.replay_timer_seconds));
|
||||
insert_values.push_back(std::to_string(tasks_entry.request_timer_seconds));
|
||||
insert_values.push_back(std::to_string(tasks_entry.lock_activity_id));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@ -377,6 +384,7 @@ public:
|
||||
insert_values.push_back("'" + Strings::Escape(tasks_entry.completion_emote) + "'");
|
||||
insert_values.push_back(std::to_string(tasks_entry.replay_timer_seconds));
|
||||
insert_values.push_back(std::to_string(tasks_entry.request_timer_seconds));
|
||||
insert_values.push_back(std::to_string(tasks_entry.lock_activity_id));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", insert_values) + ")");
|
||||
}
|
||||
@ -433,6 +441,7 @@ public:
|
||||
entry.completion_emote = row[20] ? row[20] : "";
|
||||
entry.replay_timer_seconds = atoi(row[21]);
|
||||
entry.request_timer_seconds = atoi(row[22]);
|
||||
entry.lock_activity_id = atoi(row[23]);
|
||||
|
||||
all_entries.push_back(entry);
|
||||
}
|
||||
@ -480,6 +489,7 @@ public:
|
||||
entry.completion_emote = row[20] ? row[20] : "";
|
||||
entry.replay_timer_seconds = atoi(row[21]);
|
||||
entry.request_timer_seconds = atoi(row[22]);
|
||||
entry.lock_activity_id = atoi(row[23]);
|
||||
|
||||
all_entries.push_back(entry);
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
#define ServerOP_SharedTaskPlayerList 0x0313 // zone -> world /taskplayerlist command
|
||||
#define ServerOP_SharedTaskMemberChange 0x0314 // world -> zone. Send shared task single member added/removed (client also handles message)
|
||||
#define ServerOP_SharedTaskKickPlayers 0x0315 // zone -> world /kickplayers task
|
||||
#define ServerOP_SharedTaskLock 0x0316 // zone -> world
|
||||
|
||||
enum class SharedTaskRequestGroupType {
|
||||
Solo = 0,
|
||||
@ -105,6 +106,8 @@ struct SharedTaskActivityStateEntry {
|
||||
uint32 max_done_count; // goalcount
|
||||
uint32 updated_time;
|
||||
uint32 completed_time;
|
||||
int step;
|
||||
bool optional;
|
||||
};
|
||||
|
||||
struct ServerSharedTaskActivityUpdate_Struct {
|
||||
@ -162,6 +165,12 @@ struct ServerSharedTaskKickPlayers_Struct {
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct ServerSharedTaskLock_Struct {
|
||||
uint32 source_character_id;
|
||||
uint32 task_id;
|
||||
bool lock;
|
||||
};
|
||||
|
||||
class SharedTask {
|
||||
public:
|
||||
// used in both zone and world validation
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9190
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9192
|
||||
|
||||
#ifdef BOTS
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9029
|
||||
|
||||
@ -445,6 +445,7 @@
|
||||
9189|2022_07_10_character_task_rewarded.sql|SHOW COLUMNS FROM `character_tasks` LIKE 'was_rewarded'|empty|
|
||||
9190|2022_07_13_task_reward_points.sql|SHOW COLUMNS FROM `tasks` LIKE 'reward_points'|empty|
|
||||
9191|2022_07_28_gm_state_changes.sql|SHOW COLUMNS FROM `account` LIKE 'invulnerable'|empty|
|
||||
9192|2022_07_13_task_lock_activity.sql|SHOW COLUMNS FROM `tasks` LIKE 'lock_activity_id'|empty|
|
||||
|
||||
# Upgrade conditions:
|
||||
# This won't be needed after this system is implemented, but it is used database that are not
|
||||
|
||||
2
utils/sql/git/required/2022_07_13_task_lock_activity.sql
Normal file
2
utils/sql/git/required/2022_07_13_task_lock_activity.sql
Normal file
@ -0,0 +1,2 @@
|
||||
ALTER TABLE `tasks`
|
||||
ADD COLUMN `lock_activity_id` INT NOT NULL DEFAULT '-1' AFTER `request_timer_seconds`;
|
||||
@ -14,6 +14,7 @@
|
||||
#include "../common/repositories/completed_shared_task_members_repository.h"
|
||||
#include "../common/repositories/completed_shared_task_activity_state_repository.h"
|
||||
#include "../common/repositories/shared_task_dynamic_zones_repository.h"
|
||||
#include <array>
|
||||
#include <ctime>
|
||||
|
||||
extern ClientList client_list;
|
||||
@ -153,6 +154,8 @@ void SharedTaskManager::AttemptSharedTaskCreation(
|
||||
e.activity_id = a.activityid;
|
||||
e.done_count = 0;
|
||||
e.max_done_count = a.goalcount;
|
||||
e.step = a.step;
|
||||
e.optional = a.optional;
|
||||
|
||||
shared_task_activity_state.emplace_back(e);
|
||||
}
|
||||
@ -182,7 +185,7 @@ void SharedTaskManager::AttemptSharedTaskCreation(
|
||||
new_shared_task.SetMembers(request_members);
|
||||
|
||||
// add to shared tasks list
|
||||
m_shared_tasks.emplace_back(new_shared_task);
|
||||
auto& inserted = m_shared_tasks.emplace_back(new_shared_task);
|
||||
|
||||
// send accept to members
|
||||
for (auto &m: request_members) {
|
||||
@ -197,6 +200,9 @@ void SharedTaskManager::AttemptSharedTaskCreation(
|
||||
}
|
||||
SendSharedTaskMemberListToAllMembers(&new_shared_task);
|
||||
|
||||
// check if task should immediately lock
|
||||
HandleCompletedActivities(&inserted);
|
||||
|
||||
LogTasks(
|
||||
"[AttemptSharedTaskCreation] shared_task_id [{}] created successfully | task_id [{}] member_count [{}] activity_count [{}] current tasks in state [{}]",
|
||||
new_shared_task.GetDbSharedTask().id,
|
||||
@ -395,6 +401,8 @@ void SharedTaskManager::LoadSharedTaskState()
|
||||
e.max_done_count = ad.goalcount;
|
||||
e.completed_time = sta.completed_time;
|
||||
e.updated_time = sta.updated_time;
|
||||
e.step = ad.step;
|
||||
e.optional = ad.optional;
|
||||
}
|
||||
}
|
||||
|
||||
@ -618,35 +626,10 @@ void SharedTaskManager::SharedTaskActivityUpdate(
|
||||
}
|
||||
}
|
||||
|
||||
// check if completed
|
||||
bool is_shared_task_completed = true;
|
||||
for (auto &a : shared_task->m_shared_task_activity_state) {
|
||||
if (a.done_count != a.max_done_count) {
|
||||
is_shared_task_completed = false;
|
||||
}
|
||||
}
|
||||
|
||||
// mark completed
|
||||
// handle task locking and completion
|
||||
bool is_shared_task_completed = HandleCompletedActivities(shared_task);
|
||||
if (is_shared_task_completed) {
|
||||
auto t = shared_task->GetDbSharedTask();
|
||||
if (t.id > 0) {
|
||||
LogTasksDetail(
|
||||
"[SharedTaskActivityUpdate] Marking shared task [{}] completed",
|
||||
shared_task->GetDbSharedTask().id
|
||||
);
|
||||
|
||||
// set record
|
||||
t.completion_time = std::time(nullptr);
|
||||
t.is_locked = true;
|
||||
// update database
|
||||
SharedTasksRepository::UpdateOne(*m_database, t);
|
||||
// update internally
|
||||
shared_task->SetDbSharedTask(t);
|
||||
// record completion
|
||||
RecordSharedTaskCompletion(shared_task);
|
||||
// replay timer lockouts
|
||||
AddReplayTimers(shared_task);
|
||||
}
|
||||
HandleCompletedTask(shared_task);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1815,3 +1798,75 @@ SharedTaskManager *SharedTaskManager::PurgeExpiredSharedTasks()
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
void SharedTaskManager::LockTask(SharedTask* s, bool lock)
|
||||
{
|
||||
bool is_locked = (s->GetDbSharedTask().is_locked != 0);
|
||||
if (is_locked != lock)
|
||||
{
|
||||
auto db_task = s->GetDbSharedTask();
|
||||
db_task.is_locked = lock;
|
||||
SharedTasksRepository::UpdateOne(*m_database, db_task);
|
||||
s->SetDbSharedTask(db_task);
|
||||
|
||||
if (lock)
|
||||
{
|
||||
SendLeaderMessageID(s, Chat::Yellow, SharedTaskMessage::YOUR_TASK_NOW_LOCKED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SharedTaskManager::HandleCompletedActivities(SharedTask* s)
|
||||
{
|
||||
bool is_task_complete = true;
|
||||
bool lock_task = false;
|
||||
|
||||
std::array<bool, MAXACTIVITIESPERTASK> completed_steps;
|
||||
completed_steps.fill(true);
|
||||
|
||||
// multiple activity ids may share a step, sort so previous step completions can be checked
|
||||
auto activity_states = s->GetActivityState();
|
||||
std::sort(activity_states.begin(), activity_states.end(),
|
||||
[](const auto& lhs, const auto& rhs) { return lhs.step < rhs.step; });
|
||||
|
||||
for (const auto& a : activity_states)
|
||||
{
|
||||
if (a.done_count != a.max_done_count && !a.optional)
|
||||
{
|
||||
is_task_complete = false;
|
||||
if (a.step > 0 && a.step <= MAXACTIVITIESPERTASK)
|
||||
{
|
||||
completed_steps[a.step - 1] = false;
|
||||
}
|
||||
}
|
||||
|
||||
int lock_index = s->GetTaskData().lock_activity_id;
|
||||
if (a.activity_id == lock_index && a.step > 0 && a.step <= MAXACTIVITIESPERTASK)
|
||||
{
|
||||
// lock if element is active (on first step or previous step completed)
|
||||
lock_task = (a.step == 1 || completed_steps[a.step - 2]);
|
||||
}
|
||||
}
|
||||
|
||||
// completion locks are silent
|
||||
if (!is_task_complete && lock_task)
|
||||
{
|
||||
LockTask(s, true);
|
||||
}
|
||||
|
||||
return is_task_complete;
|
||||
}
|
||||
|
||||
void SharedTaskManager::HandleCompletedTask(SharedTask* s)
|
||||
{
|
||||
auto db_task = s->GetDbSharedTask();
|
||||
LogTasksDetail("[HandleCompletedTask] Marking shared task [{}] completed", db_task.id);
|
||||
db_task.completion_time = std::time(nullptr);
|
||||
db_task.is_locked = true;
|
||||
SharedTasksRepository::UpdateOne(*m_database, db_task);
|
||||
s->SetDbSharedTask(db_task);
|
||||
|
||||
RecordSharedTaskCompletion(s);
|
||||
|
||||
AddReplayTimers(s);
|
||||
}
|
||||
|
||||
@ -58,6 +58,7 @@ public:
|
||||
void SaveSharedTaskActivityState(int64 shared_task_id, std::vector<SharedTaskActivityStateEntry> activity_state);
|
||||
|
||||
bool IsSharedTaskLeader(SharedTask *s, uint32 character_id);
|
||||
void LockTask(SharedTask* s, bool lock);
|
||||
void SendAcceptNewSharedTaskPacket(uint32 character_id, uint32 task_id, uint32_t npc_context_id, int accept_time);
|
||||
void SendRemovePlayerFromSharedTaskPacket(uint32 character_id, uint32 task_id, bool remove_from_db);
|
||||
void SendSharedTaskMemberList(uint32 character_id, const std::vector<SharedTaskMember> &members);
|
||||
@ -117,6 +118,8 @@ protected:
|
||||
bool CanAddPlayer(SharedTask *s, uint32_t character_id, std::string player_name, bool accepted);
|
||||
bool CanRequestSharedTask(uint32_t task_id, uint32_t character_id, const SharedTaskRequestCharacters &request);
|
||||
void ChooseNewLeader(SharedTask *s);
|
||||
bool HandleCompletedActivities(SharedTask* s);
|
||||
void HandleCompletedTask(SharedTask* s);
|
||||
void SendSharedTaskMemberListToAllMembers(SharedTask *s);
|
||||
void SendSharedTaskMemberAddedToAllMembers(SharedTask *s, const std::string &player_name);
|
||||
void SendSharedTaskMemberRemovedToAllMembers(SharedTask *s, const std::string &player_name);
|
||||
|
||||
@ -349,6 +349,15 @@ void SharedTaskWorldMessaging::HandleZoneMessage(ServerPacket *pack)
|
||||
|
||||
break;
|
||||
}
|
||||
case ServerOP_SharedTaskLock: {
|
||||
auto buf = reinterpret_cast<ServerSharedTaskLock_Struct*>(pack->pBuffer);
|
||||
auto shared_task = shared_task_manager.FindSharedTaskByTaskIdAndCharacterId(buf->task_id, buf->source_character_id);
|
||||
if (shared_task)
|
||||
{
|
||||
shared_task_manager.LockTask(shared_task, buf->lock);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1394,6 +1394,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
case ServerOP_SharedTaskCreateDynamicZone:
|
||||
case ServerOP_SharedTaskPurgeAllCommand:
|
||||
case ServerOP_SharedTaskPlayerList:
|
||||
case ServerOP_SharedTaskLock:
|
||||
case ServerOP_SharedTaskKickPlayers: {
|
||||
SharedTaskWorldMessaging::HandleZoneMessage(pack);
|
||||
break;
|
||||
|
||||
@ -1302,6 +1302,7 @@ public:
|
||||
return (task_state ? task_state->CompletedTasksInSet(task_set_id) : 0);
|
||||
}
|
||||
void PurgeTaskTimers();
|
||||
void LockSharedTask(bool lock) { if (task_state) { task_state->LockSharedTask(this, lock); } }
|
||||
|
||||
// shared task shims / middleware
|
||||
// these variables are used as a shim to intercept normal localized task functionality
|
||||
|
||||
@ -1430,6 +1430,11 @@ bool Lua_Client::IsTaskActivityActive(int task, int activity) {
|
||||
return self->IsTaskActivityActive(task, activity);
|
||||
}
|
||||
|
||||
void Lua_Client::LockSharedTask(bool lock) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->LockSharedTask(lock);
|
||||
}
|
||||
|
||||
int Lua_Client::GetCorpseCount() {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetCorpseCount();
|
||||
@ -2772,6 +2777,7 @@ luabind::scope lua_register_client() {
|
||||
.def("LeaveGroup", (void(Lua_Client::*)(void))&Lua_Client::LeaveGroup)
|
||||
.def("LoadPEQZoneFlags", (void(Lua_Client::*)(void))&Lua_Client::LoadPEQZoneFlags)
|
||||
.def("LoadZoneFlags", (void(Lua_Client::*)(void))&Lua_Client::LoadZoneFlags)
|
||||
.def("LockSharedTask", &Lua_Client::LockSharedTask)
|
||||
.def("MarkSingleCompassLoc", (void(Lua_Client::*)(float,float,float))&Lua_Client::MarkSingleCompassLoc)
|
||||
.def("MarkSingleCompassLoc", (void(Lua_Client::*)(float,float,float,int))&Lua_Client::MarkSingleCompassLoc)
|
||||
.def("MaxSkill", (int(Lua_Client::*)(int))&Lua_Client::MaxSkill)
|
||||
|
||||
@ -333,6 +333,7 @@ public:
|
||||
bool IsTaskCompleted(int task);
|
||||
bool IsTaskActive(int task);
|
||||
bool IsTaskActivityActive(int task, int activity);
|
||||
void LockSharedTask(bool lock);
|
||||
int GetCorpseCount();
|
||||
int GetCorpseID(int corpse);
|
||||
int GetCorpseItemAt(int corpse, int slot);
|
||||
|
||||
@ -1408,6 +1408,11 @@ bool Perl_Client_IsTaskActivityActive(Client* self, int task_id, int activity_id
|
||||
return self->IsTaskActivityActive(task_id, activity_id);
|
||||
}
|
||||
|
||||
void Perl_Client_LockSharedTask(Client* self, bool lock)
|
||||
{
|
||||
return self->LockSharedTask(lock);
|
||||
}
|
||||
|
||||
uint32_t Perl_Client_GetCorpseCount(Client* self) // @categories Account and Character, Corpse
|
||||
{
|
||||
return self->GetCorpseCount();
|
||||
@ -2621,6 +2626,7 @@ void perl_register_client()
|
||||
package.add("LeaveGroup", &Perl_Client_LeaveGroup);
|
||||
package.add("LoadPEQZoneFlags", &Perl_Client_LoadPEQZoneFlags);
|
||||
package.add("LoadZoneFlags", &Perl_Client_LoadZoneFlags);
|
||||
package.add("LockSharedTask", &Perl_Client_LockSharedTask);
|
||||
package.add("MarkCompassLoc", &Perl_Client_MarkCompassLoc);
|
||||
package.add("MaxSkill", (int(*)(Client*, uint16))&Perl_Client_MaxSkill);
|
||||
package.add("MaxSkill", (int(*)(Client*, uint16, uint16))&Perl_Client_MaxSkill);
|
||||
|
||||
@ -2727,3 +2727,17 @@ bool ClientTaskState::HasActiveTasks()
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClientTaskState::LockSharedTask(Client* client, bool lock)
|
||||
{
|
||||
if (m_active_shared_task.task_id != TASKSLOTEMPTY)
|
||||
{
|
||||
ServerPacket pack(ServerOP_SharedTaskLock, sizeof(ServerSharedTaskLock_Struct));
|
||||
auto buf = reinterpret_cast<ServerSharedTaskLock_Struct*>(pack.pBuffer);
|
||||
buf->source_character_id = client->CharacterID();
|
||||
buf->task_id = m_active_shared_task.task_id;
|
||||
buf->lock = lock;
|
||||
|
||||
worldserver.SendPacket(&pack);
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,6 +56,7 @@ public:
|
||||
void CreateTaskDynamicZone(Client* client, int task_id, DynamicZone& dz);
|
||||
void ListTaskTimers(Client* client);
|
||||
void KickPlayersSharedTask(Client* client);
|
||||
void LockSharedTask(Client* client, bool lock);
|
||||
|
||||
inline bool HasFreeTaskSlot() { return m_active_task.task_id == TASKSLOTEMPTY; }
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user