diff --git a/src/MaaCore/Common/AsstTypes.h b/src/MaaCore/Common/AsstTypes.h index d7b708e378..17465ade2c 100644 --- a/src/MaaCore/Common/AsstTypes.h +++ b/src/MaaCore/Common/AsstTypes.h @@ -335,106 +335,53 @@ namespace asst } return "Invalid"; } - - enum class TaskDerivedType - { - Invalid = -1, - Raw, // 原始任务 - BaseTask, // BaseTask - Implicit, // 隐式 TemplateTask - Template, // 显式 TemplateTask - }; - - inline std::string enum_to_string(TaskDerivedType task_derived_type) - { - static const std::unordered_map task_derived_type_map = { - { TaskDerivedType::Raw, "Raw" }, - { TaskDerivedType::BaseTask, "BaseTask" }, - { TaskDerivedType::Implicit, "Implicit" }, - { TaskDerivedType::Template, "Template" }, - }; - if (auto it = task_derived_type_map.find(task_derived_type); it != task_derived_type_map.end()) { - return it->second; - } - return "Invalid"; - } } // namespace asst namespace asst { - using TaskList = std::vector; - - // 任务流程信息 - struct TaskPipelineInfo - { - constexpr TaskPipelineInfo() = default; - constexpr virtual ~TaskPipelineInfo() = default; - constexpr TaskPipelineInfo(const TaskPipelineInfo&) = default; - constexpr TaskPipelineInfo(TaskPipelineInfo&&) noexcept = default; - constexpr TaskPipelineInfo& operator=(const TaskPipelineInfo&) = default; - constexpr TaskPipelineInfo& operator=(TaskPipelineInfo&&) noexcept = default; - std::string name; // 任务名 - TaskList next; // 下一个可能的任务(列表) - TaskList sub; // 子任务(列表) - TaskList on_error_next; // 任务出错之后要去执行什么 - TaskList exceeded_next; // 达到最多次数了之后,下一个可能的任务(列表) - TaskList reduce_other_times; // 执行了该任务后,需要减少别的任务的执行次数。例如执行了吃理智药, - // 则说明上一次点击蓝色开始行动按钮没生效,所以蓝色开始行动要-1 - }; - using TaskPipelinePtr = std::shared_ptr; - using TaskPipelineConstPtr = std::shared_ptr; - - // 任务继承信息 - struct TaskDerivedInfo : public TaskPipelineInfo - { - constexpr TaskDerivedInfo() = default; - constexpr virtual ~TaskDerivedInfo() = default; - constexpr TaskDerivedInfo(const TaskDerivedInfo&) = default; - constexpr TaskDerivedInfo(TaskDerivedInfo&&) noexcept = default; - constexpr TaskDerivedInfo& operator=(const TaskDerivedInfo&) = default; - constexpr TaskDerivedInfo& operator=(TaskDerivedInfo&&) noexcept = default; - TaskDerivedType type = TaskDerivedType::Raw; // 任务类型 - std::string base = ""; // 继承自哪个任务(Raw 任务不需要) - std::string prefix = ""; // 需要添加的前缀(仅对 TemplateTask 生效) - }; - using TaskDerivedPtr = std::shared_ptr; - using TaskDerivedConstPtr = std::shared_ptr; - // 任务信息 - struct TaskInfo : public TaskPipelineInfo + struct TaskInfo { - constexpr TaskInfo() = default; - constexpr virtual ~TaskInfo() = default; - constexpr TaskInfo(const TaskInfo&) = default; - constexpr TaskInfo(TaskInfo&&) noexcept = default; - constexpr TaskInfo& operator=(const TaskInfo&) = default; - constexpr TaskInfo& operator=(TaskInfo&&) noexcept = default; - AlgorithmType algorithm = AlgorithmType::Invalid; // 图像算法类型 - ProcessTaskAction action = ProcessTaskAction::Invalid; // 要进行的操作 - bool sub_error_ignored = false; // 子任务如果失败了,是否继续执行剩下的任务 - int max_times = INT_MAX; // 任务最多执行多少次 - Rect specific_rect; // 指定区域,目前仅针对ClickRect任务有用,会点这个区域 - int pre_delay = 0; // 执行该任务前的延时 - int post_delay = 0; // 执行该任务后的延时 - int retry_times = INT_MAX; // 未找到图像时的重试次数 - Rect roi; // 要识别的区域,若为0则全图识别 + TaskInfo() = default; + virtual ~TaskInfo() = default; + TaskInfo(const TaskInfo&) = default; + TaskInfo(TaskInfo&&) noexcept = default; + TaskInfo& operator=(const TaskInfo&) = default; + TaskInfo& operator=(TaskInfo&&) noexcept = default; + std::string name; // 任务名 + AlgorithmType algorithm = // 图像算法类型 + AlgorithmType::Invalid; + ProcessTaskAction action = // 要进行的操作 + ProcessTaskAction::Invalid; + std::vector sub; // 子任务(列表) + bool sub_error_ignored = false; // 子任务如果失败了,是否继续执行剩下的任务 + std::vector next; // 下一个可能的任务(列表) + int max_times = INT_MAX; // 任务最多执行多少次 + std::vector exceeded_next; // 达到最多次数了之后,下一个可能的任务(列表) + std::vector on_error_next; // 任务出错之后要去执行什么 + std::vector reduce_other_times; // 执行了该任务后,需要减少别的任务的执行次数。 + // 例如执行了吃理智药,则说明上一次点击蓝色开始行动按钮没生效, + // 所以蓝色开始行动要-1 + Rect specific_rect; // 指定区域,目前仅针对ClickRect任务有用,会点这个区域 + int pre_delay = 0; // 执行该任务前的延时 + int post_delay = 0; // 执行该任务后的延时 + int retry_times = INT_MAX; // 未找到图像时的重试次数 + Rect roi; // 要识别的区域,若为0则全图识别 Rect rect_move; // 识别结果移动:有些结果识别到的,和要点击的不是同一个位置。 // 即识别到了res,点击res + result_move的位置 bool cache = false; // 是否使用缓存区域 std::vector special_params; // 某些任务会用到的特殊参数 }; - using TaskPtr = std::shared_ptr; - using TaskConstPtr = std::shared_ptr; // 文字识别任务的信息 struct OcrTaskInfo : public TaskInfo { - constexpr OcrTaskInfo() = default; - constexpr virtual ~OcrTaskInfo() override = default; - constexpr OcrTaskInfo(const OcrTaskInfo&) = default; - constexpr OcrTaskInfo(OcrTaskInfo&&) noexcept = default; - constexpr OcrTaskInfo& operator=(const OcrTaskInfo&) = default; - constexpr OcrTaskInfo& operator=(OcrTaskInfo&&) noexcept = default; + OcrTaskInfo() = default; + virtual ~OcrTaskInfo() override = default; + OcrTaskInfo(const OcrTaskInfo&) = default; + OcrTaskInfo(OcrTaskInfo&&) noexcept = default; + OcrTaskInfo& operator=(const OcrTaskInfo&) = default; + OcrTaskInfo& operator=(OcrTaskInfo&&) noexcept = default; std::vector text; // 文字的容器,匹配到这里面任一个,就算匹配上了 bool full_match = false; // 是否需要全匹配,否则搜索到子串就算匹配上了 bool is_ascii = false; // 是否启用字符数字模型 @@ -443,41 +390,35 @@ namespace asst std::vector> replace_map; // 部分文字容易识别错,字符串强制replace之后,再进行匹配 }; - using OcrTaskPtr = std::shared_ptr; - using OcrTaskConstPtr = std::shared_ptr; // 图片匹配任务的信息 struct MatchTaskInfo : public TaskInfo { - constexpr MatchTaskInfo() = default; - constexpr virtual ~MatchTaskInfo() override = default; - constexpr MatchTaskInfo(const MatchTaskInfo&) = default; - constexpr MatchTaskInfo(MatchTaskInfo&&) noexcept = default; - constexpr MatchTaskInfo& operator=(const MatchTaskInfo&) = default; - constexpr MatchTaskInfo& operator=(MatchTaskInfo&&) noexcept = default; + MatchTaskInfo() = default; + virtual ~MatchTaskInfo() override = default; + MatchTaskInfo(const MatchTaskInfo&) = default; + MatchTaskInfo(MatchTaskInfo&&) noexcept = default; + MatchTaskInfo& operator=(const MatchTaskInfo&) = default; + MatchTaskInfo& operator=(MatchTaskInfo&&) noexcept = default; std::vector templ_names; // 匹配模板图片文件名 std::vector templ_thresholds; // 模板匹配阈值 std::pair mask_range; // 掩码的二值化范围 }; - using MatchTaskPtr = std::shared_ptr; - using MatchTaskConstPtr = std::shared_ptr; // hash 计算任务的信息 struct HashTaskInfo : public TaskInfo { - constexpr HashTaskInfo() = default; - constexpr virtual ~HashTaskInfo() override = default; - constexpr HashTaskInfo(const HashTaskInfo&) = default; - constexpr HashTaskInfo(HashTaskInfo&&) noexcept = default; - constexpr HashTaskInfo& operator=(const HashTaskInfo&) = default; - constexpr HashTaskInfo& operator=(HashTaskInfo&&) noexcept = default; + HashTaskInfo() = default; + virtual ~HashTaskInfo() override = default; + HashTaskInfo(const HashTaskInfo&) = default; + HashTaskInfo(HashTaskInfo&&) noexcept = default; + HashTaskInfo& operator=(const HashTaskInfo&) = default; + HashTaskInfo& operator=(HashTaskInfo&&) noexcept = default; std::vector hashes; // 需要多个哈希值 int dist_threshold = 0; // 汉明距离阈值 std::pair mask_range; // 掩码的二值化范围 bool bound = false; // 是否裁剪周围黑边 }; - using HashTaskPtr = std::shared_ptr; - using HashTaskConstPtr = std::shared_ptr; inline static const std::string UploadDataSource = "MeoAssistant"; } // namespace asst diff --git a/src/MaaCore/Config/TaskData.cpp b/src/MaaCore/Config/TaskData.cpp index 5b67879650..84e85815d5 100644 --- a/src/MaaCore/Config/TaskData.cpp +++ b/src/MaaCore/Config/TaskData.cpp @@ -9,8 +9,6 @@ #include "Common/AsstTypes.h" #include "GeneralConfig.h" -#include "TaskData/TaskDataSymbolStream.h" -#include "TaskData/TaskDataTypes.h" #include "TemplResource.h" #include "Utils/JsonMisc.hpp" #include "Utils/Logger.hpp" @@ -21,9 +19,9 @@ const std::unordered_set& asst::TaskData::get_templ_required() cons { return m_templ_required; } -asst::TaskDerivedConstPtr asst::TaskData::get_raw(std::string_view name) +asst::TaskData::taskptr_t asst::TaskData::get_raw(std::string_view name) { - if (!generate_raw_task_and_base(name, true)) [[unlikely]] { + if (!generate_task_and_its_base(name, true)) [[unlikely]] { return nullptr; } @@ -37,27 +35,32 @@ asst::TaskDerivedConstPtr asst::TaskData::get_raw(std::string_view name) return nullptr; } + // 隐式 TemplateTask + if (auto base_task_iter = get_raw(name.substr(name_len + 1))) { + return _generate_task_info(base_task_iter, name.substr(0, name_len)); + } + return nullptr; } -asst::TaskPtr asst::TaskData::get(std::string_view name) +asst::TaskData::taskptr_t asst::TaskData::get(std::string_view name) { // 普通任务 或 已经生成过的高级任务 - if (auto it = m_all_tasks_info.find(name); it != m_all_tasks_info.cend()) { + if (auto it = m_all_tasks_info.find(name); it != m_all_tasks_info.cend()) [[likely]] { return it->second; } - auto task = generate_task_info(name); + auto raw_task = get_raw(name); + auto task = _generate_task_info(raw_task); if (!task) [[unlikely]] { return nullptr; } -#define ASST_TASKDATA_GET_IF_BRANCH(list, m) \ - if (auto opt = compile_tasklist(task->list, name, m); !opt) [[unlikely]] { \ - Log.error("Generate task_list", std::string(name) + "->" #list, "failed.", opt.error()); \ - return nullptr; \ - } \ - else { \ - task->list = std::move(opt.value().tasks); \ + bool changed = false; // 任务列表是否发生变化(是否包含虚任务) +#define ASST_TASKDATA_GET_IF_BRANCH(list, m) \ + task->list.clear(); \ + if (!compile_tasklist(task->list, raw_task->list, name, changed, m)) [[unlikely]] { \ + Log.error("Generate task_list", std::string(name) + "->" #list, "failed."); \ + return nullptr; \ } // 展开五个任务列表中的虚任务 ASST_TASKDATA_GET_IF_BRANCH(next, false); @@ -70,12 +73,12 @@ asst::TaskPtr asst::TaskData::get(std::string_view name) constexpr size_t MAX_TASKS_SIZE = 65535; if (m_all_tasks_info.size() < MAX_TASKS_SIZE) [[likely]] { // 保存最终生成的任务,下次查询时可以直接返回 - return insert_or_assign_task(name, task).first->second; + return insert_or_assign_task(name, changed ? task : raw_task).first->second; } else { // 个数超过上限时不保存,直接返回,防止内存占用过大 Log.warn("Task count has exceeded the upper limit:", MAX_TASKS_SIZE, "current task:", name); - return task; + return changed ? task : raw_task; } } @@ -141,7 +144,7 @@ bool asst::TaskData::lazy_parse(const json::value& json) validity = false; continue; } - auto check_tasklist = [&](const TaskList& task_list, std::string_view list_type, + auto check_tasklist = [&](const tasklist_t& task_list, std::string_view list_type, bool enable_justreturn_check = false) { std::unordered_set tasks_set {}; std::string justreturn_task_name = ""; @@ -157,7 +160,7 @@ bool asst::TaskData::lazy_parse(const json::value& json) validity = false; } - if (auto ptr = get(task_name); ptr == nullptr) [[unlikely]] { + if (auto ptr = get_raw(task_name); ptr == nullptr) [[unlikely]] { Log.error(task_name, "in", (std::string(name) += "->") += list_type, "is null"); validity = false; continue; @@ -209,7 +212,7 @@ bool asst::TaskData::parse(const json::value& json) // 本来重构之后完全支持惰性加载,但是发现模板图片不支持( for (std::string_view name : m_json_all_tasks_info | views::keys) { - generate_task_info(name); + generate_task_and_its_base(name, true); } return true; @@ -232,37 +235,19 @@ void asst::TaskData::set_task_base(const std::string_view task_name, std::string clear_tasks(); } -bool asst::TaskData::generate_raw_task_info(std::string_view name, std::string_view prefix, std::string_view base, - const json::value& json, TaskDerivedType type) -{ - TaskPipelineConstPtr base_task = base.empty() ? nullptr : get_raw(base); - if (base_task == nullptr) { - base_task = default_task_info_ptr; - prefix = ""; - } - - TaskDerivedPtr task = std::make_shared(); - task->name = name; - task->type = type; - task->base = base; - task->prefix = prefix; - utils::get_value_or(name, json, "next", task->next, [&]() { return append_prefix(base_task->next, prefix); }); - utils::get_value_or(name, json, "sub", task->sub, [&]() { return append_prefix(base_task->sub, prefix); }); - utils::get_value_or(name, json, "exceededNext", task->exceeded_next, - [&]() { return append_prefix(base_task->exceeded_next, prefix); }); - utils::get_value_or(name, json, "onErrorNext", task->on_error_next, - [&]() { return append_prefix(base_task->on_error_next, prefix); }); - utils::get_value_or(name, json, "reduceOtherTimes", task->reduce_other_times, - [&]() { return append_prefix(base_task->reduce_other_times, prefix); }); - m_task_status[task_name_view(name)] = NotToBeGenerate; - - // 保存虚任务未展开的任务 - insert_or_assign_raw_task(name, task); - return true; -} - -bool asst::TaskData::generate_raw_task_and_base(std::string_view name, bool must_true) +bool asst::TaskData::generate_task_and_its_base(std::string_view name, bool must_true) { + auto generate_task = [&](std::string_view name, std::string_view prefix, taskptr_t base_ptr, + const json::value& task_json) { + auto task_info_ptr = generate_task_info(name, task_json, base_ptr, prefix); + if (task_info_ptr == nullptr) { + return false; + } + m_task_status[task_name_view(name)] = NotToBeGenerate; + // 保存虚任务未展开的任务 + insert_or_assign_raw_task(name, task_info_ptr); + return true; + }; switch (m_task_status[task_name_view(name)]) { case NotToBeGenerate: // 已经显式生成 @@ -272,12 +257,7 @@ bool asst::TaskData::generate_raw_task_and_base(std::string_view name, bool must // 隐式生成的资源 if (size_t p = name.find('@'); p != std::string::npos) { - if (generate_raw_task_and_base(name.substr(p + 1), must_true)) { - // 隐式 TemplateTask - generate_raw_task_info(name, name.substr(0, p), name.substr(p + 1), {}, TaskDerivedType::Implicit); - return true; - } - return false; + return generate_task_and_its_base(name.substr(p + 1), must_true); } m_task_status[name] = NotExists; @@ -303,17 +283,22 @@ bool asst::TaskData::generate_raw_task_and_base(std::string_view name, bool must // BaseTask if (auto opt = task_json.find("baseTask")) { std::string base = opt.value(); - return generate_raw_task_and_base(base, must_true) && - generate_raw_task_info(name, "", base, task_json, TaskDerivedType::BaseTask); + return generate_task_and_its_base(base, must_true) && generate_task(name, "", get_raw(base), task_json); } // TemplateTask if (size_t p = name.find('@'); p != std::string::npos) { - if (std::string_view base = name.substr(p + 1); generate_raw_task_and_base(base, false)) { - return generate_raw_task_info(name, name.substr(0, p), base, task_json, TaskDerivedType::Template); + if (std::string_view base = name.substr(p + 1); generate_task_and_its_base(base, false)) { +#ifdef ASST_DEBUG + if (task_json.as_object().empty() && get_raw(base)->algorithm != AlgorithmType::MatchTemplate) { + // 多余的空任务 + Log.warn("Task", name, "is a redundant empty task because it is not MatchTemplate"); + } +#endif + return generate_task(name, name.substr(0, p), get_raw(base), task_json); } } - return generate_raw_task_info(name, "", "", task_json, TaskDerivedType::Raw); + return generate_task(name, "", nullptr, task_json); } [[unlikely]] case Generating: Log.error("Task", name, "is generated cyclically"); @@ -324,123 +309,59 @@ bool asst::TaskData::generate_raw_task_and_base(std::string_view name, bool must } } -asst::TaskPtr asst::TaskData::generate_task_info(std::string_view name) +asst::TaskData::taskptr_t asst::TaskData::generate_task_info(std::string_view name, const json::value& task_json, + taskptr_t default_ptr, std::string_view task_prefix) { - auto raw = get_raw(name); - if (!raw) [[unlikely]] { - Log.error("Task", name, "not found"); - return nullptr; - } - - auto json_it = m_json_all_tasks_info.find(name); - const json::value& json = json_it == m_json_all_tasks_info.cend() ? json::value {} : json_it->second; - if (raw->type == TaskDerivedType::Raw && json_it == m_json_all_tasks_info.cend()) [[unlikely]] { - Log.error("Task", name, "of type Raw has no json"); - return nullptr; - } - if (raw->type == TaskDerivedType::Raw && !raw->base.empty()) [[unlikely]] { - Log.error("Task", name, "of type Raw has base", raw->base); - return nullptr; - } - if (raw->type != TaskDerivedType::Raw && raw->base.empty()) [[unlikely]] { - Log.error("Task", name, "of type", enum_to_string(raw->type), "has no base"); - return nullptr; - } - if ((raw->type == TaskDerivedType::Implicit || raw->type == TaskDerivedType::Template) && raw->prefix.empty()) - [[unlikely]] { - Log.error("Task", name, "of type", enum_to_string(raw->type), "has no prefix"); - return nullptr; - } - if ((raw->type == TaskDerivedType::Raw || raw->type == TaskDerivedType::BaseTask) && !raw->prefix.empty()) - [[unlikely]] { - Log.error("Task", name, "of type", enum_to_string(raw->type), "has prefix", raw->prefix); - return nullptr; - } - - TaskConstPtr base = default_task_info_ptr; - if (!raw->base.empty()) { - base = get(raw->base); - if (!base) [[unlikely]] { - Log.error("Base task", raw->base, "of task", name, "not found"); - return nullptr; - } + if (default_ptr == nullptr) { + default_ptr = default_task_info_ptr; + task_prefix = ""; } // 获取 algorithm 并按照 algorithm 生成 TaskInfo AlgorithmType algorithm; - utils::get_value_or(name, json, "algorithm", algorithm, base->algorithm); - TaskPtr task = nullptr; + utils::get_value_or(name, task_json, "algorithm", algorithm, default_ptr->algorithm); + + taskptr_t task_ptr = nullptr; switch (algorithm) { case AlgorithmType::MatchTemplate: - task = generate_match_task_info(name, json, std::dynamic_pointer_cast(base), raw->type); + task_ptr = generate_match_task_info(name, task_json, std::dynamic_pointer_cast(default_ptr)); break; case AlgorithmType::OcrDetect: - task = generate_ocr_task_info(name, json, std::dynamic_pointer_cast(base)); + task_ptr = generate_ocr_task_info(name, task_json, std::dynamic_pointer_cast(default_ptr)); break; case AlgorithmType::Hash: - task = generate_hash_task_info(name, json, std::dynamic_pointer_cast(base)); + task_ptr = generate_hash_task_info(name, task_json, std::dynamic_pointer_cast(default_ptr)); break; case AlgorithmType::JustReturn: - task = std::make_shared(); + task_ptr = std::make_shared(); break; default: Log.error("Unknown algorithm in task", name); return nullptr; } - if (task == nullptr) { + if (task_ptr == nullptr) { return nullptr; } - const auto& prefix = raw->prefix; - -#define ASST_TASKDATA_GET_VALUE_OR(key, value) utils::get_value_or(name, json, key, task->value, base->value) -#define ASST_TASKDATA_GET_VALUE_OR_LAZY(key, value) \ - utils::get_value_or(name, json, key, task->value, [&]() { return append_prefix(base->value, prefix); }) - - ASST_TASKDATA_GET_VALUE_OR("action", action); - ASST_TASKDATA_GET_VALUE_OR("cache", cache); - ASST_TASKDATA_GET_VALUE_OR("maxTimes", max_times); - ASST_TASKDATA_GET_VALUE_OR("preDelay", pre_delay); - ASST_TASKDATA_GET_VALUE_OR("postDelay", post_delay); - ASST_TASKDATA_GET_VALUE_OR("roi", roi); - ASST_TASKDATA_GET_VALUE_OR("subErrorIgnored", sub_error_ignored); - ASST_TASKDATA_GET_VALUE_OR("rectMove", rect_move); - ASST_TASKDATA_GET_VALUE_OR("specificRect", specific_rect); - ASST_TASKDATA_GET_VALUE_OR("specialParams", special_params); - ASST_TASKDATA_GET_VALUE_OR_LAZY("next", next); - ASST_TASKDATA_GET_VALUE_OR_LAZY("sub", sub); - ASST_TASKDATA_GET_VALUE_OR_LAZY("exceededNext", exceeded_next); - ASST_TASKDATA_GET_VALUE_OR_LAZY("onErrorNext", on_error_next); - ASST_TASKDATA_GET_VALUE_OR_LAZY("reduceOtherTimes", reduce_other_times); - -#undef ASST_TASKDATA_GET_VALUE_OR -#undef ASST_TASKDATA_GET_VALUE_OR_LAZY - -#ifdef ASST_DEBUG - // Debug 模式下检查 roi 是否超出边界 - if (auto [x, y, w, h] = task->roi; x + w > WindowWidthDefault || y + h > WindowHeightDefault) { - Log.warn(name, "roi is out of bounds"); + // 不管什么algorithm,都有基础成员(next, roi, 等等) + if (!append_base_task_info(task_ptr, name, task_json, default_ptr, task_prefix)) { + return nullptr; } -#endif - task->algorithm = algorithm; - task->name = name; - - return task; + task_ptr->algorithm = algorithm; + task_ptr->name = name; + return task_ptr; } -asst::TaskPtr asst::TaskData::generate_match_task_info(std::string_view name, const json::value& task_json, - MatchTaskConstPtr default_ptr, TaskDerivedType derived_type) +asst::TaskData::taskptr_t asst::TaskData::generate_match_task_info(std::string_view name, const json::value& task_json, + std::shared_ptr default_ptr) { if (default_ptr == nullptr) { default_ptr = default_match_task_info_ptr; } auto match_task_info_ptr = std::make_shared(); - if (!utils::get_value_or(name, task_json, "template", match_task_info_ptr->templ_names, [&]() { - return derived_type == TaskDerivedType::Implicit // 隐式 Template Task 时继承,其它时默认值使用任务名 - ? default_ptr->templ_names - : std::vector { std::string(name) + ".png" }; - })) { + if (!utils::get_value_or(name, task_json, "template", match_task_info_ptr->templ_names, + [&]() { return std::vector { std::string(name) + ".png" }; })) { return nullptr; } @@ -460,7 +381,7 @@ asst::TaskPtr asst::TaskData::generate_match_task_info(std::string_view name, co threshold_opt->as_double()); } else if (threshold_opt->is_array()) { - ranges::copy(threshold_opt->as_array() | views::transform(&json::value::as_double), + ranges::copy(threshold_opt->as_array() | views::transform(&ranges::range_value_t::as_double), std::back_inserter(match_task_info_ptr->templ_thresholds)); } else { @@ -482,8 +403,8 @@ asst::TaskPtr asst::TaskData::generate_match_task_info(std::string_view name, co return match_task_info_ptr; } -asst::TaskPtr asst::TaskData::generate_ocr_task_info(std::string_view name, const json::value& task_json, - OcrTaskConstPtr default_ptr) +asst::TaskData::taskptr_t asst::TaskData::generate_ocr_task_info(std::string_view name, const json::value& task_json, + std::shared_ptr default_ptr) { if (default_ptr == nullptr) { default_ptr = default_ocr_task_info_ptr; @@ -506,8 +427,8 @@ asst::TaskPtr asst::TaskData::generate_ocr_task_info(std::string_view name, cons return ocr_task_info_ptr; } -asst::TaskPtr asst::TaskData::generate_hash_task_info(std::string_view name, const json::value& task_json, - HashTaskConstPtr default_ptr) +asst::TaskData::taskptr_t asst::TaskData::generate_hash_task_info(std::string_view name, const json::value& task_json, + std::shared_ptr default_ptr) { if (default_ptr == nullptr) { default_ptr = default_hash_task_info_ptr; @@ -527,149 +448,44 @@ asst::TaskPtr asst::TaskData::generate_hash_task_info(std::string_view name, con return hash_task_info_ptr; } -asst::ResultOrError asst::TaskData::compile_raw_tasklist( - const TaskList& raw_tasks, std::string_view self_name, std::function get_raw, - bool allow_duplicate) +bool asst::TaskData::append_base_task_info(taskptr_t task_info_ptr, std::string_view name, const json::value& task_json, + taskptr_t default_ptr, std::string_view task_prefix) { - RawCompileResult ret { .task_changed = false, .symbols = {} }; - std::unordered_set tasks_set; // 记录任务列表中已有的任务(内容元素与 new_tasks 基本一致) - - for (std::string_view task_expr : raw_tasks) { - if (task_expr.empty() || (!allow_duplicate && tasks_set.contains(task_expr))) { - ret.task_changed = true; - continue; - } - - TaskDataSymbolStream symbol_stream; - if (auto opt = symbol_stream.parse(task_expr); opt) [[likely]] { - ret.task_changed = ret.task_changed || opt.value(); - } - else { - return { std::nullopt, std::move(opt.error()) }; - } - - auto opt = symbol_stream.decode( - [&](const TaskDataSymbol& symbol, const TaskDataSymbol& prefix) -> TaskDataSymbol::SymbolsOrError { - return TaskDataSymbol::append_prefix( - symbol, prefix, self_name, get_raw, - [&](const TaskList& raw_or_empty) -> TaskDataSymbol::SymbolsOrError { - if (auto opt = compile_raw_tasklist(raw_or_empty, self_name, get_raw, allow_duplicate)) { - return opt.value().symbols; - } - return { std::nullopt, "" }; - }); - }, - self_name); - - // 记号流处理 - if (!opt) { - return { std::nullopt, std::move(opt.error()) }; - } - - for (const auto& task : *opt) { - if (task.name().empty() || (!allow_duplicate && tasks_set.contains(task.name()))) { - ret.task_changed = true; - continue; - } - tasks_set.emplace(ret.symbols.emplace_back(task).name()); - } - - tasks_set.emplace(task_name_view(task_expr)); + if (default_ptr == nullptr) { + default_ptr = default_task_info_ptr; } - return ret; + utils::get_value_or(name, task_json, "action", task_info_ptr->action, default_ptr->action); + utils::get_value_or(name, task_json, "cache", task_info_ptr->cache, default_ptr->cache); + utils::get_value_or(name, task_json, "maxTimes", task_info_ptr->max_times, default_ptr->max_times); + utils::get_value_or(name, task_json, "exceededNext", task_info_ptr->exceeded_next, + [&]() { return append_prefix(default_ptr->exceeded_next, task_prefix); }); + utils::get_value_or(name, task_json, "onErrorNext", task_info_ptr->on_error_next, + [&]() { return append_prefix(default_ptr->on_error_next, task_prefix); }); + utils::get_value_or(name, task_json, "preDelay", task_info_ptr->pre_delay, default_ptr->pre_delay); + utils::get_value_or(name, task_json, "postDelay", task_info_ptr->post_delay, default_ptr->post_delay); + utils::get_value_or(name, task_json, "reduceOtherTimes", task_info_ptr->reduce_other_times, + [&]() { return append_prefix(default_ptr->reduce_other_times, task_prefix); }); + utils::get_value_or(name, task_json, "roi", task_info_ptr->roi, default_ptr->roi); + utils::get_value_or(name, task_json, "sub", task_info_ptr->sub, + [&]() { return append_prefix(default_ptr->sub, task_prefix); }); + utils::get_value_or(name, task_json, "subErrorIgnored", task_info_ptr->sub_error_ignored, + default_ptr->sub_error_ignored); + utils::get_value_or(name, task_json, "next", task_info_ptr->next, + [&]() { return append_prefix(default_ptr->next, task_prefix); }); + utils::get_value_or(name, task_json, "rectMove", task_info_ptr->rect_move, default_ptr->rect_move); + utils::get_value_or(name, task_json, "specificRect", task_info_ptr->specific_rect, default_ptr->specific_rect); + utils::get_value_or(name, task_json, "specialParams", task_info_ptr->special_params, default_ptr->special_params); +#ifdef ASST_DEBUG + // Debug 模式下检查 roi 是否超出边界 + if (auto [x, y, w, h] = task_info_ptr->roi; x + w > WindowWidthDefault || y + h > WindowHeightDefault) { + Log.warn(name, "roi is out of bounds"); + } +#endif + return true; } -asst::ResultOrError asst::TaskData::compile_tasklist(const TaskList& raw_tasks, - std::string_view self_name, - bool allow_duplicate) -{ - CompileResult ret { .task_changed = false, .tasks = {} }; - std::vector new_symbols; - if (auto opt = compile_raw_tasklist( - raw_tasks, self_name, [&](std::string_view name) { return get_raw(name); }, allow_duplicate); - !opt) { - return { std::nullopt, std::move(opt.error()) }; - } - else { - new_symbols = std::move(opt.value().symbols); - ret.task_changed = opt.value().task_changed; - } - std::unordered_set tasks_set; - for (const auto& task : new_symbols) { - std::string_view task_name; - if (task == TaskDataSymbol::SharpSelf) { - task_name = self_name; - ret.task_changed = true; - } - else if (task.is_name()) { - task_name = task.name(); - } - else { - ret.task_changed = true; - continue; - } - if (task_name.empty()) { - Log.error("Empty task name in", self_name); - ret.task_changed = true; - continue; - } - if (!allow_duplicate && tasks_set.contains(task_name)) { - ret.task_changed = true; - continue; - } - ret.tasks.emplace_back(task_name_view(task_name)); - tasks_set.emplace(task_name); - } - - return ret; -} - -std::string asst::TaskData::append_prefix(std::string_view task_name, std::string_view task_prefix) -{ - if (task_prefix.ends_with('@')) [[unlikely]] { - task_prefix.remove_suffix(1); - } - if (task_prefix.empty()) [[unlikely]] { - return std::string(task_name); - } - if (task_name.empty()) { - return std::string(task_prefix); - } - if (task_name.starts_with('#')) { - return std::string(task_prefix) + std::string(task_name); - } - return std::string(task_prefix) + '@' + std::string(task_name); -} - -asst::TaskList asst::TaskData::append_prefix(const TaskList& base_task_list, std::string_view task_prefix) -{ - if (task_prefix.ends_with('@')) [[unlikely]] { - task_prefix.remove_suffix(1); - } - if (task_prefix.empty()) { - return base_task_list; - } - TaskList 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 == base_view.substr(l, r - l)) [[unlikely]] { - has_same_prefix = true; - break; - } - l = r + 1; - } - task_list.emplace_back(has_same_prefix ? base : append_prefix(base_view, task_prefix)); - } - return task_list; -} - -asst::MatchTaskConstPtr asst::TaskData::_default_match_task_info() +std::shared_ptr asst::TaskData::_default_match_task_info() { auto match_task_info_ptr = std::make_shared(); match_task_info_ptr->templ_names = { "__INVALID__" }; @@ -678,7 +494,7 @@ asst::MatchTaskConstPtr asst::TaskData::_default_match_task_info() return match_task_info_ptr; } -asst::OcrTaskConstPtr asst::TaskData::_default_ocr_task_info() +std::shared_ptr asst::TaskData::_default_ocr_task_info() { auto ocr_task_info_ptr = std::make_shared(); ocr_task_info_ptr->full_match = false; @@ -689,7 +505,7 @@ asst::OcrTaskConstPtr asst::TaskData::_default_ocr_task_info() return ocr_task_info_ptr; } -asst::HashTaskConstPtr asst::TaskData::_default_hash_task_info() +std::shared_ptr asst::TaskData::_default_hash_task_info() { auto hash_task_info_ptr = std::make_shared(); hash_task_info_ptr->dist_threshold = 0; @@ -698,7 +514,7 @@ asst::HashTaskConstPtr asst::TaskData::_default_hash_task_info() return hash_task_info_ptr; } -asst::TaskConstPtr asst::TaskData::_default_task_info() +asst::TaskData::taskptr_t asst::TaskData::_default_task_info() { auto task_info_ptr = std::make_shared(); task_info_ptr->algorithm = AlgorithmType::MatchTemplate; @@ -715,6 +531,396 @@ asst::TaskConstPtr asst::TaskData::_default_task_info() return task_info_ptr; } +// new_tasks 是目的任务列表 +// raw_tasks 是源任务表达式列表 +// task_name 是任务名 +// task_changed 记录 raw_tasks 在转换为 new_tasks 时是否发生变化 +// allow_duplicate 是否允许重复 +bool asst::TaskData::compile_tasklist(tasklist_t& new_tasks, const tasklist_t& raw_tasks, std::string_view task_name, + bool& task_changed, bool allow_duplicate) +{ + std::unordered_set tasks_set; // 记录任务列表中已有的任务(内容元素与 new_tasks 基本一致) + + using symbl_t = size_t; + [[maybe_unused]] constexpr symbl_t symbl_end = 0; + [[maybe_unused]] constexpr symbl_t symbl_lambda_task_sep = 1; + [[maybe_unused]] constexpr symbl_t symbl_lparen = 2; + [[maybe_unused]] constexpr symbl_t symbl_rparen = 3; + [[maybe_unused]] constexpr symbl_t symbl_lbrace = 4; + [[maybe_unused]] constexpr symbl_t symbl_rbrace = 5; + [[maybe_unused]] constexpr symbl_t symbl_at = 6; + [[maybe_unused]] constexpr symbl_t symbl_sharp = 7; + [[maybe_unused]] constexpr symbl_t symbl_mul = 8; + [[maybe_unused]] constexpr symbl_t symbl_add = 9; + [[maybe_unused]] constexpr symbl_t symbl_sub = 10; + + [[maybe_unused]] constexpr symbl_t symbl_name_start = 11; + [[maybe_unused]] constexpr symbl_t symbl_name_sub = 11; + [[maybe_unused]] constexpr symbl_t symbl_name_next = 12; + [[maybe_unused]] constexpr symbl_t symbl_name_on_error_next = 13; + [[maybe_unused]] constexpr symbl_t symbl_name_exceeded_next = 14; + [[maybe_unused]] constexpr symbl_t symbl_name_reduce_other_times = 15; + [[maybe_unused]] constexpr symbl_t symbl_name_self = 16; + [[maybe_unused]] constexpr symbl_t symbl_name_back = 17; + [[maybe_unused]] constexpr symbl_t symbl_name_none = 18; + + static const std::vector symbl_table = { + "__END__", // 0 + ",", // 1 + "(", // 2 + ")", // 3 + "{", // 4 + "}", // 5 + "@", // 6 + "#", // 7 + "*", // 8 + "+", // 9 + "^", // 10 + "sub", // 11 + "next", // 12 + "on_error_next", // 13 + "exceeded_next", // 14 + "reduce_other_times", // 15 + "self", // 16 + "back", // 17 + "none", // 18 + }; + + auto is_symbl_name = [&](symbl_t x) { return x >= symbl_name_start; }; + auto is_symbl_subtask_type = [&](symbl_t x) { + switch (x) { + case symbl_name_sub: + case symbl_name_next: + case symbl_name_on_error_next: + case symbl_name_exceeded_next: + case symbl_name_reduce_other_times: + return true; + default: + return false; + } + }; + [[maybe_unused]] auto is_symbl_sharp_type = [&](symbl_t x) { + switch (x) { + case symbl_name_self: + case symbl_name_back: + case symbl_name_none: + return true; + default: + return is_symbl_subtask_type(x); + } + }; + + // perform_op 的结果不保证符合参数 multi 的要求 + auto perform_op = [&](std::string_view task_expr, symbl_t op, tasklistptr_t x, + tasklistptr_t y) -> std::optional { + auto ret = std::make_shared(); + + switch (op) { + case symbl_add: + ranges::copy(*x, std::back_inserter(*ret)); + ranges::copy(*y, std::back_inserter(*ret)); + break; + case symbl_sub: + for (std::string_view sx : *x) { + bool flag = true; + for (std::string_view sy : *y) { + if (sx == sy) { + flag = false; + break; + } + } + if (flag) ret->emplace_back(sx); + } + break; + case symbl_mul: { + if (y->size() != 1) { + Log.error("There is more than one y:", *y, "while perform op", symbl_table[op], "in", task_expr, + "of task:", task_name); + return std::nullopt; + } + int times = 0; + try { + times = std::stoi(y->front()); + } + catch (...) { + Log.error("y:", y->front(), "is not number while perform op", symbl_table[op], "in", task_expr, + "of task:", task_name); + return std::nullopt; + } + for (int i = 0; i < times; ++i) { + ranges::copy(*x, std::back_inserter(*ret)); + } + break; + } + case symbl_at: { + if (y->empty()) { // A@#none = A + ranges::copy(*x, std::back_inserter(*ret)); + } + else if (x->empty()) { // #none@A = A + ranges::copy(*y, std::back_inserter(*ret)); + } + else { // (A+B)@(C+D) = A@C + A@D + B@C + B@D + ranges::for_each( + *x, [&](std::string_view s) { ranges::copy(append_prefix(*y, s), std::back_inserter(*ret)); }); + } + break; + } + case symbl_sharp: { + if (y->empty()) { + Log.error("There is no sharp_type while perform op", symbl_table[op], "in", task_expr, + "of task:", task_name); + return std::nullopt; + } + for (std::string_view type : *y) { + if (!x || x->empty()) { // unary + x = std::make_shared(tasklist_t { "" }); + } + for (const auto& task : *x) { + if (type == "self") { + ret->emplace_back(task_name); + continue; + } + if (type == "none") { + continue; + } + if (type == "back") { + // "A#back" === "A", "B@A#back" === "B@A", "#back" === null + if (!task.empty()) ret->emplace_back(task); + continue; + } + taskptr_t other_task_info_ptr = task.empty() ? default_task_info_ptr : get_raw(task); + +#define ASST_TASKDATA_PERFORM_OP_IF_BRANCH(t, m) \ + else if (type == #t) \ + { \ + bool task_changed = false; \ + if (!compile_tasklist(*ret, other_task_info_ptr->t, task_name, task_changed, m)) { \ + Log.error("Failed to compile task", task + "->" #t, "while perform op", symbl_table[op], "in", task_expr, \ + "of task:", task_name); \ + return std::nullopt; \ + } \ + } + if (other_task_info_ptr == nullptr) [[unlikely]] { + Log.error("Task", task, "not found while perform op", symbl_table[op], "in", task_expr, + "of task:", task_name); + return std::nullopt; + } + ASST_TASKDATA_PERFORM_OP_IF_BRANCH(next, false) + ASST_TASKDATA_PERFORM_OP_IF_BRANCH(sub, true) + ASST_TASKDATA_PERFORM_OP_IF_BRANCH(on_error_next, false) + ASST_TASKDATA_PERFORM_OP_IF_BRANCH(exceeded_next, false) + ASST_TASKDATA_PERFORM_OP_IF_BRANCH(reduce_other_times, true) + else [[unlikely]] + { + Log.error("Unknown symbol", type, "while perform op", symbl_table[op], "in", task_expr, + "of task:", task_name); + return std::nullopt; + } +#undef ASST_TASKDATA_PERFORM_OP_IF_BRANCH + } + } + + break; + } + default: + Log.error("Unknown op", symbl_table[op], "in", task_expr, "of task:", task_name); + return std::nullopt; + } + return ret; + }; + + for (std::string_view task_expr : raw_tasks) { + if (task_expr.empty() || (!allow_duplicate && tasks_set.contains(task_expr))) { + task_changed = true; + continue; + } + + std::vector symbols_table = symbl_table; + + std::unordered_map symbols_id = {}; + for (symbl_t i = 0; i != symbols_table.size(); ++i) { + symbols_id.emplace(symbols_table[i], i); + } + + std::vector symbol_stream; + auto emplace_symbol_if_not_empty = [&](const auto l, const auto r) { + if (l < r) { + auto symbol = std::string(l, r); + if (!symbols_id.contains(symbol)) { + symbl_t id = symbols_table.size(); + symbols_id.emplace(symbol, id); + symbols_table.emplace_back(symbol); + symbol_stream.emplace_back(id); + } + else { + symbol_stream.emplace_back(symbols_id[symbol]); + } + } + }; + + // 记号流分析 + auto y_begin = task_expr.begin(); + for (auto p = task_expr.begin(); p != task_expr.end(); ++p) { + switch (*p) { + case ' ': + case '\t': + case '\r': + case '\n': + emplace_symbol_if_not_empty(y_begin, p); + y_begin = p + 1; + break; + case ',': + case '(': + case ')': + case '{': + case '}': + case '#': + case '*': + case '+': + case '^': + task_changed = true; + [[fallthrough]]; + case '@': + emplace_symbol_if_not_empty(y_begin, p); + y_begin = p + 1; + symbol_stream.emplace_back(symbols_id[std::string(1, *p)]); + break; + default: + break; + } + } + emplace_symbol_if_not_empty(y_begin, task_expr.end()); + symbol_stream.emplace_back(symbl_end); // 结束符 + // Log.debug(symbol_stream | views::transform([&](symbl_t id) { return symbols_table[id]; })); + // Log.debug(symbol_stream); + + /* + $name = 至少一位的任务名 + $numbers = 至少一位的数字 + + $subtask_type = sub + | next + | on_error_next + | exceeded_next + | reduce_other_times + + $sharp_type = $subtask_type + | self + | back + | none + + $subtask = $subtask_type ( $tasks ) + + $lambda_task = { $subtask , ... } // 暂未实现 + | { $tasks } // 暂未实现;不写 subtask_type 默认为 sub + + $parens = # $sharp_type + | $name + | $name $lambda_task // 暂未实现 + | ( $tasks ) + + $top_tasks = $top_tasks @ $parens + | $top_tasks # $sharp_type + | $parens + + $mul_tasks = $top_tasks * $numbers + | $top_tasks + + $tasks = $mul_tasks + $tasks + | $mul_tasks ^ $tasks // 删除 $mul_tasks 中所有在 $tasks 中的任务 + | $mul_tasks + */ + // 记号流处理 + auto cur = symbol_stream.cbegin(); + using decode_func_t = std::function()>; + decode_func_t decode_name, decode_tasks, decode_multasks, decode_vtasks, decode_parens; + decode_name = [&]() -> std::optional { + if (is_symbl_name(*cur)) { + return std::make_shared(tasklist_t { symbols_table[*(cur++)] }); + } + return std::nullopt; + }; + decode_tasks = [&]() -> std::optional { + auto l = decode_multasks(); + if (!l) return std::nullopt; + while (*cur == symbl_add || *cur == symbl_sub) { + symbl_t op = *(cur++); + auto r = decode_multasks(); + if (!r) return std::nullopt; + l = perform_op(task_expr, op, *l, *r); + if (!l) return std::nullopt; + } + return l; + }; + decode_multasks = [&]() -> std::optional { + auto l = decode_vtasks(); + if (!l) return std::nullopt; + while (*cur == symbl_mul) { + symbl_t op = *(cur++); + auto r = decode_name(); + if (!r) return std::nullopt; + l = perform_op(task_expr, op, *l, *r); + if (!l) return std::nullopt; + } + return l; + }; + decode_vtasks = [&]() -> std::optional { + auto l = decode_parens(); + if (!l) return std::nullopt; + while (*cur == symbl_at || *cur == symbl_sharp) { + symbl_t op = *(cur++); + auto r = decode_parens(); + if (!r) return std::nullopt; + l = perform_op(task_expr, op, *l, *r); + if (!l) return std::nullopt; + } + return l; + }; + decode_parens = [&]() -> std::optional { + if (*cur == symbl_lparen) { + ++cur; + auto l = decode_tasks(); + if (!l) return std::nullopt; + if (*cur != symbl_rparen) [[unlikely]] { + Log.error("Invalid symbol", *cur, "at", cur - symbol_stream.cbegin(), "in", symbol_stream, + "(should be ')')"); + return std::nullopt; + } + ++cur; + return l; + } + if (*cur == symbl_sharp) { + ++cur; + auto r = decode_name(); + if (!r) return std::nullopt; + return perform_op(task_expr, symbl_sharp, nullptr, *r); + } + if (is_symbl_name(*cur)) { + return decode_name(); + } + Log.error("Invalid symbol", *cur, "at", cur - symbol_stream.cbegin(), "in", symbol_stream); + return std::nullopt; + }; + auto opt = decode_tasks(); + if (!opt || (*cur != symbl_end && cur != symbol_stream.cend())) { + return false; + } + + for (const auto& task : **opt) { + if (task.empty() || (!allow_duplicate && tasks_set.contains(task))) { + task_changed = true; + continue; + } + new_tasks.emplace_back(task); + tasks_set.emplace(task_name_view(task)); + } + + tasks_set.emplace(task_name_view(task_expr)); + } + + return true; +} + #ifdef ASST_DEBUG // 为了解决类似 beddc7c828126c678391e0b4da288db6d2c2d58a 导致的问题,加载的时候做一个语法检查 // 主要是处理是否包含未知键值的问题 diff --git a/src/MaaCore/Config/TaskData.h b/src/MaaCore/Config/TaskData.h index 682945e31f..79c02c2d49 100644 --- a/src/MaaCore/Config/TaskData.h +++ b/src/MaaCore/Config/TaskData.h @@ -2,83 +2,156 @@ #include "AbstractConfigWithTempl.h" +#include +#include #include #include #include "Common/AsstTypes.h" -#include "TaskData/TaskDataSymbol.h" namespace asst { + struct TaskInfo; + class TaskData final : public SingletonHolder, public AbstractConfigWithTempl { private: - static MatchTaskConstPtr _default_match_task_info(); - static OcrTaskConstPtr _default_ocr_task_info(); - static HashTaskConstPtr _default_hash_task_info(); - static TaskConstPtr _default_task_info(); + using tasklist_t = std::vector; + using tasklistptr_t = std::shared_ptr; + 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(); // 从模板任务生成 - static inline const MatchTaskConstPtr default_match_task_info_ptr = _default_match_task_info(); - static inline const OcrTaskConstPtr default_ocr_task_info_ptr = _default_ocr_task_info(); - static inline const HashTaskConstPtr default_hash_task_info_ptr = _default_hash_task_info(); - static inline const TaskConstPtr default_task_info_ptr = _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(); - static std::string append_prefix(std::string_view task_name, std::string_view task_prefix); - static TaskList append_prefix(const TaskList& base_task_list, std::string_view task_prefix); + // 这是下面几个函数的封装 + taskptr_t generate_task_info(std::string_view name, const json::value&, taskptr_t default_ptr, + std::string_view task_prefix = ""); + taskptr_t generate_match_task_info(std::string_view name, const json::value&, + std::shared_ptr default_ptr); + taskptr_t generate_ocr_task_info(std::string_view name, const json::value&, + std::shared_ptr default_ptr); + taskptr_t generate_hash_task_info(std::string_view name, const json::value&, + std::shared_ptr default_ptr); + // TaskInfo 的基础成员 + bool append_base_task_info(taskptr_t task_info_ptr, std::string_view name, const json::value& task_json, + taskptr_t default_ptr, std::string_view task_prefix = ""); + static std::string append_prefix(std::string_view task_name, std::string_view task_prefix) + { + if (task_prefix.ends_with('@')) [[unlikely]] { + task_prefix.remove_suffix(1); + } + if (task_prefix.empty()) [[unlikely]] { + return std::string(task_name); + } + if (task_name.empty()) { + return std::string(task_prefix); + } + if (task_name.starts_with('#')) { + return std::string(task_prefix) + std::string(task_name); + } + return std::string(task_prefix) + '@' + std::string(task_name); + } + static tasklist_t append_prefix(const tasklist_t& base_task_list, std::string_view task_prefix) + { + if (task_prefix.ends_with('@')) [[unlikely]] { + task_prefix.remove_suffix(1); + } + if (task_prefix.empty()) { + return base_task_list; + } + 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 == base_view.substr(l, r - l)) [[unlikely]] { + has_same_prefix = true; + break; + } + l = r + 1; + } + task_list.emplace_back(has_same_prefix ? base : append_prefix(base_view, task_prefix)); + } + return task_list; + }; + + // 运行时动态生成任务 + static taskptr_t _generate_task_info(const taskptr_t& base_ptr) + { + if (!base_ptr) [[unlikely]] { + return nullptr; + } + switch (base_ptr->algorithm) { + case AlgorithmType::MatchTemplate: + return std::make_shared(*std::dynamic_pointer_cast(base_ptr)); + case AlgorithmType::OcrDetect: + return std::make_shared(*std::dynamic_pointer_cast(base_ptr)); + case AlgorithmType::Hash: + return std::make_shared(*std::dynamic_pointer_cast(base_ptr)); + default: + return std::make_shared(*base_ptr); + } + } + static taskptr_t _generate_task_info(const taskptr_t& base_ptr, std::string_view task_prefix) + { + taskptr_t task_info_ptr = _generate_task_info(base_ptr); + 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 to_string_list(const ListType& other_string_list) + static tasklist_t to_string_list(const ListType& other_string_list) { - TaskList task_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; } - static inline std::unordered_set m_task_names {}; - static const std::string& task_name_view(std::string_view name) { return *m_task_names.emplace(name).first; } - - struct RawCompileResult - { - bool task_changed; - TaskDataSymbol::Symbols symbols; - }; - static ResultOrError compile_raw_tasklist( - const TaskList& raw_tasks, std::string_view self_name, - std::function get_raw, bool allow_duplicate); - - private: - TaskPtr generate_task_info(std::string_view name); - TaskPtr generate_match_task_info(std::string_view name, const json::value&, MatchTaskConstPtr default_ptr, - TaskDerivedType derived_type); - TaskPtr generate_ocr_task_info(std::string_view name, const json::value&, OcrTaskConstPtr default_ptr); - TaskPtr generate_hash_task_info(std::string_view name, const json::value&, HashTaskConstPtr default_ptr); - decltype(auto) insert_or_assign_raw_task(std::string_view task_name, TaskDerivedPtr task_info_ptr) + 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 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); } - struct CompileResult - { - bool task_changed; - TaskList tasks; - }; - ResultOrError compile_tasklist(const TaskList& raw_tasks, std::string_view self_name, - bool allow_duplicate); - bool generate_raw_task_info(std::string_view name, std::string_view prefix, std::string_view base_name, - const json::value& task_json, TaskDerivedType type); - bool generate_raw_task_and_base(std::string_view name, bool must_true); + bool compile_tasklist(tasklist_t& new_tasks, const tasklist_t& raw_tasks, std::string_view name, + bool& task_changed, bool multi); + bool generate_task_and_its_base(std::string_view name, bool must_true); #ifdef ASST_DEBUG bool syntax_check(std::string_view task_name, const json::value& task_json); #endif - TaskDerivedConstPtr get_raw(std::string_view name); + taskptr_t get_raw(std::string_view name); + template + requires(std::derived_from && + !std::same_as) // Parameter must be a TaskInfo + std::shared_ptr get_raw(std::string_view name) + { + return std::dynamic_pointer_cast(get_raw(name)); + } public: virtual ~TaskData() override = default; @@ -87,15 +160,21 @@ namespace asst void set_task_base(const std::string_view task_name, std::string base_task_name); bool lazy_parse(const json::value& json); - TaskPtr get(std::string_view name); + taskptr_t get(std::string_view name); template requires(std::derived_from && !std::same_as) // Parameter must be a TaskInfo std::shared_ptr get(std::string_view name) { - // TODO: should be const return std::dynamic_pointer_cast(get(name)); } + std::optional get_json(std::string_view name) const + { + if (m_json_all_tasks_info.find(name) != m_json_all_tasks_info.cend()) + return m_json_all_tasks_info.at(name); + else + return std::nullopt; + } protected: enum TaskStatus @@ -108,11 +187,12 @@ namespace asst virtual bool parse(const json::value& json) override; + std::unordered_set m_task_names; std::unordered_set m_templ_required; std::unordered_map m_task_status; - std::unordered_map m_json_all_tasks_info; // 原始的 json 信息 - std::unordered_map m_raw_all_tasks_info; // 未展开虚任务的任务信息 - std::unordered_map m_all_tasks_info; // 已展开虚任务的任务信息 + std::unordered_map m_json_all_tasks_info; // 原始的 json 信息 + std::unordered_map m_raw_all_tasks_info; // 未展开虚任务的任务信息 + std::unordered_map m_all_tasks_info; // 已展开虚任务的任务信息 }; inline static auto& Task = TaskData::get_instance(); diff --git a/src/MaaCore/Config/TaskData/TaskDataSymbol.cpp b/src/MaaCore/Config/TaskData/TaskDataSymbol.cpp deleted file mode 100644 index 0b3f2a1d82..0000000000 --- a/src/MaaCore/Config/TaskData/TaskDataSymbol.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "TaskDataSymbol.h" - -asst::TaskDataSymbol::SymbolsOrError asst::TaskDataSymbol::append_prefix( - const TaskDataSymbol& symbol, const TaskDataSymbol& prefix, std::string_view self_name, - std::function get_raw, - std::function compile_tasklist) -{ - // 注意:A@#self 是 A 而不是 A@self_name, #self@A 是 self_name@A 而不是 A - std::string_view prefix_name; - if (prefix == SharpSelf) { - prefix_name = self_name; - } - else if (prefix.is_name()) { - prefix_name = prefix.name(); - } - else [[unlikely]] { - return { std::nullopt, "prefix " + prefix.name() + " is not name or self" }; - } - if (prefix_name.empty()) { - return std::vector { symbol }; - } - if (symbol == SharpNone) { - return std::vector { symbol }; - } - if (symbol == SharpSelf) { - return std::vector { symbol }; - } - if (symbol.is_name()) { - if (symbol.name().empty()) { - return { std::nullopt, "name is empty" }; - } - return std::vector { TaskDataSymbol(std::string(prefix_name) + '@' + symbol.name()) }; - } - if (symbol == SharpBack) { - return std::vector { TaskDataSymbol(prefix_name) }; - } - auto other_task_info_ptr = get_raw(prefix_name); -#define ASST_TASKDATA_PERFORM_OP_IF_BRANCH(t, s, m) \ - if (symbol == s) { \ - if (auto opt = compile_tasklist(other_task_info_ptr->t)) { \ - return opt.value(); \ - } \ - return { std::nullopt, "failed to compile tasklist" }; \ - } - if (other_task_info_ptr == nullptr) [[unlikely]] { - return { std::nullopt, "task not found" }; - } - ASST_TASKDATA_PERFORM_OP_IF_BRANCH(next, SharpNext, false) - ASST_TASKDATA_PERFORM_OP_IF_BRANCH(sub, SharpSub, true) - ASST_TASKDATA_PERFORM_OP_IF_BRANCH(on_error_next, SharpOnErrorNext, false) - ASST_TASKDATA_PERFORM_OP_IF_BRANCH(exceeded_next, SharpExceededNext, false) - ASST_TASKDATA_PERFORM_OP_IF_BRANCH(reduce_other_times, SharpReduceOtherTimes, true) -#undef ASST_TASKDATA_PERFORM_OP_IF_BRANCH - return { std::nullopt, "unknown symbol" }; -} diff --git a/src/MaaCore/Config/TaskData/TaskDataSymbol.h b/src/MaaCore/Config/TaskData/TaskDataSymbol.h deleted file mode 100644 index c4209a757e..0000000000 --- a/src/MaaCore/Config/TaskData/TaskDataSymbol.h +++ /dev/null @@ -1,135 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "Common/AsstTypes.h" -#include "TaskDataTypes.h" -#include "Utils/Logger.hpp" - -namespace asst -{ - class TaskDataSymbol final - { - public: - using Symbols = std::vector; - using SymbolsOrError = ResultOrError; - - enum Type - { - End, - LambdaTaskSep, - LParen, - RParen, - LBrace, - RBrace, - At, - Sharp, - Mul, - Add, - Sub, - SharpSub, - SharpNext, - SharpOnErrorNext, - SharpExceededNext, - SharpReduceOtherTimes, - SharpSelf, - SharpBack, - SharpNone, - Name, - }; - - static const inline std::unordered_map symbol_repr_to_type = { - { "__END__", End }, - { ",", LambdaTaskSep }, - { "(", LParen }, - { ")", RParen }, - { "{", LBrace }, - { "}", RBrace }, - { "@", At }, - { "#", Sharp }, - { "*", Mul }, - { "+", Add }, - { "^", Sub }, - { "sub", SharpSub }, - { "next", SharpNext }, - { "on_error_next", SharpOnErrorNext }, - { "exceeded_next", SharpExceededNext }, - { "reduce_other_times", SharpReduceOtherTimes }, - { "self", SharpSelf }, - { "back", SharpBack }, - { "none", SharpNone }, - { "__name__", Name }, - }; - static const inline std::unordered_map symbol_type_to_repr = { - { End, "__END__" }, - { LambdaTaskSep, "," }, - { LParen, "(" }, - { RParen, ")" }, - { LBrace, "{" }, - { RBrace, "}" }, - { At, "@" }, - { Sharp, "#" }, - { Mul, "*" }, - { Add, "+" }, - { Sub, "^" }, - { SharpSub, "sub" }, - { SharpNext, "next" }, - { SharpOnErrorNext, "on_error_next" }, - { SharpExceededNext, "exceeded_next" }, - { SharpReduceOtherTimes, "reduce_other_times" }, - { SharpSelf, "self" }, - { SharpBack, "back" }, - { SharpNone, "none" }, - { Name, "__name__" }, - }; - - Type m_symbol; - std::string m_name; - - public: - bool operator==(Type other) const noexcept { return m_symbol == other; } - bool operator==(std::string_view other) const noexcept { return m_symbol == Name && m_name == other; } - bool operator==(const TaskDataSymbol& other) const noexcept - { - return m_symbol == other.m_symbol && (m_symbol != Name || m_name == other.m_name); - } - TaskDataSymbol(Type symbol) : m_symbol(symbol) {} - template - requires(std::constructible_from) - TaskDataSymbol(StringT&& name) : m_symbol(Name), m_name(std::forward(name)) - {} - - static bool is_sharp_type(Type type) noexcept - { - static std::unordered_set sharp_types = { - SharpSub, SharpNext, SharpOnErrorNext, SharpExceededNext, SharpReduceOtherTimes, - SharpSelf, SharpBack, SharpNone, - }; - return sharp_types.contains(type); - } - static const std::string& repr(Type type) - { - const auto pos = symbol_type_to_repr.find(type); - return pos == symbol_type_to_repr.end() ? symbol_type_to_repr.at(Name) : pos->second; - } - static Type type(std::string_view repr) - { - const auto pos = symbol_repr_to_type.find(repr); - return pos == symbol_repr_to_type.end() ? Name : pos->second; - } - - static SymbolsOrError append_prefix(const TaskDataSymbol& symbol, const TaskDataSymbol& prefix, - std::string_view self_name, - std::function get_raw, - std::function compile_tasklist); - - bool is_name() const noexcept { return m_symbol == Name; } - bool is_sharp_type() const noexcept { return is_sharp_type(m_symbol); } - Type type() const noexcept { return m_symbol; } - // const std::string& repr() const& noexcept { return repr(m_symbol); } - const std::string& name() const& noexcept { return is_name() ? m_name : repr(m_symbol); } - }; -} // namespace asst diff --git a/src/MaaCore/Config/TaskData/TaskDataSymbolStream.cpp b/src/MaaCore/Config/TaskData/TaskDataSymbolStream.cpp deleted file mode 100644 index 6624b31bac..0000000000 --- a/src/MaaCore/Config/TaskData/TaskDataSymbolStream.cpp +++ /dev/null @@ -1,265 +0,0 @@ -#include "TaskDataSymbolStream.h" - -asst::ResultOrError asst::TaskDataSymbolStream::parse(std::string_view task_expr) -{ - bool task_changed = false; - auto emplace_symbol_if_not_empty = [&](const auto l, const auto r) { - if (l < r) { - auto symbol = std::string_view(l, r); - auto symbol_type = Symbol::type(symbol); - if (Symbol::is_sharp_type(symbol_type)) { - if (!m_symbolstream.empty() && m_symbolstream.back() == Symbol::Sharp) { - m_symbolstream.pop_back(); - if (!m_symbolstream.empty() && - (m_symbolstream.back().is_name() || m_symbolstream.back().is_sharp_type() || - m_symbolstream.back() == Symbol::RParen)) { - m_symbolstream.emplace_back(Symbol::At); - } - m_symbolstream.emplace_back(symbol_type); - } - else { - m_symbolstream.emplace_back(symbol); - } - } - else if (symbol_type != Symbol::Name) { - m_symbolstream.emplace_back(symbol_type); - } - else { - m_symbolstream.emplace_back(symbol); - } - } - }; - - // 记号流分析 - auto y_begin = task_expr.cbegin(); - for (auto p = task_expr.cbegin(); p != task_expr.cend(); ++p) { - switch (*p) { - case ' ': - case '\t': - case '\r': - case '\n': - task_changed = true; - emplace_symbol_if_not_empty(y_begin, p); - y_begin = p + 1; - break; - case ',': - case '(': - case ')': - case '{': - case '}': - case '#': - case '*': - case '+': - case '^': - task_changed = true; - [[fallthrough]]; - case '@': { - emplace_symbol_if_not_empty(y_begin, p); - y_begin = p + 1; - auto symbol = TaskDataSymbol::type(std::string_view { std::addressof(*p), 1 }); - if (symbol == TaskDataSymbol::Name) [[unlikely]] { - // should not happen - return { std::nullopt, std::string("Error when decode symbol ") + *p + " at " + - std::to_string(p - task_expr.cbegin()) + " in " += task_expr }; - } - m_symbolstream.emplace_back(symbol); - break; - } - default: - break; - } - } - emplace_symbol_if_not_empty(y_begin, task_expr.cend()); - m_symbolstream.emplace_back(TaskDataSymbol::End); // 结束符 - - return task_changed; -} - -asst::TaskDataSymbolStream::SymbolsOrError asst::TaskDataSymbolStream::decode(AppendPrefixFunc append_prefix, - std::string_view self_name) const -{ - /* - $name = 至少一位的任务名 - $numbers = 至少一位的数字 - - $subtask_type = # sub - | # next - | # on_error_next - | # exceeded_next - | # reduce_other_times - - $sharp_type = $subtask_type - | # self - | # back - | # none - - $subtask = $subtask_type ( $tasks ) - - $lambda_task = { $subtask , ... } // 暂未实现 - | { $tasks } // 暂未实现;不写 subtask_type 默认为 sub - - $parens = $sharp_type - | $name - | $name $lambda_task // 暂未实现 - | ( $tasks ) - - $top_tasks = $top_tasks @ $parens - | $parens - - $mul_tasks = $top_tasks * $numbers - | $top_tasks - - $tasks = $mul_tasks + $tasks - | $mul_tasks ^ $tasks // 删除 $mul_tasks 中所有在 $tasks 中的任务 - | $mul_tasks - */ - using decode_func_t = std::function; - - auto cur = m_symbolstream.cbegin(); - decode_func_t decode_name_or_vtask, decode_tasks, decode_multasks, decode_vtasks, decode_parens; - decode_name_or_vtask = [&]() -> SymbolsOrError { - if (cur->is_name() || cur->is_sharp_type()) { - return Symbols { *(cur++) }; - } - return { std::nullopt, "expect name or sharp type, got " + cur->name() }; - }; - decode_tasks = [&]() -> SymbolsOrError { - Symbols x = {}, y = {}; - if (auto opt = decode_multasks(); !opt) { - return opt; - } - else { - x = *opt; - } - while (*cur == Symbol::Add || *cur == Symbol::Sub) { - Symbol op = *(cur++); - if (auto opt = decode_multasks(); !opt) { - return opt; - } - else { - y = *opt; - } - if (op == Symbol::Add) { - // x = x + y - ranges::move(y, std::back_inserter(x)); - } - else { - // x = x - y - // WARNING: - // self_name + #self ^ #self = #none - // self_name + #self ^ self_name = #self - if (ranges::any_of(y, [&](const auto& sy) { return sy == Symbol::SharpSelf; })) { - std::erase(x, self_name); - } - std::erase_if(x, [&](const auto& sx) { - return ranges::any_of(y, [&](const auto& sy) { return sx == sy; }); - }); - } - } - return x; - }; - decode_multasks = [&]() -> SymbolsOrError { - Symbols x = {}, y = {}; - if (auto opt = decode_vtasks(); !opt) { - return opt; - } - else { - x = *opt; - } - while (*cur == Symbol::Mul) { - Symbol op = *(cur++); - if (auto opt = decode_name_or_vtask(); !opt) { - return opt; - } - else { - y = *opt; - } - if (y.size() != 1) { - return { std::nullopt, "too many y" }; - } - int times = 0; - if (const auto s = y.front(); !s.is_name() || !utils::chars_to_number(s.name(), times)) { - return { std::nullopt, "y is not number" }; - } - if (times < 0) { - return { std::nullopt, "y is negative" }; - } - if (times == 0) { - x = {}; - Log.warn("y:", times, "is zero"); - continue; - } - if (times == 1) { - continue; - } - Symbols x_copy = x; - for (int i = 0; i < times - 1; ++i) { - ranges::copy(x_copy, std::back_inserter(x)); - } - } - return x; - }; - decode_vtasks = [&]() -> SymbolsOrError { - Symbols x = {}, y = {}; - if (auto opt = decode_parens(); !opt) { - return opt; - } - else { - x = *opt; - } - while (*cur == Symbol::At) { - Symbol op = *(cur++); - if (auto opt = decode_parens(); !opt) { - return opt; - } - else { - y = *opt; - } - // (A+B)@(C+D) = A@C + A@D + B@C + B@D - Symbols x_copy = x; - x.clear(); - for (const auto& sx : x_copy) { - for (const auto& sy : y) { - auto opt = append_prefix(sy, sx); - if (!opt) { - return { std::nullopt, - "decode_vtasks: failed while " + sx.name() + " @ " + sy.name() + ", " + opt.error() }; - } - ranges::copy(*opt, std::back_inserter(x)); - } - } - } - return x; - }; - decode_parens = [&]() -> SymbolsOrError { - if (*cur == Symbol::LParen) { - ++cur; - Symbols x = {}; - if (auto opt = decode_tasks(); !opt) { - return opt; - } - else { - x = *opt; - } - if (*cur != Symbol::RParen) [[unlikely]] { - return { std::nullopt, "decode_parens: expected ')', got " + cur->name() }; - } - ++cur; - return x; - } - if (cur->is_name() || cur->is_sharp_type()) { - return decode_name_or_vtask(); - } - return { std::nullopt, "decode_parens: unexpected symbol " + cur->name() }; - }; - - auto opt = decode_tasks(); - if (!opt) { - return opt; - } - if (cur != m_symbolstream.cend() && *cur != TaskDataSymbol::End) { - return { std::nullopt, "decode: did not reach end, got " + cur->name() }; - } - - return opt; -} diff --git a/src/MaaCore/Config/TaskData/TaskDataSymbolStream.h b/src/MaaCore/Config/TaskData/TaskDataSymbolStream.h deleted file mode 100644 index 81a02a9061..0000000000 --- a/src/MaaCore/Config/TaskData/TaskDataSymbolStream.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include - -#include "Config/TaskData.h" -#include "TaskDataSymbol.h" -#include "TaskDataTypes.h" - -namespace asst -{ - class TaskDataSymbolStream final - { - public: - using Symbol = TaskDataSymbol; - using Symbols = TaskDataSymbol::Symbols; - using SymbolsOrError = TaskDataSymbol::SymbolsOrError; - using AppendPrefixFunc = std::function; - - private: - Symbols m_symbolstream; - - public: - ResultOrError parse(std::string_view task_expr); - SymbolsOrError decode(AppendPrefixFunc append_prefix, std::string_view self_name) const; - }; -} // namespace asst diff --git a/src/MaaCore/Config/TaskData/TaskDataTypes.h b/src/MaaCore/Config/TaskData/TaskDataTypes.h deleted file mode 100644 index f185d8f541..0000000000 --- a/src/MaaCore/Config/TaskData/TaskDataTypes.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace asst -{ - template - class ResultOrError - { - using _variant_t = std::variant; - bool m_success; - _variant_t m_result_or_error; - - public: - template - requires std::constructible_from - constexpr ResultOrError(Args&&... args) - : m_success(true), m_result_or_error(std::in_place_index<0>, std::forward(args)...) - {} - - constexpr ResultOrError([[maybe_unused]] std::nullopt_t nullopt, std::string result) - : m_success(false), m_result_or_error(std::in_place_index<1>, std::move(result)) - {} - - constexpr operator bool() const noexcept { return m_success; } - constexpr bool has_value() const noexcept { return m_success; } - - constexpr ResultT& operator*() & { return std::get<0>(m_result_or_error); } - constexpr const ResultT& operator*() const& { return std::get<0>(m_result_or_error); } - constexpr ResultT&& operator*() && { return std::move(std::get<0>(m_result_or_error)); } - constexpr const ResultT&& operator*() const&& { return std::move(std::get<0>(m_result_or_error)); } - - constexpr ResultT& value() & { return std::get<0>(m_result_or_error); } - constexpr const ResultT& value() const& { return std::get<0>(m_result_or_error); } - constexpr ResultT&& value() && { return std::move(std::get<0>(m_result_or_error)); } - constexpr const ResultT&& value() const&& { return std::move(std::get<0>(m_result_or_error)); } - - constexpr std::string& error() & { return std::get<1>(m_result_or_error); } - constexpr const std::string& error() const& { return std::get<1>(m_result_or_error); } - constexpr std::string&& error() && { return std::move(std::get<1>(m_result_or_error)); } - constexpr const std::string&& error() const&& { return std::move(std::get<1>(m_result_or_error)); } - }; -} // namespace asst diff --git a/src/MaaCore/MaaCore.vcxproj b/src/MaaCore/MaaCore.vcxproj index 52d220e77e..99dcffe697 100644 --- a/src/MaaCore/MaaCore.vcxproj +++ b/src/MaaCore/MaaCore.vcxproj @@ -48,9 +48,6 @@ - - - @@ -232,8 +229,6 @@ - - diff --git a/src/MaaCore/MaaCore.vcxproj.filters b/src/MaaCore/MaaCore.vcxproj.filters index bc079a14ad..b65b19442a 100644 --- a/src/MaaCore/MaaCore.vcxproj.filters +++ b/src/MaaCore/MaaCore.vcxproj.filters @@ -95,9 +95,6 @@ {bf83678b-e9d7-4cf1-8616-aba2009072b6} - - {8d436f0b-8dad-42b2-9549-f9073b808104} - @@ -684,15 +681,6 @@ Source\Task\Roguelike - - Source\Resource\TaskData - - - Source\Resource\TaskData - - - Source\Resource\TaskData - @@ -1148,11 +1136,5 @@ Source\Task\Roguelike - - Source\Resource\TaskData - - - Source\Resource\TaskData - \ No newline at end of file