diff --git a/src/MaaCore/Config/TaskData.cpp b/src/MaaCore/Config/TaskData.cpp index a45d9898fb..6cbb9fc9d2 100644 --- a/src/MaaCore/Config/TaskData.cpp +++ b/src/MaaCore/Config/TaskData.cpp @@ -14,6 +14,108 @@ #include "Utils/Ranges.hpp" #include "Utils/StringMisc.hpp" +namespace asst +{ + // base types + template + requires(std::constructible_from) + bool parse_json_as(const json::value& input, OutT& output) + { + if (input.is()) { + output = input.as(); + return true; + } + return false; + } + + // std::pair <- [first, second] + template + requires(requires(const json::value& input, FirstT x, SecondT y) { + parse_json_as(input, x) && parse_json_as(input, y); + }) + bool parse_json_as(const json::value& input, std::pair& output) + { + if (!input.is_array()) { + return false; + } + const auto& items = input.as_array(); + if (items.size() != 2) { + return false; + } + return parse_json_as(items[0], output.first) && parse_json_as(items[1], output.second); + } + + // std::vector <- val | [val, ...] + template + requires(requires(const json::value& input, ValT x) { parse_json_as(input, x); }) + bool parse_json_as(const json::value& input, std::vector& output) + { + if (!input.is_array()) { + if constexpr (std::constructible_from) { + if (input.is()) { + output = { input.as() }; + return true; + } + } + return false; + } + const auto& items = input.as_array(); + output.clear(); + for (const auto& item : items) { + ValT val; + if (!parse_json_as(item, val)) { + output.clear(); + return false; + } + output.emplace_back(std::move(val)); + } + return true; + } + + // asst::Rect <- [int, int, int, int] + bool parse_json_as(const json::value& input, asst::Rect& output) + { + if (!input.is_array()) { + return false; + } + const auto& items = input.as_array(); + if (items.size() != 4) { + return false; + } + return parse_json_as(items[0], output.x) && parse_json_as(items[1], output.y) && + parse_json_as(items[2], output.width) && parse_json_as(items[3], output.height); + } + + template + std::optional parse_json_as(const json::value& input) + { + OutT output; + return parse_json_as(input, output) ? output : std::nullopt; + } + + template + requires(std::constructible_from || + std::constructible_from()())>) + bool get_and_check_value(const json::value& input, const std::string& key, OutT& output, DefaultT&& default_val) + { + auto opt = input.find(key); + if (!opt) { + if constexpr (std::constructible_from) { + output = std::forward(default_val); + } + else { + output = default_val(); + } + return true; + } + if (parse_json_as(*opt, output)) { + return true; + } + asst::Log.error("Invalid type of", key, "in", input); + return false; + } +} // namespace asst + const std::unordered_set& asst::TaskData::get_templ_required() const noexcept { return m_templ_required; @@ -757,18 +859,8 @@ asst::TaskData::taskptr_t asst::TaskData::generate_match_task_info(std::string_v default_ptr = default_match_task_info_ptr; } auto match_task_info_ptr = std::make_shared(); - auto templ_opt = task_json.find("template"); - if (!templ_opt) { - match_task_info_ptr->templ_names = { std::string(name) + ".png" }; - } - else if (templ_opt->is_string()) { - match_task_info_ptr->templ_names = { templ_opt->as_string() }; - } - else if (templ_opt->is_array()) { - match_task_info_ptr->templ_names = to_string_list(templ_opt->as_array()); - } - else { - Log.error("Invalid template type in task", name); + if (!get_and_check_value(task_json, "template", match_task_info_ptr->templ_names, + [&]() { return std::vector { std::string(name) + ".png" }; })) { return nullptr; } @@ -806,14 +898,7 @@ asst::TaskData::taskptr_t asst::TaskData::generate_match_task_info(std::string_v return nullptr; } - if (auto opt = task_json.find("maskRange")) { - auto& mask_range = *opt; - match_task_info_ptr->mask_range = - std::make_pair(static_cast(mask_range[0]), static_cast(mask_range[1])); - } - else { - match_task_info_ptr->mask_range = default_ptr->mask_range; - } + get_and_check_value(task_json, "maskRange", match_task_info_ptr->mask_range, default_ptr->mask_range); return match_task_info_ptr; } @@ -826,6 +911,7 @@ asst::TaskData::taskptr_t asst::TaskData::generate_ocr_task_info([[maybe_unused] } auto ocr_task_info_ptr = std::make_shared(); + // text 不允许为字符串,必须是字符串数组,不能用 get_and_check_value auto array_opt = task_json.find("text"); ocr_task_info_ptr->text = array_opt ? to_string_list(array_opt.value()) : default_ptr->text; #ifdef ASST_DEBUG @@ -833,18 +919,11 @@ asst::TaskData::taskptr_t asst::TaskData::generate_ocr_task_info([[maybe_unused] Log.warn("Ocr task", name, "has implicit empty text."); } #endif - ocr_task_info_ptr->full_match = task_json.get("fullMatch", default_ptr->full_match); - ocr_task_info_ptr->is_ascii = task_json.get("isAscii", default_ptr->is_ascii); - ocr_task_info_ptr->without_det = task_json.get("withoutDet", default_ptr->without_det); - ocr_task_info_ptr->replace_full = task_json.get("replaceFull", default_ptr->replace_full); - if (auto opt = task_json.find("ocrReplace")) { - for (const json::value& rep : opt.value()) { - ocr_task_info_ptr->replace_map.emplace_back(std::make_pair(rep[0].as_string(), rep[1].as_string())); - } - } - else { - ocr_task_info_ptr->replace_map = default_ptr->replace_map; - } + get_and_check_value(task_json, "fullMatch", ocr_task_info_ptr->full_match, default_ptr->full_match); + get_and_check_value(task_json, "isAscii", ocr_task_info_ptr->is_ascii, default_ptr->is_ascii); + get_and_check_value(task_json, "withoutDet", ocr_task_info_ptr->without_det, default_ptr->without_det); + get_and_check_value(task_json, "replaceFull", ocr_task_info_ptr->replace_full, default_ptr->replace_full); + get_and_check_value(task_json, "ocrReplace", ocr_task_info_ptr->replace_map, default_ptr->replace_map); return ocr_task_info_ptr; } @@ -856,6 +935,7 @@ asst::TaskData::taskptr_t asst::TaskData::generate_hash_task_info([[maybe_unused default_ptr = default_hash_task_info_ptr; } auto hash_task_info_ptr = std::make_shared(); + // hash 不允许为字符串,必须是字符串数组,不能用 get_and_check_value auto array_opt = task_json.find("hash"); hash_task_info_ptr->hashes = array_opt ? to_string_list(array_opt.value()) : default_ptr->hashes; #ifdef ASST_DEBUG @@ -863,19 +943,9 @@ asst::TaskData::taskptr_t asst::TaskData::generate_hash_task_info([[maybe_unused Log.warn("Hash task", name, "has implicit empty hashes."); } #endif - - hash_task_info_ptr->dist_threshold = task_json.get("threshold", default_ptr->dist_threshold); - - if (auto opt = task_json.find("maskRange")) { - auto& mask_range = *opt; - hash_task_info_ptr->mask_range = - std::make_pair(static_cast(mask_range[0]), static_cast(mask_range[1])); - } - else { - hash_task_info_ptr->mask_range = default_ptr->mask_range; - } - hash_task_info_ptr->bound = task_json.get("bound", default_ptr->bound); - + get_and_check_value(task_json, "threshold", hash_task_info_ptr->dist_threshold, default_ptr->dist_threshold); + get_and_check_value(task_json, "maskRange", hash_task_info_ptr->mask_range, default_ptr->mask_range); + get_and_check_value(task_json, "bound", hash_task_info_ptr->bound, default_ptr->bound); return hash_task_info_ptr; } @@ -896,67 +966,30 @@ bool asst::TaskData::append_base_task_info(taskptr_t task_info_ptr, std::string_ else { task_info_ptr->action = default_ptr->action; } - task_info_ptr->cache = task_json.get("cache", default_ptr->cache); - task_info_ptr->max_times = task_json.get("maxTimes", default_ptr->max_times); - auto array_opt = task_json.find("exceededNext"); - task_info_ptr->exceeded_next = - array_opt ? to_string_list(array_opt.value()) : append_prefix(default_ptr->exceeded_next, task_prefix); - array_opt = task_json.find("onErrorNext"); - task_info_ptr->on_error_next = - array_opt ? to_string_list(array_opt.value()) : append_prefix(default_ptr->on_error_next, task_prefix); - task_info_ptr->pre_delay = task_json.get("preDelay", default_ptr->pre_delay); - task_info_ptr->post_delay = task_json.get("postDelay", default_ptr->post_delay); - array_opt = task_json.find("reduceOtherTimes"); - task_info_ptr->reduce_other_times = - array_opt ? to_string_list(array_opt.value()) : append_prefix(default_ptr->reduce_other_times, task_prefix); - if (auto opt = task_json.find("roi")) { - auto& roi_arr = *opt; - int x = static_cast(roi_arr[0]); - int y = static_cast(roi_arr[1]); - int width = static_cast(roi_arr[2]); - int height = static_cast(roi_arr[3]); + get_and_check_value(task_json, "cache", task_info_ptr->cache, default_ptr->cache); + get_and_check_value(task_json, "maxTimes", task_info_ptr->max_times, default_ptr->max_times); + get_and_check_value(task_json, "exceededNext", task_info_ptr->exceeded_next, + [&]() { return append_prefix(default_ptr->exceeded_next, task_prefix); }); + get_and_check_value(task_json, "onErrorNext", task_info_ptr->on_error_next, + [&]() { return append_prefix(default_ptr->on_error_next, task_prefix); }); + get_and_check_value(task_json, "preDelay", task_info_ptr->pre_delay, default_ptr->pre_delay); + get_and_check_value(task_json, "postDelay", task_info_ptr->post_delay, default_ptr->post_delay); + get_and_check_value(task_json, "reduceOtherTimes", task_info_ptr->reduce_other_times, + [&]() { return append_prefix(default_ptr->reduce_other_times, task_prefix); }); + get_and_check_value(task_json, "roi", task_info_ptr->roi, default_ptr->roi); #ifdef ASST_DEBUG - if (x + width > WindowWidthDefault || y + height > WindowHeightDefault) { - Log.error(name, "roi is out of bounds"); - return false; - } + 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 - task_info_ptr->roi = Rect(x, y, width, height); - } - else { - task_info_ptr->roi = default_ptr->roi; - } - array_opt = task_json.find("sub"); - task_info_ptr->sub = array_opt ? to_string_list(array_opt.value()) : append_prefix(default_ptr->sub, task_prefix); - task_info_ptr->sub_error_ignored = task_json.get("subErrorIgnored", default_ptr->sub_error_ignored); - array_opt = task_json.find("next"); - task_info_ptr->next = array_opt ? to_string_list(array_opt.value()) : append_prefix(default_ptr->next, task_prefix); - if (auto opt = task_json.find("rectMove")) { - auto& move_arr = opt.value(); - task_info_ptr->rect_move = Rect(move_arr[0].as_integer(), move_arr[1].as_integer(), move_arr[2].as_integer(), - move_arr[3].as_integer()); - } - else { - task_info_ptr->rect_move = default_ptr->rect_move; - } - - if (auto opt = task_json.find("specificRect")) { - auto& rect_arr = opt.value(); - task_info_ptr->specific_rect = Rect(rect_arr[0].as_integer(), rect_arr[1].as_integer(), - rect_arr[2].as_integer(), rect_arr[3].as_integer()); - } - else { - task_info_ptr->specific_rect = default_ptr->specific_rect; - } - if (auto opt = task_json.find("specialParams")) { - auto& special_params = opt.value(); - for (auto& param : special_params) { - task_info_ptr->special_params.emplace_back(param.as_integer()); - } - } - else { - task_info_ptr->special_params = default_ptr->special_params; - } + get_and_check_value(task_json, "sub", task_info_ptr->sub, + [&]() { return append_prefix(default_ptr->sub, task_prefix); }); + get_and_check_value(task_json, "subErrorIgnored", task_info_ptr->sub_error_ignored, default_ptr->sub_error_ignored); + get_and_check_value(task_json, "next", task_info_ptr->next, + [&]() { return append_prefix(default_ptr->next, task_prefix); }); + get_and_check_value(task_json, "rectMove", task_info_ptr->rect_move, default_ptr->rect_move); + get_and_check_value(task_json, "specificRect", task_info_ptr->specific_rect, default_ptr->specific_rect); + get_and_check_value(task_json, "specialParams", task_info_ptr->special_params, default_ptr->special_params); return true; }