#pragma once #include "Resource/AbstractConfigerWithTempl.h" #include #include #include #include "Utils/AsstTypes.h" #ifdef ASST_DEBUG #include "Utils/Logger.hpp" #endif namespace asst { struct TaskInfo; class TaskData final : public SingletonHolder, public AbstractConfigerWithTempl { private: using tasklist_t = std::vector; using taskptr_t = std::shared_ptr; std::shared_ptr _default_match_task_info(); std::shared_ptr _default_ocr_task_info(); std::shared_ptr _default_hash_task_info(); taskptr_t _default_task_info(); // 从模板任务生成 const std::shared_ptr default_match_task_info_ptr = _default_match_task_info(); const std::shared_ptr default_ocr_task_info_ptr = _default_ocr_task_info(); const std::shared_ptr default_hash_task_info_ptr = _default_hash_task_info(); const taskptr_t default_task_info_ptr = _default_task_info(); // 这是下面几个函数的封装 taskptr_t generate_task_info(const std::string& name, const json::value&, taskptr_t default_ptr, std::string_view task_prefix = ""); taskptr_t generate_match_task_info(const std::string& name, const json::value&, std::shared_ptr default_ptr); taskptr_t generate_ocr_task_info(const std::string& name, const json::value&, std::shared_ptr default_ptr); taskptr_t generate_hash_task_info(const std::string& name, const json::value&, std::shared_ptr default_ptr); // TaskInfo 的基础成员 bool append_base_task_info(taskptr_t task_info_ptr, const std::string& name, const json::value& task_json, taskptr_t default_ptr, std::string_view task_prefix = ""); static std::string append_prefix(const std::string& task_name, std::string_view task_prefix) { if (task_prefix.ends_with('@')) [[unlikely]] { task_prefix.remove_suffix(1); } if (task_prefix.empty()) [[unlikely]] { return task_name; } if (task_name.empty()) { return std::string(task_prefix); } if (task_name.starts_with('#')) { return std::string(task_prefix) + task_name; } return std::string(task_prefix) + '@' + task_name; } static tasklist_t append_prefix(const tasklist_t& base_task_list, std::string_view task_prefix_view) { if (task_prefix_view.ends_with('@')) [[unlikely]] { task_prefix_view.remove_suffix(1); } if (task_prefix_view.empty()) { return base_task_list; } std::string task_prefix(task_prefix_view); tasklist_t task_list = {}; for (const std::string& base : base_task_list) { std::string_view base_view = base; size_t l = 0; bool has_same_prefix = false; // base 任务里已经存在相同前缀,则不加前缀 // https://github.com/MaaAssistantArknights/MaaAssistantArknights/pull/2116#issuecomment-1270115238 for (size_t r = base_view.find('@'); r != std::string_view::npos; r = base_view.find('@', l)) { if (task_prefix_view == base_view.substr(l, r - l)) [[unlikely]] { has_same_prefix = true; break; } l = r + 1; } if (has_same_prefix) [[unlikely]] { task_list.emplace_back(base); continue; } task_list.emplace_back(append_prefix(base, task_prefix)); } return task_list; }; // 运行时动态生成任务 static taskptr_t _generate_task_info(const taskptr_t& base_ptr, std::string_view task_prefix = "") { taskptr_t task_info_ptr; switch (base_ptr->algorithm) { case AlgorithmType::MatchTemplate: task_info_ptr = std::make_shared(*std::dynamic_pointer_cast(base_ptr)); break; case AlgorithmType::OcrDetect: task_info_ptr = std::make_shared(*std::dynamic_pointer_cast(base_ptr)); break; case AlgorithmType::Hash: task_info_ptr = std::make_shared(*std::dynamic_pointer_cast(base_ptr)); break; default: task_info_ptr = std::make_shared(*base_ptr); break; } if (!task_prefix.empty()) { task_info_ptr->name = append_prefix(base_ptr->name, task_prefix); task_info_ptr->sub = append_prefix(base_ptr->sub, task_prefix); task_info_ptr->next = append_prefix(base_ptr->next, task_prefix); task_info_ptr->exceeded_next = append_prefix(base_ptr->exceeded_next, task_prefix); task_info_ptr->on_error_next = append_prefix(base_ptr->on_error_next, task_prefix); task_info_ptr->reduce_other_times = append_prefix(base_ptr->reduce_other_times, task_prefix); } return task_info_ptr; } template requires(!std::same_as, std::string> && requires { std::declval>().as_string(); }) static tasklist_t to_string_list(const ListType& other_string_list) { tasklist_t task_list = {}; task_list.reserve(other_string_list.size()); ranges::copy(other_string_list | views::transform(&ranges::range_value_t::as_string), std::back_inserter(task_list)); return task_list; } std::string_view task_name_view(std::string_view task_name) { return *m_task_names.emplace(task_name).first; } decltype(auto) insert_or_assign_raw_task(std::string_view task_name, taskptr_t task_info_ptr) { return m_raw_all_tasks_info.insert_or_assign(task_name_view(task_name), task_info_ptr); } decltype(auto) insert_or_assign_task(std::string_view task_name, taskptr_t task_info_ptr) { return m_all_tasks_info.insert_or_assign(task_name_view(task_name), task_info_ptr); } std::optional expend_sharp_task(std::string_view name, taskptr_t old_task); #ifdef ASST_DEBUG bool syntax_check(const std::string& task_name, const json::value& task_json); #endif template requires(std::derived_from || std::same_as) // Parameter must be a TaskInfo std::shared_ptr get_raw(std::string_view name) const { // 普通 task 或已经生成过的 `@` 型 task if (auto it = m_raw_all_tasks_info.find(name); it != m_raw_all_tasks_info.cend()) [[likely]] { if constexpr (std::same_as) { return it->second; } else { return std::dynamic_pointer_cast(it->second); } } size_t at_pos = name.find('@'); if (at_pos == std::string_view::npos) [[unlikely]] { return nullptr; } // `@` 前面的字符长度 size_t name_len = at_pos; auto base_task_iter = get_raw(name.substr(name_len + 1)); if (base_task_iter == nullptr) [[unlikely]] { return nullptr; } std::string_view derived_task_name = name.substr(0, name_len); auto task_info_ptr = _generate_task_info(base_task_iter, derived_task_name); if (task_info_ptr == nullptr) [[unlikely]] { return nullptr; } if constexpr (std::same_as) { return task_info_ptr; } else { return std::dynamic_pointer_cast(task_info_ptr); } } public: virtual ~TaskData() override = default; virtual const std::unordered_set& get_templ_required() const noexcept override; template requires(std::derived_from || std::same_as) // Parameter must be a TaskInfo std::shared_ptr get(std::string_view name) { // 普通 task 或已经生成过的 `@` 型 task if (auto it = m_all_tasks_info.find(name); it != m_all_tasks_info.cend()) [[likely]] { if constexpr (std::same_as) { return it->second; } else { return std::dynamic_pointer_cast(it->second); } } auto task_info_ptr = expend_sharp_task(name, get_raw(name)).value_or(nullptr); if constexpr (std::same_as) { return task_info_ptr; } else { return std::dynamic_pointer_cast(task_info_ptr); } } protected: virtual bool parse(const json::value& json) override; std::unordered_set m_task_names; std::unordered_map m_raw_all_tasks_info; std::unordered_map m_all_tasks_info; std::unordered_set m_templ_required; }; inline static auto& Task = TaskData::get_instance(); } // namespace asst