[Shared Tasks] Implement task timer groups (#2340)

This adds task replay and request timer groups (an arbitrary id) which
allows for different tasks to share lockouts
This commit is contained in:
hg 2022-07-30 21:18:19 -04:00 committed by GitHub
parent f64d072af7
commit 8a962e09f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 116 additions and 35 deletions

View File

@ -23,6 +23,7 @@ public:
int character_id;
int task_id;
int timer_type;
int timer_group;
time_t expire_time;
};
@ -38,6 +39,7 @@ public:
"character_id",
"task_id",
"timer_type",
"timer_group",
"expire_time",
};
}
@ -49,6 +51,7 @@ public:
"character_id",
"task_id",
"timer_type",
"timer_group",
"UNIX_TIMESTAMP(expire_time)",
};
}
@ -94,6 +97,7 @@ public:
entry.character_id = 0;
entry.task_id = 0;
entry.timer_type = 0;
entry.timer_group = 0;
entry.expire_time = std::time(nullptr);
return entry;
@ -134,7 +138,8 @@ public:
entry.character_id = atoi(row[1]);
entry.task_id = atoi(row[2]);
entry.timer_type = atoi(row[3]);
entry.expire_time = strtoll(row[4] ? row[4] : "-1", nullptr, 10);
entry.timer_group = atoi(row[4]);
entry.expire_time = strtoll(row[5] ? row[5] : "-1", nullptr, 10);
return entry;
}
@ -171,7 +176,8 @@ public:
update_values.push_back(columns[1] + " = " + std::to_string(character_task_timers_entry.character_id));
update_values.push_back(columns[2] + " = " + std::to_string(character_task_timers_entry.task_id));
update_values.push_back(columns[3] + " = " + std::to_string(character_task_timers_entry.timer_type));
update_values.push_back(columns[4] + " = FROM_UNIXTIME(" + (character_task_timers_entry.expire_time > 0 ? std::to_string(character_task_timers_entry.expire_time) : "null") + ")");
update_values.push_back(columns[4] + " = " + std::to_string(character_task_timers_entry.timer_group));
update_values.push_back(columns[5] + " = FROM_UNIXTIME(" + (character_task_timers_entry.expire_time > 0 ? std::to_string(character_task_timers_entry.expire_time) : "null") + ")");
auto results = db.QueryDatabase(
fmt::format(
@ -197,6 +203,7 @@ public:
insert_values.push_back(std::to_string(character_task_timers_entry.character_id));
insert_values.push_back(std::to_string(character_task_timers_entry.task_id));
insert_values.push_back(std::to_string(character_task_timers_entry.timer_type));
insert_values.push_back(std::to_string(character_task_timers_entry.timer_group));
insert_values.push_back("FROM_UNIXTIME(" + (character_task_timers_entry.expire_time > 0 ? std::to_string(character_task_timers_entry.expire_time) : "null") + ")");
auto results = db.QueryDatabase(
@ -231,6 +238,7 @@ public:
insert_values.push_back(std::to_string(character_task_timers_entry.character_id));
insert_values.push_back(std::to_string(character_task_timers_entry.task_id));
insert_values.push_back(std::to_string(character_task_timers_entry.timer_type));
insert_values.push_back(std::to_string(character_task_timers_entry.timer_group));
insert_values.push_back("FROM_UNIXTIME(" + (character_task_timers_entry.expire_time > 0 ? std::to_string(character_task_timers_entry.expire_time) : "null") + ")");
insert_chunks.push_back("(" + Strings::Implode(",", insert_values) + ")");
@ -269,7 +277,8 @@ public:
entry.character_id = atoi(row[1]);
entry.task_id = atoi(row[2]);
entry.timer_type = atoi(row[3]);
entry.expire_time = strtoll(row[4] ? row[4] : "-1", nullptr, 10);
entry.timer_group = atoi(row[4]);
entry.expire_time = strtoll(row[5] ? row[5] : "-1", nullptr, 10);
all_entries.push_back(entry);
}
@ -298,7 +307,8 @@ public:
entry.character_id = atoi(row[1]);
entry.task_id = atoi(row[2]);
entry.timer_type = atoi(row[3]);
entry.expire_time = strtoll(row[4] ? row[4] : "-1", nullptr, 10);
entry.timer_group = atoi(row[4]);
entry.expire_time = strtoll(row[5] ? row[5] : "-1", nullptr, 10);
all_entries.push_back(entry);
}

View File

@ -40,7 +40,9 @@ public:
int repeatable;
int faction_reward;
std::string completion_emote;
int replay_timer_group;
int replay_timer_seconds;
int request_timer_group;
int request_timer_seconds;
int lock_activity_id;
};
@ -74,7 +76,9 @@ public:
"repeatable",
"faction_reward",
"completion_emote",
"replay_timer_group",
"replay_timer_seconds",
"request_timer_group",
"request_timer_seconds",
"lock_activity_id",
};
@ -104,7 +108,9 @@ public:
"repeatable",
"faction_reward",
"completion_emote",
"replay_timer_group",
"replay_timer_seconds",
"request_timer_group",
"request_timer_seconds",
"lock_activity_id",
};
@ -168,7 +174,9 @@ public:
entry.repeatable = 1;
entry.faction_reward = 0;
entry.completion_emote = "";
entry.replay_timer_group = 0;
entry.replay_timer_seconds = 0;
entry.request_timer_group = 0;
entry.request_timer_seconds = 0;
entry.lock_activity_id = -1;
@ -227,9 +235,11 @@ public:
entry.repeatable = atoi(row[18]);
entry.faction_reward = atoi(row[19]);
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]);
entry.replay_timer_group = atoi(row[21]);
entry.replay_timer_seconds = atoi(row[22]);
entry.request_timer_group = atoi(row[23]);
entry.request_timer_seconds = atoi(row[24]);
entry.lock_activity_id = atoi(row[25]);
return entry;
}
@ -284,9 +294,11 @@ public:
update_values.push_back(columns[18] + " = " + std::to_string(tasks_entry.repeatable));
update_values.push_back(columns[19] + " = " + std::to_string(tasks_entry.faction_reward));
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));
update_values.push_back(columns[21] + " = " + std::to_string(tasks_entry.replay_timer_group));
update_values.push_back(columns[22] + " = " + std::to_string(tasks_entry.replay_timer_seconds));
update_values.push_back(columns[23] + " = " + std::to_string(tasks_entry.request_timer_group));
update_values.push_back(columns[24] + " = " + std::to_string(tasks_entry.request_timer_seconds));
update_values.push_back(columns[25] + " = " + std::to_string(tasks_entry.lock_activity_id));
auto results = db.QueryDatabase(
fmt::format(
@ -329,7 +341,9 @@ public:
insert_values.push_back(std::to_string(tasks_entry.repeatable));
insert_values.push_back(std::to_string(tasks_entry.faction_reward));
insert_values.push_back("'" + Strings::Escape(tasks_entry.completion_emote) + "'");
insert_values.push_back(std::to_string(tasks_entry.replay_timer_group));
insert_values.push_back(std::to_string(tasks_entry.replay_timer_seconds));
insert_values.push_back(std::to_string(tasks_entry.request_timer_group));
insert_values.push_back(std::to_string(tasks_entry.request_timer_seconds));
insert_values.push_back(std::to_string(tasks_entry.lock_activity_id));
@ -382,7 +396,9 @@ public:
insert_values.push_back(std::to_string(tasks_entry.repeatable));
insert_values.push_back(std::to_string(tasks_entry.faction_reward));
insert_values.push_back("'" + Strings::Escape(tasks_entry.completion_emote) + "'");
insert_values.push_back(std::to_string(tasks_entry.replay_timer_group));
insert_values.push_back(std::to_string(tasks_entry.replay_timer_seconds));
insert_values.push_back(std::to_string(tasks_entry.request_timer_group));
insert_values.push_back(std::to_string(tasks_entry.request_timer_seconds));
insert_values.push_back(std::to_string(tasks_entry.lock_activity_id));
@ -439,9 +455,11 @@ public:
entry.repeatable = atoi(row[18]);
entry.faction_reward = atoi(row[19]);
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]);
entry.replay_timer_group = atoi(row[21]);
entry.replay_timer_seconds = atoi(row[22]);
entry.request_timer_group = atoi(row[23]);
entry.request_timer_seconds = atoi(row[24]);
entry.lock_activity_id = atoi(row[25]);
all_entries.push_back(entry);
}
@ -487,9 +505,11 @@ public:
entry.repeatable = atoi(row[18]);
entry.faction_reward = atoi(row[19]);
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]);
entry.replay_timer_group = atoi(row[21]);
entry.replay_timer_seconds = atoi(row[22]);
entry.request_timer_group = atoi(row[23]);
entry.request_timer_seconds = atoi(row[24]);
entry.lock_activity_id = atoi(row[25]);
all_entries.push_back(entry);
}

