From 0e89e09de224b85d91a036dd12470188594171b0 Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Wed, 30 Dec 2020 22:58:22 +0700 Subject: [PATCH 01/19] feat(Core/QuestTracker): implement queue quest tracker --- src/common/Utilities/TaskScheduler.cpp | 230 ++++++++ src/common/Utilities/TaskScheduler.h | 647 +++++++++++++++++++++ src/server/game/Entities/Player/Player.cpp | 17 +- src/server/game/Handlers/QuestHandler.cpp | 9 +- src/server/game/Quests/QuestTracker.cpp | 117 ++++ src/server/game/Quests/QuestTracker.h | 40 ++ src/server/game/World/World.cpp | 6 + src/server/scripts/Commands/cs_quest.cpp | 8 +- 8 files changed, 1048 insertions(+), 26 deletions(-) create mode 100644 src/common/Utilities/TaskScheduler.cpp create mode 100644 src/common/Utilities/TaskScheduler.h create mode 100644 src/server/game/Quests/QuestTracker.cpp create mode 100644 src/server/game/Quests/QuestTracker.h diff --git a/src/common/Utilities/TaskScheduler.cpp b/src/common/Utilities/TaskScheduler.cpp new file mode 100644 index 00000000000000..8739d8e6b7d1dc --- /dev/null +++ b/src/common/Utilities/TaskScheduler.cpp @@ -0,0 +1,230 @@ +/* + * This file is part of the WarheadCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "TaskScheduler.h" +#include "Errors.h" + +TaskScheduler& TaskScheduler::ClearValidator() +{ + _predicate = EmptyValidator; + return *this; +} + +TaskScheduler& TaskScheduler::Update(success_t const& callback) +{ + _now = clock_t::now(); + Dispatch(callback); + return *this; +} + +TaskScheduler& TaskScheduler::Update(size_t const milliseconds, success_t const& callback) +{ + return Update(std::chrono::milliseconds(milliseconds), callback); +} + +TaskScheduler& TaskScheduler::Async(std::function const& callable) +{ + _asyncHolder.push(callable); + return *this; +} + +TaskScheduler& TaskScheduler::CancelAll() +{ + /// Clear the task holder + _task_holder.Clear(); + _asyncHolder = AsyncHolder(); + return *this; +} + +TaskScheduler& TaskScheduler::CancelGroup(group_t const group) +{ + _task_holder.RemoveIf([group](TaskContainer const & task) -> bool + { + return task->IsInGroup(group); + }); + return *this; +} + +TaskScheduler& TaskScheduler::CancelGroupsOf(std::vector const& groups) +{ + std::for_each(groups.begin(), groups.end(), + std::bind(&TaskScheduler::CancelGroup, this, std::placeholders::_1)); + + return *this; +} + +TaskScheduler& TaskScheduler::InsertTask(TaskContainer task) +{ + _task_holder.Push(std::move(task)); + return *this; +} + +void TaskScheduler::Dispatch(success_t const& callback) +{ + // If the validation failed abort the dispatching here. + if (!_predicate()) + return; + + // Process all asyncs + while (!_asyncHolder.empty()) + { + _asyncHolder.front()(); + _asyncHolder.pop(); + + // If the validation failed abort the dispatching here. + if (!_predicate()) + return; + } + + while (!_task_holder.IsEmpty()) + { + if (_task_holder.First()->_end > _now) + break; + + // Perfect forward the context to the handler + // Use weak references to catch destruction before callbacks. + TaskContext context(_task_holder.Pop(), std::weak_ptr(self_reference)); + + // Invoke the context + context.Invoke(); + + // If the validation failed abort the dispatching here. + if (!_predicate()) + return; + } + + // On finish call the final callback + callback(); +} + +void TaskScheduler::TaskQueue::Push(TaskContainer&& task) +{ + container.insert(task); +} + +auto TaskScheduler::TaskQueue::Pop() -> TaskContainer +{ + TaskContainer result = *container.begin(); + container.erase(container.begin()); + return result; +} + +auto TaskScheduler::TaskQueue::First() const -> TaskContainer const& +{ + return *container.begin(); +} + +void TaskScheduler::TaskQueue::Clear() +{ + container.clear(); +} + +void TaskScheduler::TaskQueue::RemoveIf(std::function const& filter) +{ + for (auto itr = container.begin(); itr != container.end();) + if (filter(*itr)) + itr = container.erase(itr); + else + ++itr; +} + +void TaskScheduler::TaskQueue::ModifyIf(std::function const& filter) +{ + std::vector cache; + for (auto itr = container.begin(); itr != container.end();) + if (filter(*itr)) + { + cache.push_back(*itr); + itr = container.erase(itr); + } + else + ++itr; + + container.insert(cache.begin(), cache.end()); +} + +bool TaskScheduler::TaskQueue::IsEmpty() const +{ + return container.empty(); +} + +TaskContext& TaskContext::Dispatch(std::function const& apply) +{ + if (auto const owner = _owner.lock()) + apply(*owner); + + return *this; +} + +bool TaskContext::IsExpired() const +{ + return _owner.expired(); +} + +bool TaskContext::IsInGroup(TaskScheduler::group_t const group) const +{ + return _task->IsInGroup(group); +} + +TaskContext& TaskContext::SetGroup(TaskScheduler::group_t const group) +{ + _task->_group = group; + return *this; +} + +TaskContext& TaskContext::ClearGroup() +{ + _task->_group = std::nullopt; + return *this; +} + +TaskScheduler::repeated_t TaskContext::GetRepeatCounter() const +{ + return _task->_repeated; +} + +TaskContext& TaskContext::Async(std::function const& callable) +{ + return Dispatch(std::bind(&TaskScheduler::Async, std::placeholders::_1, callable)); +} + +TaskContext& TaskContext::CancelAll() +{ + return Dispatch(std::mem_fn(&TaskScheduler::CancelAll)); +} + +TaskContext& TaskContext::CancelGroup(TaskScheduler::group_t const group) +{ + return Dispatch(std::bind(&TaskScheduler::CancelGroup, std::placeholders::_1, group)); +} + +TaskContext& TaskContext::CancelGroupsOf(std::vector const& groups) +{ + return Dispatch(std::bind(&TaskScheduler::CancelGroupsOf, std::placeholders::_1, std::cref(groups))); +} + +void TaskContext::AssertOnConsumed() const +{ + // This was adapted to TC to prevent static analysis tools from complaining. + // If you encounter this assertion check if you repeat a TaskContext more then 1 time! + ASSERT(!(*_consumed) && "Bad task logic, task context was consumed already!"); +} + +void TaskContext::Invoke() +{ + _task->_task(*this); +} diff --git a/src/common/Utilities/TaskScheduler.h b/src/common/Utilities/TaskScheduler.h new file mode 100644 index 00000000000000..0c1b1764ecdc37 --- /dev/null +++ b/src/common/Utilities/TaskScheduler.h @@ -0,0 +1,647 @@ +/* + * This file is part of the WarheadCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef _TASK_SCHEDULER_H_ +#define _TASK_SCHEDULER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Util.h" + +class TaskContext; + +/// The TaskScheduler class provides the ability to schedule std::function's in the near future. +/// Use TaskScheduler::Update to update the scheduler. +/// Popular methods are: +/// * Schedule (Schedules a std::function which will be executed in the near future). +/// * Schedules an asynchronous function which will be executed at the next update tick. +/// * Cancel, Delay & Reschedule (Methods to manipulate already scheduled tasks). +/// Tasks are organized in groups (uint), multiple tasks can have the same group id, +/// you can provide a group or not, but keep in mind that you can only manipulate specific tasks through its group id! +/// Tasks callbacks use the function signature void(TaskContext) where TaskContext provides +/// access to the function schedule plan which makes it possible to repeat the task +/// with the same duration or a new one. +/// It also provides access to the repeat counter which is useful for task that repeat itself often +/// but behave different every time (spoken event dialogs for example). +class TaskScheduler +{ + friend class TaskContext; + + // Time definitions (use steady clock) + typedef std::chrono::steady_clock clock_t; + typedef clock_t::time_point timepoint_t; + typedef clock_t::duration duration_t; + + // Task group type + typedef uint32 group_t; + // Task repeated type + typedef uint32 repeated_t; + // Task handle type + typedef std::function task_handler_t; + // Predicate type + typedef std::function predicate_t; + // Success handle type + typedef std::function success_t; + + class Task + { + friend class TaskContext; + friend class TaskScheduler; + + timepoint_t _end; + duration_t _duration; + std::optional _group; + repeated_t _repeated; + task_handler_t _task; + + public: + // All Argument construct + Task(timepoint_t const& end, duration_t const& duration, std::optional const& group, + repeated_t const repeated, task_handler_t const& task) + : _end(end), _duration(duration), _group(group), _repeated(repeated), _task(task) { } + + // Minimal Argument construct + Task(timepoint_t const& end, duration_t const& duration, task_handler_t const& task) + : _end(end), _duration(duration), _group(std::nullopt), _repeated(0), _task(task) { } + + // Copy construct + Task(Task const&) = delete; + // Move construct + Task(Task&&) = delete; + // Copy Assign + Task& operator= (Task const&) = default; + // Move Assign + Task& operator= (Task&& right) = delete; + + // Order tasks by its end + inline bool operator< (Task const& other) const + { + return _end < other._end; + } + + inline bool operator> (Task const& other) const + { + return _end > other._end; + } + + // Compare tasks with its end + inline bool operator== (Task const& other) + { + return _end == other._end; + } + + // Returns true if the task is in the given group + inline bool IsInGroup(group_t const group) const + { + return _group == group; + } + }; + + typedef std::shared_ptr TaskContainer; + + /// Container which provides Task order, insert and reschedule operations. + struct Compare + { + bool operator() (TaskContainer const& left, TaskContainer const& right) const + { + return (*left.get()) < (*right.get()); + }; + }; + + class TaskQueue + { + std::multiset container; + + public: + // Pushes the task in the container + void Push(TaskContainer&& task); + + /// Pops the task out of the container + TaskContainer Pop(); + + TaskContainer const& First() const; + + void Clear(); + + void RemoveIf(std::function const& filter); + + void ModifyIf(std::function const& filter); + + bool IsEmpty() const; + }; + + /// Contains a self reference to track if this object was deleted or not. + std::shared_ptr self_reference; + + /// The current time point (now) + timepoint_t _now; + + /// The Task Queue which contains all task objects. + TaskQueue _task_holder; + + typedef std::queue> AsyncHolder; + + /// Contains all asynchronous tasks which will be invoked at + /// the next update tick. + AsyncHolder _asyncHolder; + + predicate_t _predicate; + + static bool EmptyValidator() + { + return true; + } + + static void EmptyCallback() + { + } + +public: + TaskScheduler() + : self_reference(this, [](TaskScheduler const*) { }), _now(clock_t::now()), _predicate(EmptyValidator) { } + + template TaskScheduler(P&& predicate) + : self_reference(this, [](TaskScheduler const*) { }), _now(clock_t::now()), _predicate(std::forward

(predicate)) { } + + TaskScheduler(TaskScheduler const&) = delete; + TaskScheduler(TaskScheduler&&) = delete; + TaskScheduler& operator= (TaskScheduler const&) = delete; + TaskScheduler& operator= (TaskScheduler&&) = delete; + + /// Sets a validator which is asked if tasks are allowed to be executed. + template + TaskScheduler& SetValidator(P&& predicate) + { + _predicate = std::forward

(predicate); + return *this; + } + + /// Clears the validator which is asked if tasks are allowed to be executed. + TaskScheduler& ClearValidator(); + + /// Update the scheduler to the current time. + /// Calls the optional callback on successfully finish. + TaskScheduler& Update(success_t const& callback = EmptyCallback); + + /// Update the scheduler with a difftime in ms. + /// Calls the optional callback on successfully finish. + TaskScheduler& Update(size_t const milliseconds, success_t const& callback = EmptyCallback); + + /// Update the scheduler with a difftime. + /// Calls the optional callback on successfully finish. + template + TaskScheduler& Update(std::chrono::duration<_Rep, _Period> const& difftime, + success_t const& callback = EmptyCallback) + { + _now += difftime; + Dispatch(callback); + return *this; + } + + /// Schedule an callable function that is executed at the next update tick. + /// Its safe to modify the TaskScheduler from within the callable. + TaskScheduler& Async(std::function const& callable); + + /// Schedule an event with a fixed rate. + /// Never call this from within a task context! Use TaskContext::Schedule instead! + template + TaskScheduler& Schedule(std::chrono::duration<_Rep, _Period> const& time, + task_handler_t const& task) + { + return ScheduleAt(_now, time, task); + } + + /// Schedule an event with a fixed rate. + /// Never call this from within a task context! Use TaskContext::Schedule instead! + template + TaskScheduler& Schedule(std::chrono::duration<_Rep, _Period> const& time, + group_t const group, task_handler_t const& task) + { + return ScheduleAt(_now, time, group, task); + } + + /// Schedule an event with a randomized rate between min and max rate. + /// Never call this from within a task context! Use TaskContext::Schedule instead! + template + TaskScheduler& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max, task_handler_t const& task) + { + return Schedule(RandomDurationBetween(min, max), task); + } + + /// Schedule an event with a fixed rate. + /// Never call this from within a task context! Use TaskContext::Schedule instead! + template + TaskScheduler& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max, group_t const group, + task_handler_t const& task) + { + return Schedule(RandomDurationBetween(min, max), group, task); + } + + /// Cancels all tasks. + /// Never call this from within a task context! Use TaskContext::CancelAll instead! + TaskScheduler& CancelAll(); + + /// Cancel all tasks of a single group. + /// Never call this from within a task context! Use TaskContext::CancelGroup instead! + TaskScheduler& CancelGroup(group_t const group); + + /// Cancels all groups in the given std::vector. + /// Hint: Use std::initializer_list for this: "{1, 2, 3, 4}" + TaskScheduler& CancelGroupsOf(std::vector const& groups); + + /// Delays all tasks with the given duration. + template + TaskScheduler& DelayAll(std::chrono::duration<_Rep, _Period> const& duration) + { + _task_holder.ModifyIf([&duration](TaskContainer const & task) -> bool + { + task->_end += duration; + return true; + }); + return *this; + } + + /// Delays all tasks with a random duration between min and max. + template + TaskScheduler& DelayAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return DelayAll(RandomDurationBetween(min, max)); + } + + /// Delays all tasks of a group with the given duration. + template + TaskScheduler& DelayGroup(group_t const group, std::chrono::duration<_Rep, _Period> const& duration) + { + _task_holder.ModifyIf([&duration, group](TaskContainer const & task) -> bool + { + if (task->IsInGroup(group)) + { + task->_end += duration; + return true; + } + else + return false; + }); + return *this; + } + + /// Delays all tasks of a group with a random duration between min and max. + template + TaskScheduler& DelayGroup(group_t const group, + std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return DelayGroup(group, RandomDurationBetween(min, max)); + } + + /// Reschedule all tasks with a given duration. + template + TaskScheduler& RescheduleAll(std::chrono::duration<_Rep, _Period> const& duration) + { + auto const end = _now + duration; + _task_holder.ModifyIf([end](TaskContainer const & task) -> bool + { + task->_end = end; + return true; + }); + return *this; + } + + /// Reschedule all tasks with a random duration between min and max. + template + TaskScheduler& RescheduleAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return RescheduleAll(RandomDurationBetween(min, max)); + } + + /// Reschedule all tasks of a group with the given duration. + template + TaskScheduler& RescheduleGroup(group_t const group, std::chrono::duration<_Rep, _Period> const& duration) + { + auto const end = _now + duration; + _task_holder.ModifyIf([end, group](TaskContainer const & task) -> bool + { + if (task->IsInGroup(group)) + { + task->_end = end; + return true; + } + else + return false; + }); + return *this; + } + + /// Reschedule all tasks of a group with a random duration between min and max. + template + TaskScheduler& RescheduleGroup(group_t const group, + std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return RescheduleGroup(group, RandomDurationBetween(min, max)); + } + +private: + /// Insert a new task to the enqueued tasks. + TaskScheduler& InsertTask(TaskContainer task); + + template + TaskScheduler& ScheduleAt(timepoint_t const& end, + std::chrono::duration<_Rep, _Period> const& time, task_handler_t const& task) + { + return InsertTask(TaskContainer(new Task(end + time, time, task))); + } + + /// Schedule an event with a fixed rate. + /// Never call this from within a task context! Use TaskContext::schedule instead! + template + TaskScheduler& ScheduleAt(timepoint_t const& end, + std::chrono::duration<_Rep, _Period> const& time, + group_t const group, task_handler_t const& task) + { + static repeated_t const DEFAULT_REPEATED = 0; + return InsertTask(TaskContainer(new Task(end + time, time, group, DEFAULT_REPEATED, task))); + } + + // Returns a random duration between min and max + template + static std::chrono::milliseconds RandomDurationBetween(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + auto const milli_min = std::chrono::duration_cast(min); + auto const milli_max = std::chrono::duration_cast(max); + + // TC specific: use SFMT URandom + return std::chrono::milliseconds(urand(uint32(milli_min.count()), uint32(milli_max.count()))); + } + + /// Dispatch remaining tasks + void Dispatch(success_t const& callback); +}; + +class TaskContext +{ + friend class TaskScheduler; + + /// Associated task + TaskScheduler::TaskContainer _task; + + /// Owner + std::weak_ptr _owner; + + /// Marks the task as consumed + std::shared_ptr _consumed; + + /// Dispatches an action safe on the TaskScheduler + TaskContext& Dispatch(std::function const& apply); + +public: + // Empty constructor + TaskContext() + : _task(), _owner(), _consumed(std::make_shared(true)) { } + + // Construct from task and owner + explicit TaskContext(TaskScheduler::TaskContainer&& task, std::weak_ptr&& owner) + : _task(task), _owner(owner), _consumed(std::make_shared(false)) { } + + // Copy construct + TaskContext(TaskContext const& right) + : _task(right._task), _owner(right._owner), _consumed(right._consumed) { } + + // Move construct + TaskContext(TaskContext&& right) + : _task(std::move(right._task)), _owner(std::move(right._owner)), _consumed(std::move(right._consumed)) { } + + // Copy assign + TaskContext& operator= (TaskContext const& right) + { + _task = right._task; + _owner = right._owner; + _consumed = right._consumed; + return *this; + } + + // Move assign + TaskContext& operator= (TaskContext&& right) + { + _task = std::move(right._task); + _owner = std::move(right._owner); + _consumed = std::move(right._consumed); + return *this; + } + + /// Returns true if the owner was deallocated and this context has expired. + bool IsExpired() const; + + /// Returns true if the event is in the given group + bool IsInGroup(TaskScheduler::group_t const group) const; + + /// Sets the event in the given group + TaskContext& SetGroup(TaskScheduler::group_t const group); + + /// Removes the group from the event + TaskContext& ClearGroup(); + + /// Returns the repeat counter which increases every time the task is repeated. + TaskScheduler::repeated_t GetRepeatCounter() const; + + /// Repeats the event and sets a new duration. + /// std::chrono::seconds(5) for example. + /// This will consume the task context, its not possible to repeat the task again + /// from the same task context! + template + TaskContext& Repeat(std::chrono::duration<_Rep, _Period> const& duration) + { + AssertOnConsumed(); + + // Set new duration, in-context timing and increment repeat counter + _task->_duration = duration; + _task->_end += duration; + _task->_repeated += 1; + (*_consumed) = true; + return Dispatch(std::bind(&TaskScheduler::InsertTask, std::placeholders::_1, _task)); + } + + /// Repeats the event with the same duration. + /// This will consume the task context, its not possible to repeat the task again + /// from the same task context! + TaskContext& Repeat() + { + return Repeat(_task->_duration); + } + + /// Repeats the event and set a new duration that is randomized between min and max. + /// std::chrono::seconds(5) for example. + /// This will consume the task context, its not possible to repeat the task again + /// from the same task context! + template + TaskContext& Repeat(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return Repeat(TaskScheduler::RandomDurationBetween(min, max)); + } + + /// Schedule a callable function that is executed at the next update tick from within the context. + /// Its safe to modify the TaskScheduler from within the callable. + TaskContext& Async(std::function const& callable); + + /// Schedule an event with a fixed rate from within the context. + /// Its possible that the new event is executed immediately! + /// Use TaskScheduler::Async to create a task + /// which will be called at the next update tick. + template + TaskContext& Schedule(std::chrono::duration<_Rep, _Period> const& time, + TaskScheduler::task_handler_t const& task) + { + auto const end = _task->_end; + return Dispatch([end, time, task](TaskScheduler & scheduler) -> TaskScheduler & + { + return scheduler.ScheduleAt<_Rep, _Period>(end, time, task); + }); + } + + /// Schedule an event with a fixed rate from within the context. + /// Its possible that the new event is executed immediately! + /// Use TaskScheduler::Async to create a task + /// which will be called at the next update tick. + template + TaskContext& Schedule(std::chrono::duration<_Rep, _Period> const& time, + TaskScheduler::group_t const group, TaskScheduler::task_handler_t const& task) + { + auto const end = _task->_end; + return Dispatch([end, time, group, task](TaskScheduler & scheduler) -> TaskScheduler & + { + return scheduler.ScheduleAt<_Rep, _Period>(end, time, group, task); + }); + } + + /// Schedule an event with a randomized rate between min and max rate from within the context. + /// Its possible that the new event is executed immediately! + /// Use TaskScheduler::Async to create a task + /// which will be called at the next update tick. + template + TaskContext& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max, TaskScheduler::task_handler_t const& task) + { + return Schedule(TaskScheduler::RandomDurationBetween(min, max), task); + } + + /// Schedule an event with a randomized rate between min and max rate from within the context. + /// Its possible that the new event is executed immediately! + /// Use TaskScheduler::Async to create a task + /// which will be called at the next update tick. + template + TaskContext& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max, TaskScheduler::group_t const group, + TaskScheduler::task_handler_t const& task) + { + return Schedule(TaskScheduler::RandomDurationBetween(min, max), group, task); + } + + /// Cancels all tasks from within the context. + TaskContext& CancelAll(); + + /// Cancel all tasks of a single group from within the context. + TaskContext& CancelGroup(TaskScheduler::group_t const group); + + /// Cancels all groups in the given std::vector from within the context. + /// Hint: Use std::initializer_list for this: "{1, 2, 3, 4}" + TaskContext& CancelGroupsOf(std::vector const& groups); + + /// Delays all tasks with the given duration from within the context. + template + TaskContext& DelayAll(std::chrono::duration<_Rep, _Period> const& duration) + { + return Dispatch(std::bind(&TaskScheduler::DelayAll<_Rep, _Period>, std::placeholders::_1, duration)); + } + + /// Delays all tasks with a random duration between min and max from within the context. + template + TaskContext& DelayAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return DelayAll(TaskScheduler::RandomDurationBetween(min, max)); + } + + /// Delays all tasks of a group with the given duration from within the context. + template + TaskContext& DelayGroup(TaskScheduler::group_t const group, std::chrono::duration<_Rep, _Period> const& duration) + { + return Dispatch(std::bind(&TaskScheduler::DelayGroup<_Rep, _Period>, std::placeholders::_1, group, duration)); + } + + /// Delays all tasks of a group with a random duration between min and max from within the context. + template + TaskContext& DelayGroup(TaskScheduler::group_t const group, + std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return DelayGroup(group, TaskScheduler::RandomDurationBetween(min, max)); + } + + /// Reschedule all tasks with the given duration. + template + TaskContext& RescheduleAll(std::chrono::duration<_Rep, _Period> const& duration) + { + return Dispatch(std::bind(&TaskScheduler::RescheduleAll, std::placeholders::_1, duration)); + } + + /// Reschedule all tasks with a random duration between min and max. + template + TaskContext& RescheduleAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return RescheduleAll(TaskScheduler::RandomDurationBetween(min, max)); + } + + /// Reschedule all tasks of a group with the given duration. + template + TaskContext& RescheduleGroup(TaskScheduler::group_t const group, std::chrono::duration<_Rep, _Period> const& duration) + { + return Dispatch(std::bind(&TaskScheduler::RescheduleGroup<_Rep, _Period>, std::placeholders::_1, group, duration)); + } + + /// Reschedule all tasks of a group with a random duration between min and max. + template + TaskContext& RescheduleGroup(TaskScheduler::group_t const group, + std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return RescheduleGroup(group, TaskScheduler::RandomDurationBetween(min, max)); + } + +private: + /// Asserts if the task was consumed already. + void AssertOnConsumed() const; + + /// Invokes the associated hook of the task. + void Invoke(); +}; + +#endif diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 7bde02c25d83a5..8c3ac4b16da19b 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -77,6 +77,7 @@ #include "TicketMgr.h" #include "ScriptMgr.h" #include "GameGraveyard.h" +#include "QuestTracker.h" #ifdef ELUNA #include "LuaEngine.h" @@ -15859,15 +15860,8 @@ void Player::AddQuest(Quest const* quest, Object* questGiver) // check if Quest Tracker is enabled if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) { - // prepare Quest Tracker datas - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_QUEST_TRACK); - stmt->setUInt32(0, quest_id); - stmt->setUInt32(1, GetGUIDLow()); - stmt->setString(2, _HASH); - stmt->setString(3, _DATE); - // add to Quest Tracker - CharacterDatabase.Execute(stmt); + sQuestTracker->Add(quest_id, GetGUIDLow(), GitRevision::GetHash(), GitRevision::GetDate()); } // Xinef: area auras may change on quest accept! @@ -15900,13 +15894,8 @@ void Player::CompleteQuest(uint32 quest_id) // check if Quest Tracker is enabled if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) { - // prepare Quest Tracker datas - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_QUEST_TRACK_COMPLETE_TIME); - stmt->setUInt32(0, quest_id); - stmt->setUInt32(1, GetGUIDLow()); - // add to Quest Tracker - CharacterDatabase.Execute(stmt); + sQuestTracker->UpdateCompleteTime(quest_id, GetGUIDLow()); } } diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index 3155c4249635de..234ae9d1301254 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -21,6 +21,8 @@ #include "ScriptMgr.h" #include "GameObjectAI.h" #include "Language.h" +#include "QuestTracker.h" + #ifdef ELUNA #include "LuaEngine.h" #endif @@ -431,13 +433,8 @@ void WorldSession::HandleQuestLogRemoveQuest(WorldPacket& recvData) // check if Quest Tracker is enabled if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) { - // prepare Quest Tracker datas - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_QUEST_TRACK_ABANDON_TIME); - stmt->setUInt32(0, questId); - stmt->setUInt32(1, _player->GetGUIDLow()); - // add to Quest Tracker - CharacterDatabase.Execute(stmt); + sQuestTracker->UpdateAbandonTime(questId, _player->GetGUIDLow()); } } diff --git a/src/server/game/Quests/QuestTracker.cpp b/src/server/game/Quests/QuestTracker.cpp new file mode 100644 index 00000000000000..49df0331e350e3 --- /dev/null +++ b/src/server/game/Quests/QuestTracker.cpp @@ -0,0 +1,117 @@ +/* + * This file is part of the WarheadCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "QuestTracker.h" +#include "DatabaseEnv.h" +#include "World.h" +#include "TaskScheduler.h" +#include +#include + +namespace +{ + std::vector> _questTrackStore; + std::vector _queueStmt; + TaskScheduler scheduler; +} + +QuestTracker* QuestTracker::instance() +{ + static QuestTracker instance; + return &instance; +} + +void QuestTracker::InitSystem() +{ + if (!sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) + { + sLog->outString(">> System disabled"); + return; + } + + scheduler.Schedule(15s, [this](TaskContext context) + { + Execute(); + + context.Repeat(); + }); + + sLog->outString(">> System loading"); +} + +void QuestTracker::Update(uint32 diff) +{ + if (!sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) + return; + + scheduler.Update(diff); +} + +void QuestTracker::Execute() +{ + auto trans = CharacterDatabase.BeginTransaction(); + + for (auto const& [ID, CharacterLowGuid, Hash, Revision] : _questTrackStore) + { + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_QUEST_TRACK); + stmt->setUInt32(0, ID); + stmt->setUInt32(1, CharacterLowGuid); + stmt->setString(2, Hash); + stmt->setString(3, Revision); + trans->Append(stmt); + } + + for (auto const& stmt : _queueStmt) + trans->Append(stmt); + + CharacterDatabase.CommitTransaction(trans); + + _questTrackStore.clear(); + _queueStmt.clear(); +} + +void QuestTracker::Add(uint32 questID, uint32 characterLowGuid, std::string const& coreHash, std::string const& coreRevision) +{ + _questTrackStore.emplace_back(std::make_tuple(questID, characterLowGuid, coreHash, coreRevision)); +} + +void QuestTracker::UpdateCompleteTime(uint32 questID, uint32 characterLowGuid) +{ + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_QUEST_TRACK_COMPLETE_TIME); + stmt->setUInt32(0, questID); + stmt->setUInt32(1, characterLowGuid); + + _queueStmt.emplace_back(stmt); +} + +void QuestTracker::UpdateAbandonTime(uint32 questID, uint32 characterLowGuid) +{ + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_QUEST_TRACK_ABANDON_TIME); + stmt->setUInt32(0, questID); + stmt->setUInt32(1, characterLowGuid); + + _queueStmt.emplace_back(stmt); +} + +void QuestTracker::UpdateGMComplete(uint32 questID, uint32 characterLowGuid) +{ + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_QUEST_TRACK_GM_COMPLETE); + stmt->setUInt32(0, questID); + stmt->setUInt32(1, characterLowGuid); + + _queueStmt.emplace_back(stmt); +} diff --git a/src/server/game/Quests/QuestTracker.h b/src/server/game/Quests/QuestTracker.h new file mode 100644 index 00000000000000..10563f2d681297 --- /dev/null +++ b/src/server/game/Quests/QuestTracker.h @@ -0,0 +1,40 @@ +/* + * This file is part of the WarheadCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef _QUEST_TRACKER_H +#define _QUEST_TRACKER_H + +#include "Common.h" + +class QuestTracker +{ +public: + static QuestTracker* instance(); + + void InitSystem(); + void Update(uint32 diff); + void Execute(); + + void Add(uint32 questID, uint32 characterLowGuid, std::string const& coreHash, std::string const& coreRevision); + void UpdateCompleteTime(uint32 questID, uint32 characterLowGuid); + void UpdateAbandonTime(uint32 questID, uint32 characterLowGuid); + void UpdateGMComplete(uint32 questID, uint32 characterLowGuid); +}; + +#define sQuestTracker QuestTracker::instance() + +#endif diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index ff72fe66b4a8f4..3031cdb26f8d0c 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -77,6 +77,7 @@ #include "SavingSystem.h" #include "ServerMotd.h" #include "GameGraveyard.h" +#include "QuestTracker.h" #include #ifdef ELUNA @@ -2028,6 +2029,9 @@ void World::SetInitialWorldSettings() mgr = ChannelMgr::forTeam(TEAM_HORDE); mgr->LoadChannels(); + sLog->outString("Start QuestTracker system..."); + sQuestTracker->InitSystem(); + #ifdef ELUNA ///- Run eluna scripts. // in multithread foreach: run scripts @@ -2346,6 +2350,8 @@ void World::Update(uint32 diff) sScriptMgr->OnWorldUpdate(diff); SavingSystemMgr::Update(diff); + + sQuestTracker->Update(diff); } void World::ForceGameEventUpdate() diff --git a/src/server/scripts/Commands/cs_quest.cpp b/src/server/scripts/Commands/cs_quest.cpp index ad08860cf51958..8b61d41a9203c4 100644 --- a/src/server/scripts/Commands/cs_quest.cpp +++ b/src/server/scripts/Commands/cs_quest.cpp @@ -16,6 +16,7 @@ EndScriptData */ #include "Player.h" #include "ReputationMgr.h" #include "ScriptMgr.h" +#include "QuestTracker.h" class quest_commandscript : public CommandScript { @@ -234,13 +235,8 @@ class quest_commandscript : public CommandScript // check if Quest Tracker is enabled if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) { - // prepare Quest Tracker datas - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_QUEST_TRACK_GM_COMPLETE); - stmt->setUInt32(0, quest->GetQuestId()); - stmt->setUInt32(1, player->GetGUIDLow()); - // add to Quest Tracker - CharacterDatabase.Execute(stmt); + sQuestTracker->UpdateGMComplete(quest->GetQuestId(), player->GetGUIDLow()); } player->CompleteQuest(entry); From f27b448c157b4378d4eea62dfcba3ef110bb4342 Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Tue, 5 Jan 2021 05:14:06 +0700 Subject: [PATCH 02/19] Split updates and fix memleak --- src/server/game/Quests/QuestTracker.cpp | 57 +++++++++++++++---------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/src/server/game/Quests/QuestTracker.cpp b/src/server/game/Quests/QuestTracker.cpp index 49df0331e350e3..07b3ceba4756ea 100644 --- a/src/server/game/Quests/QuestTracker.cpp +++ b/src/server/game/Quests/QuestTracker.cpp @@ -25,7 +25,9 @@ namespace { std::vector> _questTrackStore; - std::vector _queueStmt; + std::vector> _questCompleteStore; + std::vector> _questAbandonStore; + std::vector> _questGMCompleteStore; TaskScheduler scheduler; } @@ -43,7 +45,7 @@ void QuestTracker::InitSystem() return; } - scheduler.Schedule(15s, [this](TaskContext context) + scheduler.Schedule(10s, [this](TaskContext context) { Execute(); @@ -63,6 +65,7 @@ void QuestTracker::Update(uint32 diff) void QuestTracker::Execute() { + /// Insert section auto trans = CharacterDatabase.BeginTransaction(); for (auto const& [ID, CharacterLowGuid, Hash, Revision] : _questTrackStore) @@ -75,43 +78,53 @@ void QuestTracker::Execute() trans->Append(stmt); } - for (auto const& stmt : _queueStmt) - trans->Append(stmt); - CharacterDatabase.CommitTransaction(trans); _questTrackStore.clear(); - _queueStmt.clear(); + + // Update section + auto SendUpdateQuestTrack = [](uint8 stmtIndex, uint32 questID, uint32 characterLowGuid) + { + auto trans = CharacterDatabase.BeginTransaction(); + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(stmtIndex); + stmt->setUInt32(0, questID); + stmt->setUInt32(1, characterLowGuid); + trans->Append(stmt); + + CharacterDatabase.CommitTransaction(trans); + }; + + for (auto const& [questID, characterLowGuid] : _questCompleteStore) + SendUpdateQuestTrack(CHAR_UPD_QUEST_TRACK_COMPLETE_TIME, questID, characterLowGuid); + + for (auto const& [questID, characterLowGuid] : _questAbandonStore) + SendUpdateQuestTrack(CHAR_UPD_QUEST_TRACK_ABANDON_TIME, questID, characterLowGuid); + + for (auto const& [questID, characterLowGuid] : _questGMCompleteStore) + SendUpdateQuestTrack(CHAR_UPD_QUEST_TRACK_GM_COMPLETE, questID, characterLowGuid); + + _questCompleteStore.clear(); + _questAbandonStore.clear(); + _questGMCompleteStore.clear(); } void QuestTracker::Add(uint32 questID, uint32 characterLowGuid, std::string const& coreHash, std::string const& coreRevision) { - _questTrackStore.emplace_back(std::make_tuple(questID, characterLowGuid, coreHash, coreRevision)); + _questTrackStore.emplace_back(questID, characterLowGuid, coreHash, coreRevision); } void QuestTracker::UpdateCompleteTime(uint32 questID, uint32 characterLowGuid) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_QUEST_TRACK_COMPLETE_TIME); - stmt->setUInt32(0, questID); - stmt->setUInt32(1, characterLowGuid); - - _queueStmt.emplace_back(stmt); + _questCompleteStore.emplace_back(questID, characterLowGuid); } void QuestTracker::UpdateAbandonTime(uint32 questID, uint32 characterLowGuid) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_QUEST_TRACK_ABANDON_TIME); - stmt->setUInt32(0, questID); - stmt->setUInt32(1, characterLowGuid); - - _queueStmt.emplace_back(stmt); + _questAbandonStore.emplace_back(questID, characterLowGuid); } void QuestTracker::UpdateGMComplete(uint32 questID, uint32 characterLowGuid) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_QUEST_TRACK_GM_COMPLETE); - stmt->setUInt32(0, questID); - stmt->setUInt32(1, characterLowGuid); - - _queueStmt.emplace_back(stmt); + _questGMCompleteStore.emplace_back(questID, characterLowGuid); } From 72f5877d4a051f554c299fcbf8f30ac48ddb6f2e Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Fri, 8 Jan 2021 04:41:47 +0700 Subject: [PATCH 03/19] CharacterDatabaseStatements enum --- src/server/game/Quests/QuestTracker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Quests/QuestTracker.cpp b/src/server/game/Quests/QuestTracker.cpp index 07b3ceba4756ea..9bc33b06f8b5e7 100644 --- a/src/server/game/Quests/QuestTracker.cpp +++ b/src/server/game/Quests/QuestTracker.cpp @@ -83,7 +83,7 @@ void QuestTracker::Execute() _questTrackStore.clear(); // Update section - auto SendUpdateQuestTrack = [](uint8 stmtIndex, uint32 questID, uint32 characterLowGuid) + auto SendUpdateQuestTrack = [](CharacterDatabaseStatements stmtIndex, uint32 questID, uint32 characterLowGuid) { auto trans = CharacterDatabase.BeginTransaction(); From eba4dd9b228bb53d8a5861753032794ae54a3f09 Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Fri, 8 Jan 2021 09:47:02 +0700 Subject: [PATCH 04/19] to 30s --- src/server/game/Quests/QuestTracker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Quests/QuestTracker.cpp b/src/server/game/Quests/QuestTracker.cpp index 9bc33b06f8b5e7..fb6016ebe3be45 100644 --- a/src/server/game/Quests/QuestTracker.cpp +++ b/src/server/game/Quests/QuestTracker.cpp @@ -45,7 +45,7 @@ void QuestTracker::InitSystem() return; } - scheduler.Schedule(10s, [this](TaskContext context) + scheduler.Schedule(30s, [this](TaskContext context) { Execute(); From f00f0a577ddc059ed5aa1d1e7bdf25676e9a61f9 Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Fri, 8 Jan 2021 09:47:12 +0700 Subject: [PATCH 05/19] 1 trans for updates --- src/server/game/Quests/QuestTracker.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/server/game/Quests/QuestTracker.cpp b/src/server/game/Quests/QuestTracker.cpp index fb6016ebe3be45..e013827a95a2da 100644 --- a/src/server/game/Quests/QuestTracker.cpp +++ b/src/server/game/Quests/QuestTracker.cpp @@ -83,16 +83,14 @@ void QuestTracker::Execute() _questTrackStore.clear(); // Update section + auto trans = CharacterDatabase.BeginTransaction(); + auto SendUpdateQuestTrack = [](CharacterDatabaseStatements stmtIndex, uint32 questID, uint32 characterLowGuid) { - auto trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(stmtIndex); stmt->setUInt32(0, questID); stmt->setUInt32(1, characterLowGuid); trans->Append(stmt); - - CharacterDatabase.CommitTransaction(trans); }; for (auto const& [questID, characterLowGuid] : _questCompleteStore) @@ -104,6 +102,8 @@ void QuestTracker::Execute() for (auto const& [questID, characterLowGuid] : _questGMCompleteStore) SendUpdateQuestTrack(CHAR_UPD_QUEST_TRACK_GM_COMPLETE, questID, characterLowGuid); + CharacterDatabase.CommitTransaction(trans); + _questCompleteStore.clear(); _questAbandonStore.clear(); _questGMCompleteStore.clear(); From 5c8ddce51ef6106cf2f032eb8a8345f450b0eb9c Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Fri, 8 Jan 2021 10:20:14 +0700 Subject: [PATCH 06/19] fix builld --- src/server/game/Quests/QuestTracker.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/server/game/Quests/QuestTracker.cpp b/src/server/game/Quests/QuestTracker.cpp index e013827a95a2da..7f788b67861268 100644 --- a/src/server/game/Quests/QuestTracker.cpp +++ b/src/server/game/Quests/QuestTracker.cpp @@ -66,7 +66,7 @@ void QuestTracker::Update(uint32 diff) void QuestTracker::Execute() { /// Insert section - auto trans = CharacterDatabase.BeginTransaction(); + auto insertTrans = CharacterDatabase.BeginTransaction(); for (auto const& [ID, CharacterLowGuid, Hash, Revision] : _questTrackStore) { @@ -75,22 +75,22 @@ void QuestTracker::Execute() stmt->setUInt32(1, CharacterLowGuid); stmt->setString(2, Hash); stmt->setString(3, Revision); - trans->Append(stmt); + insertTrans->Append(stmt); } - CharacterDatabase.CommitTransaction(trans); + CharacterDatabase.CommitTransaction(insertTrans); _questTrackStore.clear(); // Update section - auto trans = CharacterDatabase.BeginTransaction(); + auto updateTrans = CharacterDatabase.BeginTransaction(); auto SendUpdateQuestTrack = [](CharacterDatabaseStatements stmtIndex, uint32 questID, uint32 characterLowGuid) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(stmtIndex); stmt->setUInt32(0, questID); stmt->setUInt32(1, characterLowGuid); - trans->Append(stmt); + updateTrans->Append(stmt); }; for (auto const& [questID, characterLowGuid] : _questCompleteStore) @@ -102,7 +102,7 @@ void QuestTracker::Execute() for (auto const& [questID, characterLowGuid] : _questGMCompleteStore) SendUpdateQuestTrack(CHAR_UPD_QUEST_TRACK_GM_COMPLETE, questID, characterLowGuid); - CharacterDatabase.CommitTransaction(trans); + CharacterDatabase.CommitTransaction(updateTrans); _questCompleteStore.clear(); _questAbandonStore.clear(); From a0124f2d6ee6195e91a158731bcf1721b585e044 Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Fri, 8 Jan 2021 10:25:41 +0700 Subject: [PATCH 07/19] fix lambda --- src/server/game/Quests/QuestTracker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Quests/QuestTracker.cpp b/src/server/game/Quests/QuestTracker.cpp index 7f788b67861268..e8d944fab79c49 100644 --- a/src/server/game/Quests/QuestTracker.cpp +++ b/src/server/game/Quests/QuestTracker.cpp @@ -85,7 +85,7 @@ void QuestTracker::Execute() // Update section auto updateTrans = CharacterDatabase.BeginTransaction(); - auto SendUpdateQuestTrack = [](CharacterDatabaseStatements stmtIndex, uint32 questID, uint32 characterLowGuid) + auto SendUpdateQuestTrack = [&updateTrans](CharacterDatabaseStatements stmtIndex, uint32 questID, uint32 characterLowGuid) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(stmtIndex); stmt->setUInt32(0, questID); From d0e1d8c7648a673aaf5fd75b5bc6dbf8d1b4bc98 Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Wed, 20 Jan 2021 20:26:07 +0700 Subject: [PATCH 08/19] Whitespace cleanup --- src/server/game/Movement/MovementGenerators/PathGenerator.cpp | 1 - src/server/game/Quests/QuestTracker.cpp | 2 +- src/server/game/Warden/WardenCheckMgr.cpp | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/server/game/Movement/MovementGenerators/PathGenerator.cpp b/src/server/game/Movement/MovementGenerators/PathGenerator.cpp index 918a66448e7f3f..d5b61dc8f7230c 100644 --- a/src/server/game/Movement/MovementGenerators/PathGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/PathGenerator.cpp @@ -548,7 +548,6 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con _sourceUnit->UpdateAllowedPositionZ(_pathPoints[lastIdx].x, _pathPoints[lastIdx].y, _pathPoints[lastIdx].z); } - // pussywizard: fix for running back and forth while target moves // pussywizard: second point (first is actual position) is forward to current server position, but when received by the client it's already behind, so the npc runs back to that point // pussywizard: the higher speed, the more probable the situation is, so try to move second point as far forward the path as possible diff --git a/src/server/game/Quests/QuestTracker.cpp b/src/server/game/Quests/QuestTracker.cpp index e8d944fab79c49..6814954e57d543 100644 --- a/src/server/game/Quests/QuestTracker.cpp +++ b/src/server/game/Quests/QuestTracker.cpp @@ -103,7 +103,7 @@ void QuestTracker::Execute() SendUpdateQuestTrack(CHAR_UPD_QUEST_TRACK_GM_COMPLETE, questID, characterLowGuid); CharacterDatabase.CommitTransaction(updateTrans); - + _questCompleteStore.clear(); _questAbandonStore.clear(); _questGMCompleteStore.clear(); diff --git a/src/server/game/Warden/WardenCheckMgr.cpp b/src/server/game/Warden/WardenCheckMgr.cpp index a38e665e7ce1a4..66b679e4477ba0 100644 --- a/src/server/game/Warden/WardenCheckMgr.cpp +++ b/src/server/game/Warden/WardenCheckMgr.cpp @@ -238,4 +238,3 @@ WardenCheckResult const* WardenCheckMgr::GetWardenResultById(uint16 Id) return nullptr; } - From 570ccacabcb787842123eabadd3d2204236c1ab7 Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Wed, 20 Jan 2021 20:30:00 +0700 Subject: [PATCH 09/19] Delete transactions --- src/server/game/Quests/QuestTracker.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/server/game/Quests/QuestTracker.cpp b/src/server/game/Quests/QuestTracker.cpp index 6814954e57d543..9d126c84a2d5ca 100644 --- a/src/server/game/Quests/QuestTracker.cpp +++ b/src/server/game/Quests/QuestTracker.cpp @@ -66,7 +66,7 @@ void QuestTracker::Update(uint32 diff) void QuestTracker::Execute() { /// Insert section - auto insertTrans = CharacterDatabase.BeginTransaction(); + //auto insertTrans = CharacterDatabase.BeginTransaction(); for (auto const& [ID, CharacterLowGuid, Hash, Revision] : _questTrackStore) { @@ -75,22 +75,22 @@ void QuestTracker::Execute() stmt->setUInt32(1, CharacterLowGuid); stmt->setString(2, Hash); stmt->setString(3, Revision); - insertTrans->Append(stmt); + CharacterDatabase.Execute(stmt); } - CharacterDatabase.CommitTransaction(insertTrans); + //CharacterDatabase.CommitTransaction(insertTrans); _questTrackStore.clear(); // Update section - auto updateTrans = CharacterDatabase.BeginTransaction(); + //auto updateTrans = CharacterDatabase.BeginTransaction(); - auto SendUpdateQuestTrack = [&updateTrans](CharacterDatabaseStatements stmtIndex, uint32 questID, uint32 characterLowGuid) + auto SendUpdateQuestTrack = [](CharacterDatabaseStatements stmtIndex, uint32 questID, uint32 characterLowGuid) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(stmtIndex); stmt->setUInt32(0, questID); stmt->setUInt32(1, characterLowGuid); - updateTrans->Append(stmt); + CharacterDatabase.Execute(stmt); }; for (auto const& [questID, characterLowGuid] : _questCompleteStore) @@ -102,7 +102,7 @@ void QuestTracker::Execute() for (auto const& [questID, characterLowGuid] : _questGMCompleteStore) SendUpdateQuestTrack(CHAR_UPD_QUEST_TRACK_GM_COMPLETE, questID, characterLowGuid); - CharacterDatabase.CommitTransaction(updateTrans); + //CharacterDatabase.CommitTransaction(updateTrans); _questCompleteStore.clear(); _questAbandonStore.clear(); From e19f28c8f092b8976211f1ad68ea2aaf2f8e4dc6 Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Wed, 20 Jan 2021 21:08:21 +0700 Subject: [PATCH 10/19] Add info --- src/server/game/Quests/QuestTracker.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/server/game/Quests/QuestTracker.cpp b/src/server/game/Quests/QuestTracker.cpp index 9d126c84a2d5ca..8dacfdbe9c378b 100644 --- a/src/server/game/Quests/QuestTracker.cpp +++ b/src/server/game/Quests/QuestTracker.cpp @@ -65,9 +65,11 @@ void QuestTracker::Update(uint32 diff) void QuestTracker::Execute() { - /// Insert section - //auto insertTrans = CharacterDatabase.BeginTransaction(); + sLog->outString("> QuestTracker: Start Execute..."); + + uint32 msTimeStart = getMSTime(); + /// Insert section for (auto const& [ID, CharacterLowGuid, Hash, Revision] : _questTrackStore) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_QUEST_TRACK); @@ -78,13 +80,11 @@ void QuestTracker::Execute() CharacterDatabase.Execute(stmt); } - //CharacterDatabase.CommitTransaction(insertTrans); + sLog->outString("> QuestTracker: Execute 'CHAR_INS_QUEST_TRACK' (%u)", static_cast(_questTrackStore.size())); _questTrackStore.clear(); // Update section - //auto updateTrans = CharacterDatabase.BeginTransaction(); - auto SendUpdateQuestTrack = [](CharacterDatabaseStatements stmtIndex, uint32 questID, uint32 characterLowGuid) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(stmtIndex); @@ -96,17 +96,23 @@ void QuestTracker::Execute() for (auto const& [questID, characterLowGuid] : _questCompleteStore) SendUpdateQuestTrack(CHAR_UPD_QUEST_TRACK_COMPLETE_TIME, questID, characterLowGuid); + sLog->outString("> QuestTracker: Execute 'CHAR_UPD_QUEST_TRACK_COMPLETE_TIME' (%u)", static_cast(_questCompleteStore.size())); + for (auto const& [questID, characterLowGuid] : _questAbandonStore) SendUpdateQuestTrack(CHAR_UPD_QUEST_TRACK_ABANDON_TIME, questID, characterLowGuid); + sLog->outString("> QuestTracker: Execute 'CHAR_UPD_QUEST_TRACK_ABANDON_TIME' (%u)", static_cast(_questAbandonStore.size())); + for (auto const& [questID, characterLowGuid] : _questGMCompleteStore) SendUpdateQuestTrack(CHAR_UPD_QUEST_TRACK_GM_COMPLETE, questID, characterLowGuid); - //CharacterDatabase.CommitTransaction(updateTrans); + sLog->outString("> QuestTracker: Execute 'CHAR_UPD_QUEST_TRACK_GM_COMPLETE' (%u)", static_cast(_questGMCompleteStore.size())); _questCompleteStore.clear(); _questAbandonStore.clear(); _questGMCompleteStore.clear(); + + sLog->outString("> QuestTracker: Execute end in %u ms", GetMSTimeDiffToNow(msTimeStart)); } void QuestTracker::Add(uint32 questID, uint32 characterLowGuid, std::string const& coreHash, std::string const& coreRevision) From 2ec6dc46dae9276709ab41f1971774be765e9d1e Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Fri, 29 Jan 2021 15:54:22 +0700 Subject: [PATCH 11/19] rework --- src/server/game/Quests/QuestTracker.cpp | 172 ++++++++++++++++-------- 1 file changed, 119 insertions(+), 53 deletions(-) diff --git a/src/server/game/Quests/QuestTracker.cpp b/src/server/game/Quests/QuestTracker.cpp index 8dacfdbe9c378b..4ca762b38e2349 100644 --- a/src/server/game/Quests/QuestTracker.cpp +++ b/src/server/game/Quests/QuestTracker.cpp @@ -17,17 +17,24 @@ #include "QuestTracker.h" #include "DatabaseEnv.h" -#include "World.h" +#include "GameConfig.h" #include "TaskScheduler.h" +#include "StringFormat.h" #include #include namespace { - std::vector> _questTrackStore; - std::vector> _questCompleteStore; - std::vector> _questAbandonStore; - std::vector> _questGMCompleteStore; + // Typdefs + using QuestTrackInsert = std::vector>; + using QuestTrackUpdate = std::vector>; + + QuestTrackInsert _questTrackStore; + QuestTrackUpdate _questCompleteStore; + QuestTrackUpdate _questAbandonStore; + QuestTrackUpdate _questGMCompleteStore; + + // Scheduler - for update queue TaskScheduler scheduler; } @@ -39,98 +46,157 @@ QuestTracker* QuestTracker::instance() void QuestTracker::InitSystem() { - if (!sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) + if (!CONF_GET_BOOL("QuestTracker.Enable")) { - sLog->outString(">> System disabled"); + LOG_INFO("server.loading", ">> System disabled"); return; } - scheduler.Schedule(30s, [this](TaskContext context) - { - Execute(); - - context.Repeat(); - }); + SetExecuteDelay(); - sLog->outString(">> System loading"); + LOG_INFO("server.loading", ">> System loading"); } void QuestTracker::Update(uint32 diff) { - if (!sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) + if (!CONF_GET_BOOL("QuestTracker.Enable")) return; scheduler.Update(diff); } +void QuestTracker::SetExecuteDelay() +{ + if (!CONF_GET_BOOL("QuestTracker.Queue.Enable") || !CONF_GET_BOOL("QuestTracker.Queue.Enable")) + return; + + Seconds updateSecs = Seconds(CONF_GET_UINT("QuestTracker.Queue.Delay")); + if (updateSecs < 1s) + { + LOG_ERROR("server", "> QuestTracker: ExecuteDelay < 1 second. Set 10 seconds"); + updateSecs = 10s; + return; + } + + scheduler.CancelAll(); + + scheduler.Schedule(updateSecs, [this](TaskContext context) + { + Execute(); + + context.Repeat(); + }); +} + void QuestTracker::Execute() { - sLog->outString("> QuestTracker: Start Execute..."); + if (_questTrackStore.empty() && + _questCompleteStore.empty() && + _questAbandonStore.empty() && + _questGMCompleteStore.empty()) + return; + + LOG_INFO("server", "> QuestTracker: Start Execute..."); uint32 msTimeStart = getMSTime(); /// Insert section - for (auto const& [ID, CharacterLowGuid, Hash, Revision] : _questTrackStore) + if (!_questTrackStore.empty()) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_QUEST_TRACK); - stmt->setUInt32(0, ID); - stmt->setUInt32(1, CharacterLowGuid); - stmt->setString(2, Hash); - stmt->setString(3, Revision); - CharacterDatabase.Execute(stmt); + for (auto const& [ID, CharacterLowGuid, Hash, Revision] : _questTrackStore) + { + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_QUEST_TRACK); + stmt->setUInt32(0, ID); + stmt->setUInt32(1, CharacterLowGuid); + stmt->setString(2, Hash); + stmt->setString(3, Revision); + CharacterDatabase.Execute(stmt); + } + + LOG_INFO("server", "> QuestTracker: Execute 'CHAR_INS_QUEST_TRACK' (%u)", static_cast(_questTrackStore.size())); + + _questTrackStore.clear(); } - sLog->outString("> QuestTracker: Execute 'CHAR_INS_QUEST_TRACK' (%u)", static_cast(_questTrackStore.size())); - - _questTrackStore.clear(); - - // Update section - auto SendUpdateQuestTrack = [](CharacterDatabaseStatements stmtIndex, uint32 questID, uint32 characterLowGuid) + /// Update section + auto SendUpdate = [&](QuestTrackUpdate& updateStore, CharacterDatabaseStatements stmtIndex, std::string const& updateType) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(stmtIndex); - stmt->setUInt32(0, questID); - stmt->setUInt32(1, characterLowGuid); - CharacterDatabase.Execute(stmt); - }; + if (updateStore.empty()) + return; - for (auto const& [questID, characterLowGuid] : _questCompleteStore) - SendUpdateQuestTrack(CHAR_UPD_QUEST_TRACK_COMPLETE_TIME, questID, characterLowGuid); + auto SendUpdateQuestTracker = [&](uint32 questID, uint32 characterLowGuid) + { + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(stmtIndex); + stmt->setUInt32(0, questID); + stmt->setUInt32(1, characterLowGuid); + CharacterDatabase.Execute(stmt); + }; - sLog->outString("> QuestTracker: Execute 'CHAR_UPD_QUEST_TRACK_COMPLETE_TIME' (%u)", static_cast(_questCompleteStore.size())); + for (auto const& [questID, characterLowGuid] : updateStore) + SendUpdateQuestTracker(questID, characterLowGuid); - for (auto const& [questID, characterLowGuid] : _questAbandonStore) - SendUpdateQuestTrack(CHAR_UPD_QUEST_TRACK_ABANDON_TIME, questID, characterLowGuid); + LOG_INFO("server", "> QuestTracker: Execute '%s' (%u)", updateType.c_str(), static_cast(updateStore.size())); - sLog->outString("> QuestTracker: Execute 'CHAR_UPD_QUEST_TRACK_ABANDON_TIME' (%u)", static_cast(_questAbandonStore.size())); - - for (auto const& [questID, characterLowGuid] : _questGMCompleteStore) - SendUpdateQuestTrack(CHAR_UPD_QUEST_TRACK_GM_COMPLETE, questID, characterLowGuid); - - sLog->outString("> QuestTracker: Execute 'CHAR_UPD_QUEST_TRACK_GM_COMPLETE' (%u)", static_cast(_questGMCompleteStore.size())); + updateStore.clear(); + }; - _questCompleteStore.clear(); - _questAbandonStore.clear(); - _questGMCompleteStore.clear(); + SendUpdate(_questCompleteStore, CHAR_UPD_QUEST_TRACK_COMPLETE_TIME, "CHAR_UPD_QUEST_TRACK_COMPLETE_TIME"); + SendUpdate(_questAbandonStore, CHAR_UPD_QUEST_TRACK_ABANDON_TIME, "CHAR_UPD_QUEST_TRACK_ABANDON_TIME"); + SendUpdate(_questGMCompleteStore, CHAR_UPD_QUEST_TRACK_GM_COMPLETE, "CHAR_UPD_QUEST_TRACK_GM_COMPLETE"); - sLog->outString("> QuestTracker: Execute end in %u ms", GetMSTimeDiffToNow(msTimeStart)); + LOG_INFO("server", "> QuestTracker: Execute end in %u ms", GetMSTimeDiffToNow(msTimeStart)); } void QuestTracker::Add(uint32 questID, uint32 characterLowGuid, std::string const& coreHash, std::string const& coreRevision) { - _questTrackStore.emplace_back(questID, characterLowGuid, coreHash, coreRevision); + if (CONF_GET_BOOL("QuestTracker.Queue.Enable")) + _questTrackStore.emplace_back(questID, characterLowGuid, coreHash, coreRevision); + else + { + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_QUEST_TRACK); + stmt->setUInt32(0, questID); + stmt->setUInt32(1, characterLowGuid); + stmt->setString(2, coreHash); + stmt->setString(3, coreRevision); + CharacterDatabase.Execute(stmt); + } } void QuestTracker::UpdateCompleteTime(uint32 questID, uint32 characterLowGuid) { - _questCompleteStore.emplace_back(questID, characterLowGuid); + if (CONF_GET_BOOL("QuestTracker.Queue.Enable")) + _questCompleteStore.emplace_back(questID, characterLowGuid); + else + { + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_QUEST_TRACK_COMPLETE_TIME); + stmt->setUInt32(0, questID); + stmt->setUInt32(1, characterLowGuid); + CharacterDatabase.Execute(stmt); + } } void QuestTracker::UpdateAbandonTime(uint32 questID, uint32 characterLowGuid) { - _questAbandonStore.emplace_back(questID, characterLowGuid); + if (CONF_GET_BOOL("QuestTracker.Queue.Enable")) + _questAbandonStore.emplace_back(questID, characterLowGuid); + else + { + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_QUEST_TRACK_ABANDON_TIME); + stmt->setUInt32(0, questID); + stmt->setUInt32(1, characterLowGuid); + CharacterDatabase.Execute(stmt); + } } void QuestTracker::UpdateGMComplete(uint32 questID, uint32 characterLowGuid) { - _questGMCompleteStore.emplace_back(questID, characterLowGuid); + if (CONF_GET_BOOL("QuestTracker.Queue.Enable")) + _questGMCompleteStore.emplace_back(questID, characterLowGuid); + else + { + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_QUEST_TRACK_GM_COMPLETE); + stmt->setUInt32(0, questID); + stmt->setUInt32(1, characterLowGuid); + CharacterDatabase.Execute(stmt); + } } From 6f6cbda85a95ffbc53c85393105463e03b5fbed0 Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Fri, 29 Jan 2021 16:06:42 +0700 Subject: [PATCH 12/19] to ac --- src/server/game/Entities/Player/Player.cpp | 4 +-- src/server/game/Handlers/QuestHandler.cpp | 2 +- src/server/game/Quests/QuestTracker.cpp | 33 ++++++++++---------- src/server/game/Quests/QuestTracker.h | 1 + src/server/game/World/IWorld.h | 4 ++- src/server/game/World/World.cpp | 7 +++-- src/server/scripts/Commands/cs_quest.cpp | 2 +- src/server/worldserver/worldserver.conf.dist | 16 ++++++++-- 8 files changed, 44 insertions(+), 25 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index df13626e073fae..000f857b47ba10 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -15897,7 +15897,7 @@ void Player::AddQuest(Quest const* quest, Object* questGiver) SendQuestUpdate(quest_id); // check if Quest Tracker is enabled - if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) + if (sWorld->getBoolConfig(CONFIG_QUEST_TRACKER_ENABLE)) { // add to Quest Tracker sQuestTracker->Add(quest_id, GetGUIDLow(), GitRevision::GetHash(), GitRevision::GetDate()); @@ -15935,7 +15935,7 @@ void Player::CompleteQuest(uint32 quest_id) AdditionalSavingAddMask(ADDITIONAL_SAVING_INVENTORY_AND_GOLD | ADDITIONAL_SAVING_QUEST_STATUS); // check if Quest Tracker is enabled - if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) + if (sWorld->getBoolConfig(CONFIG_QUEST_TRACKER_ENABLE)) { // add to Quest Tracker sQuestTracker->UpdateCompleteTime(quest_id, GetGUIDLow()); diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index 234ae9d1301254..09bea3426e0bbb 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -431,7 +431,7 @@ void WorldSession::HandleQuestLogRemoveQuest(WorldPacket& recvData) sLog->outDetail("Player %u abandoned quest %u", _player->GetGUIDLow(), questId); #endif // check if Quest Tracker is enabled - if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) + if (sWorld->getBoolConfig(CONFIG_QUEST_TRACKER_ENABLE)) { // add to Quest Tracker sQuestTracker->UpdateAbandonTime(questId, _player->GetGUIDLow()); diff --git a/src/server/game/Quests/QuestTracker.cpp b/src/server/game/Quests/QuestTracker.cpp index 4ca762b38e2349..0b054e931ae942 100644 --- a/src/server/game/Quests/QuestTracker.cpp +++ b/src/server/game/Quests/QuestTracker.cpp @@ -17,7 +17,8 @@ #include "QuestTracker.h" #include "DatabaseEnv.h" -#include "GameConfig.h" +#include "Duration.h" +#include "World.h" #include "TaskScheduler.h" #include "StringFormat.h" #include @@ -46,20 +47,20 @@ QuestTracker* QuestTracker::instance() void QuestTracker::InitSystem() { - if (!CONF_GET_BOOL("QuestTracker.Enable")) + if (!sWorld->getBoolConfig(CONFIG_QUEST_TRACKER_ENABLE)) { - LOG_INFO("server.loading", ">> System disabled"); + sLog->outString(">> System disabled"); return; } SetExecuteDelay(); - LOG_INFO("server.loading", ">> System loading"); + sLog->outString(">> System loading"); } void QuestTracker::Update(uint32 diff) { - if (!CONF_GET_BOOL("QuestTracker.Enable")) + if (!sWorld->getBoolConfig(CONFIG_QUEST_TRACKER_ENABLE)) return; scheduler.Update(diff); @@ -67,13 +68,13 @@ void QuestTracker::Update(uint32 diff) void QuestTracker::SetExecuteDelay() { - if (!CONF_GET_BOOL("QuestTracker.Queue.Enable") || !CONF_GET_BOOL("QuestTracker.Queue.Enable")) + if (!sWorld->getBoolConfig(CONFIG_QUEST_TRACKER_ENABLE) || !sWorld->getBoolConfig(CONFIG_QUEST_TRACKER_QUEUE_ENABLE)) return; - Seconds updateSecs = Seconds(CONF_GET_UINT("QuestTracker.Queue.Delay")); + Seconds updateSecs = Seconds(sWorld->getIntConfig(CONFIG_QUEST_TRACKER_QUEUE_DELAY)); if (updateSecs < 1s) { - LOG_ERROR("server", "> QuestTracker: ExecuteDelay < 1 second. Set 10 seconds"); + sLog->outError("> QuestTracker: ExecuteDelay < 1 second. Set 10 seconds"); updateSecs = 10s; return; } @@ -96,7 +97,7 @@ void QuestTracker::Execute() _questGMCompleteStore.empty()) return; - LOG_INFO("server", "> QuestTracker: Start Execute..."); + sLog->outString("> QuestTracker: Start Execute..."); uint32 msTimeStart = getMSTime(); @@ -113,7 +114,7 @@ void QuestTracker::Execute() CharacterDatabase.Execute(stmt); } - LOG_INFO("server", "> QuestTracker: Execute 'CHAR_INS_QUEST_TRACK' (%u)", static_cast(_questTrackStore.size())); + sLog->outString("> QuestTracker: Execute 'CHAR_INS_QUEST_TRACK' (%u)", static_cast(_questTrackStore.size())); _questTrackStore.clear(); } @@ -135,7 +136,7 @@ void QuestTracker::Execute() for (auto const& [questID, characterLowGuid] : updateStore) SendUpdateQuestTracker(questID, characterLowGuid); - LOG_INFO("server", "> QuestTracker: Execute '%s' (%u)", updateType.c_str(), static_cast(updateStore.size())); + sLog->outString("> QuestTracker: Execute '%s' (%u)", updateType.c_str(), static_cast(updateStore.size())); updateStore.clear(); }; @@ -144,12 +145,12 @@ void QuestTracker::Execute() SendUpdate(_questAbandonStore, CHAR_UPD_QUEST_TRACK_ABANDON_TIME, "CHAR_UPD_QUEST_TRACK_ABANDON_TIME"); SendUpdate(_questGMCompleteStore, CHAR_UPD_QUEST_TRACK_GM_COMPLETE, "CHAR_UPD_QUEST_TRACK_GM_COMPLETE"); - LOG_INFO("server", "> QuestTracker: Execute end in %u ms", GetMSTimeDiffToNow(msTimeStart)); + sLog->outString("> QuestTracker: Execute end in %u ms", GetMSTimeDiffToNow(msTimeStart)); } void QuestTracker::Add(uint32 questID, uint32 characterLowGuid, std::string const& coreHash, std::string const& coreRevision) { - if (CONF_GET_BOOL("QuestTracker.Queue.Enable")) + if (sWorld->getBoolConfig(CONFIG_QUEST_TRACKER_QUEUE_ENABLE)) _questTrackStore.emplace_back(questID, characterLowGuid, coreHash, coreRevision); else { @@ -164,7 +165,7 @@ void QuestTracker::Add(uint32 questID, uint32 characterLowGuid, std::string cons void QuestTracker::UpdateCompleteTime(uint32 questID, uint32 characterLowGuid) { - if (CONF_GET_BOOL("QuestTracker.Queue.Enable")) + if (sWorld->getBoolConfig(CONFIG_QUEST_TRACKER_QUEUE_ENABLE)) _questCompleteStore.emplace_back(questID, characterLowGuid); else { @@ -177,7 +178,7 @@ void QuestTracker::UpdateCompleteTime(uint32 questID, uint32 characterLowGuid) void QuestTracker::UpdateAbandonTime(uint32 questID, uint32 characterLowGuid) { - if (CONF_GET_BOOL("QuestTracker.Queue.Enable")) + if (sWorld->getBoolConfig(CONFIG_QUEST_TRACKER_QUEUE_ENABLE)) _questAbandonStore.emplace_back(questID, characterLowGuid); else { @@ -190,7 +191,7 @@ void QuestTracker::UpdateAbandonTime(uint32 questID, uint32 characterLowGuid) void QuestTracker::UpdateGMComplete(uint32 questID, uint32 characterLowGuid) { - if (CONF_GET_BOOL("QuestTracker.Queue.Enable")) + if (sWorld->getBoolConfig(CONFIG_QUEST_TRACKER_QUEUE_ENABLE)) _questGMCompleteStore.emplace_back(questID, characterLowGuid); else { diff --git a/src/server/game/Quests/QuestTracker.h b/src/server/game/Quests/QuestTracker.h index 10563f2d681297..52dc6b11f6a8ca 100644 --- a/src/server/game/Quests/QuestTracker.h +++ b/src/server/game/Quests/QuestTracker.h @@ -27,6 +27,7 @@ class QuestTracker void InitSystem(); void Update(uint32 diff); + void SetExecuteDelay(); void Execute(); void Add(uint32 questID, uint32 characterLowGuid, std::string const& coreHash, std::string const& coreRevision); diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index a29ee3c7506eb5..c1c2c2faeedf17 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -146,7 +146,6 @@ enum WorldBoolConfigs CONFIG_DONT_CACHE_RANDOM_MOVEMENT_PATHS, // pussywizard CONFIG_QUEST_IGNORE_AUTO_ACCEPT, CONFIG_QUEST_IGNORE_AUTO_COMPLETE, - CONFIG_QUEST_ENABLE_QUEST_TRACKER, CONFIG_WARDEN_ENABLED, CONFIG_ENABLE_CONTINENT_TRANSPORT, CONFIG_ENABLE_CONTINENT_TRANSPORT_PRELOADING, @@ -164,6 +163,8 @@ enum WorldBoolConfigs CONFIG_SET_ALL_CREATURES_WITH_WAYPOINT_MOVEMENT_ACTIVE, CONFIG_DEBUG_BATTLEGROUND, CONFIG_DEBUG_ARENA, + CONFIG_QUEST_TRACKER_ENABLE, + CONFIG_QUEST_TRACKER_QUEUE_ENABLE, BOOL_CONFIG_VALUE_COUNT }; @@ -370,6 +371,7 @@ enum WorldIntConfigs CONFIG_GUILD_BANK_TAB_COST_4, CONFIG_GUILD_BANK_TAB_COST_5, CONFIG_GM_LEVEL_CHANNEL_MODERATION, + CONFIG_QUEST_TRACKER_QUEUE_DELAY, INT_CONFIG_VALUE_COUNT }; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 183ad0e6eb1122..e5b49c0b37be2a 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1042,8 +1042,6 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_WORLD_BOSS_LEVEL_DIFF] = sConfigMgr->GetIntDefault("WorldBossLevelDiff", 3); - m_bool_configs[CONFIG_QUEST_ENABLE_QUEST_TRACKER] = sConfigMgr->GetBoolDefault("Quests.EnableQuestTracker", false); - // note: disable value (-1) will assigned as 0xFFFFFFF, to prevent overflow at calculations limit it to max possible player level MAX_LEVEL(100) m_int_configs[CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF] = sConfigMgr->GetIntDefault("Quests.LowLevelHideDiff", 4); if (m_int_configs[CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF] > MAX_LEVEL) @@ -1411,6 +1409,11 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_GM_LEVEL_CHANNEL_MODERATION] = sConfigMgr->GetIntDefault("Channel.ModerationGMLevel", 1); + // Quest Tracker + m_bool_configs[CONFIG_QUEST_TRACKER_ENABLE] = sConfigMgr->GetBoolDefault("QuestTracker.Enable", false); + m_bool_configs[CONFIG_QUEST_TRACKER_QUEUE_ENABLE] = sConfigMgr->GetBoolDefault("QuestTracker.Queue.Enable", false); + m_bool_configs[CONFIG_QUEST_TRACKER_QUEUE_DELAY] = sConfigMgr->GetBoolDefault("QuestTracker.Queue.Delay", 10); + // call ScriptMgr if we're reloading the configuration sScriptMgr->OnAfterConfigLoad(reload); } diff --git a/src/server/scripts/Commands/cs_quest.cpp b/src/server/scripts/Commands/cs_quest.cpp index 8b61d41a9203c4..d9477a41921c56 100644 --- a/src/server/scripts/Commands/cs_quest.cpp +++ b/src/server/scripts/Commands/cs_quest.cpp @@ -233,7 +233,7 @@ class quest_commandscript : public CommandScript player->ModifyMoney(-ReqOrRewMoney); // check if Quest Tracker is enabled - if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) + if (sWorld->getBoolConfig(CONFIG_QUEST_TRACKER_ENABLE)) { // add to Quest Tracker sQuestTracker->UpdateGMComplete(quest->GetQuestId(), player->GetGUIDLow()); diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 71443bb40280a4..7a1b50e6c9b9bd 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -1302,12 +1302,24 @@ Instance.ResetTimeHour = 4 Instance.UnloadDelay = 1800000 # -# Quests.EnableQuestTracker +# QuestTracker.Enable # Description: Store data in the database about quest completion and abandonment to help finding bugged quests. # Default: 0 - (Disabled) # 1 - (Enabled) +# +# QuestTracker.Queue.Enable +# Description: Enable queue +# Default: 0 - (Disabled) +# 1 - (Enabled) +# +# QuestTracker.Queue.Delay +# Description: Delay for update queue in seconds. 1 seconds minimun +# Default: 10 +# -Quests.EnableQuestTracker = 0 +QuestTracker.Enable = 1 +QuestTracker.Queue.Enable = 0 +QuestTracker.Queue.Delay = 10 # # Quests.LowLevelHideDiff From 1e23325df472426dc868f5e73e6cca0fc6aa6716 Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Fri, 29 Jan 2021 16:09:42 +0700 Subject: [PATCH 13/19] disable default --- src/server/worldserver/worldserver.conf.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 7a1b50e6c9b9bd..3d94f08291ea5e 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -1317,7 +1317,7 @@ Instance.UnloadDelay = 1800000 # Default: 10 # -QuestTracker.Enable = 1 +QuestTracker.Enable = 0 QuestTracker.Queue.Enable = 0 QuestTracker.Queue.Delay = 10 From 4fe27c45693feda0f03fdc275366ee3251a50c29 Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Fri, 29 Jan 2021 16:17:23 +0700 Subject: [PATCH 14/19] copyright --- src/common/Utilities/TaskScheduler.cpp | 16 ++-------------- src/common/Utilities/TaskScheduler.h | 16 ++-------------- src/server/game/Quests/QuestTracker.cpp | 16 ++-------------- src/server/game/Quests/QuestTracker.h | 16 ++-------------- 4 files changed, 8 insertions(+), 56 deletions(-) diff --git a/src/common/Utilities/TaskScheduler.cpp b/src/common/Utilities/TaskScheduler.cpp index 8739d8e6b7d1dc..7a443cbe6d974f 100644 --- a/src/common/Utilities/TaskScheduler.cpp +++ b/src/common/Utilities/TaskScheduler.cpp @@ -1,18 +1,6 @@ /* - * This file is part of the WarheadCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2008+ TrinityCore */ #include "TaskScheduler.h" diff --git a/src/common/Utilities/TaskScheduler.h b/src/common/Utilities/TaskScheduler.h index 0c1b1764ecdc37..6c8d8a60465236 100644 --- a/src/common/Utilities/TaskScheduler.h +++ b/src/common/Utilities/TaskScheduler.h @@ -1,18 +1,6 @@ /* - * This file is part of the WarheadCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2008+ TrinityCore */ #ifndef _TASK_SCHEDULER_H_ diff --git a/src/server/game/Quests/QuestTracker.cpp b/src/server/game/Quests/QuestTracker.cpp index 0b054e931ae942..4c082db7014b2d 100644 --- a/src/server/game/Quests/QuestTracker.cpp +++ b/src/server/game/Quests/QuestTracker.cpp @@ -1,18 +1,6 @@ /* - * This file is part of the WarheadCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021 WarheadCore . See AUTHORS file for Copyright information */ #include "QuestTracker.h" diff --git a/src/server/game/Quests/QuestTracker.h b/src/server/game/Quests/QuestTracker.h index 52dc6b11f6a8ca..a2cf8ce6da6e3b 100644 --- a/src/server/game/Quests/QuestTracker.h +++ b/src/server/game/Quests/QuestTracker.h @@ -1,18 +1,6 @@ /* - * This file is part of the WarheadCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2020+ WarheadCore */ #ifndef _QUEST_TRACKER_H From 4d76ed670297bfe664aed7b0fb78f2c0b93b572f Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Fri, 29 Jan 2021 16:18:09 +0700 Subject: [PATCH 15/19] + --- src/server/game/Quests/QuestTracker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Quests/QuestTracker.cpp b/src/server/game/Quests/QuestTracker.cpp index 4c082db7014b2d..0abbf7a39d3676 100644 --- a/src/server/game/Quests/QuestTracker.cpp +++ b/src/server/game/Quests/QuestTracker.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 - * Copyright (C) 2021 WarheadCore . See AUTHORS file for Copyright information + * Copyright (C) 2021 WarheadCore */ #include "QuestTracker.h" From 023831f20c0e7734b525d6e6bb08497d568225a9 Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Fri, 29 Jan 2021 16:54:56 +0700 Subject: [PATCH 16/19] to int --- src/server/game/World/World.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index e5b49c0b37be2a..2774a7e4f4f4d3 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1412,7 +1412,7 @@ void World::LoadConfigSettings(bool reload) // Quest Tracker m_bool_configs[CONFIG_QUEST_TRACKER_ENABLE] = sConfigMgr->GetBoolDefault("QuestTracker.Enable", false); m_bool_configs[CONFIG_QUEST_TRACKER_QUEUE_ENABLE] = sConfigMgr->GetBoolDefault("QuestTracker.Queue.Enable", false); - m_bool_configs[CONFIG_QUEST_TRACKER_QUEUE_DELAY] = sConfigMgr->GetBoolDefault("QuestTracker.Queue.Delay", 10); + m_int_configs[CONFIG_QUEST_TRACKER_QUEUE_DELAY] = sConfigMgr->GetIntDefault("QuestTracker.Queue.Delay", 10); // call ScriptMgr if we're reloading the configuration sScriptMgr->OnAfterConfigLoad(reload); From dd5d5daee8d41aae913baef812952657e0921f2b Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Mon, 22 Feb 2021 21:58:15 +0700 Subject: [PATCH 17/19] Improve --- src/server/game/Quests/QuestTracker.cpp | 2 +- src/server/game/World/World.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/game/Quests/QuestTracker.cpp b/src/server/game/Quests/QuestTracker.cpp index 0abbf7a39d3676..7160f6c1993f76 100644 --- a/src/server/game/Quests/QuestTracker.cpp +++ b/src/server/game/Quests/QuestTracker.cpp @@ -37,7 +37,7 @@ void QuestTracker::InitSystem() { if (!sWorld->getBoolConfig(CONFIG_QUEST_TRACKER_ENABLE)) { - sLog->outString(">> System disabled"); + sLog->outString(">> The QuestTracker System is disabled"); return; } diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 2e9e9e3a12189e..7f0648f21789f6 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -2053,7 +2053,7 @@ void World::SetInitialWorldSettings() mgr = ChannelMgr::forTeam(TEAM_HORDE); mgr->LoadChannels(); - sLog->outString("Start QuestTracker system..."); + sLog->outString("Initialising QuestTracker system..."); sQuestTracker->InitSystem(); #ifdef ELUNA From 1ecf4ce7bd0c5191d5e5e3f01262e651a42bbe69 Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Sun, 28 Feb 2021 19:45:17 +0700 Subject: [PATCH 18/19] includes --- src/server/game/Handlers/QuestHandler.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index 6010b2eca4b776..f35d3e8800f182 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -18,6 +18,9 @@ #include "Player.h" #include "QuestDef.h" #include "ScriptMgr.h" +#include "World.h" +#include "WorldPacket.h" +#include "WorldSession.h" #include "QuestTracker.h" #ifdef ELUNA From a2a45b2e5eef96d8df03ee82ad4f9749b37c69a4 Mon Sep 17 00:00:00 2001 From: Winfidonarleyan Date: Sun, 28 Feb 2021 20:39:27 +0700 Subject: [PATCH 19/19] new config api --- src/server/game/World/World.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 3d5def51cdc89f..7e0223152a1624 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1413,9 +1413,9 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_GM_LEVEL_CHANNEL_MODERATION] = sConfigMgr->GetOption("Channel.ModerationGMLevel", 1); // Quest Tracker - m_bool_configs[CONFIG_QUEST_TRACKER_ENABLE] = sConfigMgr->GetBoolDefault("QuestTracker.Enable", false); - m_bool_configs[CONFIG_QUEST_TRACKER_QUEUE_ENABLE] = sConfigMgr->GetBoolDefault("QuestTracker.Queue.Enable", false); - m_int_configs[CONFIG_QUEST_TRACKER_QUEUE_DELAY] = sConfigMgr->GetIntDefault("QuestTracker.Queue.Delay", 10); + m_bool_configs[CONFIG_QUEST_TRACKER_ENABLE] = sConfigMgr->GetOption("QuestTracker.Enable", false); + m_bool_configs[CONFIG_QUEST_TRACKER_QUEUE_ENABLE] = sConfigMgr->GetOption("QuestTracker.Queue.Enable", false); + m_int_configs[CONFIG_QUEST_TRACKER_QUEUE_DELAY] = sConfigMgr->GetOption("QuestTracker.Queue.Delay", 10); // call ScriptMgr if we're reloading the configuration sScriptMgr->OnAfterConfigLoad(reload);