View File

@ -223,7 +223,9 @@ struct TaskInformation {
int min_players;
int max_players;
bool repeatable{};
int replay_timer_group;
int replay_timer_seconds;
int request_timer_group;
int request_timer_seconds;
ActivityInformation activity_information[MAXACTIVITIESPERTASK];

View File

@ -34,7 +34,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9192
#define CURRENT_BINARY_DATABASE_VERSION 9193
#ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9029

View File

@ -446,6 +446,7 @@
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|
9193|2022_07_16_task_timer_groups.sql|SHOW COLUMNS FROM `tasks` LIKE 'replay_timer_group'|empty|
# Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1,6 @@
ALTER TABLE `tasks`
ADD COLUMN `replay_timer_group` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `completion_emote`,
ADD COLUMN `request_timer_group` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `replay_timer_seconds`;
ALTER TABLE `character_task_timers`
ADD COLUMN `timer_group` INT NOT NULL DEFAULT '0' AFTER `timer_type`;

View File

@ -132,6 +132,7 @@ void SharedTaskManager::AttemptSharedTaskCreation(
timer.character_id = m.character_id;
timer.task_id = task.id;
timer.timer_type = static_cast<int>(TaskTimerType::Request);
timer.timer_group = task.request_timer_group;
timer.expire_time = shared_task_entity.accepted_time + task.request_timer_seconds;
task_timers.emplace_back(timer);
@ -1090,6 +1091,7 @@ void SharedTaskManager::AddPlayerByCharacterIdAndName(
timer.character_id = character_id;
timer.task_id = s->GetDbSharedTask().task_id;
timer.timer_type = static_cast<int>(TaskTimerType::Request);
timer.timer_group = s->GetTaskData().request_timer_group;
timer.expire_time = expire_time;
CharacterTaskTimersRepository::InsertOne(*m_database, timer);
@ -1300,6 +1302,29 @@ void SharedTaskManager::SendMembersMessageID(
}
}
std::vector<CharacterTaskTimersRepository::CharacterTaskTimers> SharedTaskManager::GetCharacterTimers(
const std::vector<uint32_t>& character_ids, const TasksRepository::Tasks& task)
{
// todo: consider caching character timers in world and zone to avoid queries
auto task_timers = CharacterTaskTimersRepository::GetWhere(*m_database, fmt::format(
SQL(
character_id IN ({})
AND (task_id = {}
OR (timer_group > 0 AND timer_type = {} AND timer_group = {})
OR (timer_group > 0 AND timer_type = {} AND timer_group = {}))
AND expire_time > NOW() ORDER BY timer_type ASC LIMIT 1
),
fmt::join(character_ids, ","),
task.id,
static_cast<int>(TaskTimerType::Replay),
task.replay_timer_group,
static_cast<int>(TaskTimerType::Request),
task.request_timer_group
));
return task_timers;
}
bool SharedTaskManager::CanRequestSharedTask(
uint32_t task_id,
uint32_t character_id,
@ -1395,12 +1420,7 @@ bool SharedTaskManager::CanRequestSharedTask(
}
// check if any party members have a replay or request timer for the task (limit 1, replay checked first)
auto character_task_timers = CharacterTaskTimersRepository::GetWhere(
*m_database, fmt::format(
"character_id IN ({}) AND task_id = {} AND expire_time > NOW() ORDER BY timer_type ASC LIMIT 1",
fmt::join(request.character_ids, ","), task_id
)
);
auto character_task_timers = GetCharacterTimers(request.character_ids, task);
if (!character_task_timers.empty()) {
auto timer_type = static_cast<TaskTimerType>(character_task_timers.front().timer_type);
@ -1523,11 +1543,7 @@ bool SharedTaskManager::CanAddPlayer(SharedTask *s, uint32_t character_id, std::
// check if player has a replay or request timer lockout
// todo: live allows characters with a request timer to be re-invited if they quit, but only until they zone? (investigate/edge case)
auto task_timers = CharacterTaskTimersRepository::GetWhere(
*m_database, fmt::format(
"character_id = {} AND task_id = {} AND expire_time > NOW() ORDER BY timer_type ASC LIMIT 1",
character_id, s->GetDbSharedTask().task_id
));
auto task_timers = GetCharacterTimers({ character_id }, s->GetTaskData());
if (!task_timers.empty()) {
auto timer_type = static_cast<TaskTimerType>(task_timers.front().timer_type);
@ -1679,6 +1695,7 @@ void SharedTaskManager::AddReplayTimers(SharedTask *s)
timer.character_id = member_id;
timer.task_id = s->GetTaskData().id;
timer.timer_type = static_cast<int>(TaskTimerType::Replay);
timer.timer_group = s->GetTaskData().replay_timer_group;
timer.expire_time = expire_time;
task_timers.emplace_back(timer);
@ -1701,8 +1718,9 @@ void SharedTaskManager::AddReplayTimers(SharedTask *s)
// this can occur if a player has a timer for being a past member of
// a shared task but joined another before the first was completed
CharacterTaskTimersRepository::DeleteWhere(*m_database, fmt::format(
"task_id = {} AND timer_type = {} AND character_id IN ({})",
"(task_id = {} OR (timer_group > 0 AND timer_group = {})) AND timer_type = {} AND character_id IN ({})",
s->GetTaskData().id,
s->GetTaskData().replay_timer_group,
static_cast<int>(TaskTimerType::Replay),
fmt::join(s->member_id_history, ",")
));

View File

@ -3,6 +3,7 @@
#include "../common/database.h"
#include "../common/shared_tasks.h"
#include "../common/repositories/character_task_timers_repository.h"
class DynamicZone;
@ -114,6 +115,9 @@ protected:
// store a reference of active invitations that have been sent to players
std::vector<SharedTaskActiveInvitation> m_active_invitations{};
std::vector<CharacterTaskTimersRepository::CharacterTaskTimers> GetCharacterTimers(
const std::vector<uint32_t>& character_ids, const TasksRepository::Tasks& task);
void AddReplayTimers(SharedTask *s);
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);

View File

@ -2327,8 +2327,19 @@ void ClientTaskState::AcceptNewTask(
if (task->type != TaskType::Shared)
{
auto task_timers = CharacterTaskTimersRepository::GetWhere(database, fmt::format(
"character_id = {} AND task_id = {} AND expire_time > NOW() ORDER BY timer_type ASC LIMIT 1",
client->CharacterID(), task_id
SQL(
character_id = {}
AND (task_id = {}
OR (timer_group > 0 AND timer_type = {} AND timer_group = {})
OR (timer_group > 0 AND timer_type = {} AND timer_group = {}))
AND expire_time > NOW() ORDER BY timer_type ASC LIMIT 1
),
client->CharacterID(),
task_id,
static_cast<int>(TaskTimerType::Replay),
task->replay_timer_group,
static_cast<int>(TaskTimerType::Request),
task->request_timer_group
));
if (!task_timers.empty())
@ -2426,6 +2437,7 @@ void ClientTaskState::AcceptNewTask(
timer.character_id = client->CharacterID();
timer.task_id = task_id;
timer.timer_type = static_cast<int>(TaskTimerType::Request);
timer.timer_group = task->request_timer_group;
timer.expire_time = expire_time;
CharacterTaskTimersRepository::InsertOne(database, timer);
@ -2639,13 +2651,17 @@ void ClientTaskState::AddReplayTimer(Client* client, ClientTaskInformation& clie
auto timer = CharacterTaskTimersRepository::NewEntity();
timer.character_id = client->CharacterID();
timer.task_id = client_task.task_id;
timer.expire_time = expire_time;
timer.timer_type = static_cast<int>(TaskTimerType::Replay);
timer.timer_group = task.replay_timer_group;
timer.expire_time = expire_time;
// replace any existing replay timer
CharacterTaskTimersRepository::DeleteWhere(database, fmt::format(
"task_id = {} AND timer_type = {} AND character_id = {}",
client_task.task_id, static_cast<int>(TaskTimerType::Replay), client->CharacterID()));
"(task_id = {} OR (timer_group > 0 AND timer_group = {})) AND timer_type = {} AND character_id = {}",
client_task.task_id,
task.replay_timer_group,
static_cast<int>(TaskTimerType::Replay),
client->CharacterID()));
CharacterTaskTimersRepository::InsertOne(database, timer);

View File

@ -115,7 +115,9 @@ bool TaskManager::LoadTasks(int single_task)
m_task_data[task_id]->max_players = task.max_players;
m_task_data[task_id]->repeatable = task.repeatable;
m_task_data[task_id]->completion_emote = task.completion_emote;
m_task_data[task_id]->replay_timer_group = task.replay_timer_group;
m_task_data[task_id]->replay_timer_seconds = task.replay_timer_seconds;
m_task_data[task_id]->request_timer_group = task.request_timer_group;
m_task_data[task_id]->request_timer_seconds = task.request_timer_seconds;
m_task_data[task_id]->activity_count = 0;
m_task_data[task_id]->sequence_mode = ActivitiesSequential;
@ -125,7 +127,7 @@ bool TaskManager::LoadTasks(int single_task)
"[LoadTasks] (Task) task_id [{}] type [{}] () duration [{}] duration_code [{}] title [{}] description [{}] "
" reward [{}] rewardid [{}] cashreward [{}] xpreward [{}] rewardmethod [{}] faction_reward [{}] minlevel [{}] "
" maxlevel [{}] level_spread [{}] min_players [{}] max_players [{}] repeatable [{}] completion_emote [{}]",
" replay_timer_seconds [{}] request_timer_seconds [{}]",
" replay_group [{}] replay_timer_seconds [{}] request_group [{}] request_timer_seconds [{}]",
task.id,
task.type,
task.duration,
@ -145,7 +147,9 @@ bool TaskManager::LoadTasks(int single_task)
task.max_players,
task.repeatable,
task.completion_emote,
task.replay_timer_group,
task.replay_timer_seconds,
task.request_timer_group,
task.request_timer_seconds
);
}