mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-15 17:30:27 +08:00
rft: 调整 TaskData 的部分内部函数,增加注释
This commit is contained in:
@@ -28,6 +28,24 @@ namespace asst
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parse_json_as(const json::value& input, AlgorithmType& output)
|
||||
{
|
||||
if (input.is_string()) {
|
||||
output = get_algorithm_type(input.as_string());
|
||||
return output != AlgorithmType::Invalid;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parse_json_as(const json::value& input, ProcessTaskAction& output)
|
||||
{
|
||||
if (input.is_string()) {
|
||||
output = get_action_type(input.as_string());
|
||||
return output != ProcessTaskAction::Invalid;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// std::pair<FirstT, SecondT> <- [first, second]
|
||||
template <typename FirstT, typename SecondT>
|
||||
requires(requires(const json::value& input, FirstT x, SecondT y) {
|
||||
@@ -95,7 +113,8 @@ namespace asst
|
||||
|
||||
template <typename OutT, typename DefaultT>
|
||||
requires(std::constructible_from<OutT, DefaultT> || std::constructible_from<OutT, std::invoke_result_t<DefaultT>>)
|
||||
bool get_and_check_value(const json::value& input, const std::string& key, OutT& output, DefaultT&& default_val)
|
||||
bool get_value_or(std::string_view repr, const json::value& input, const std::string& key, OutT& output,
|
||||
DefaultT&& default_val)
|
||||
{
|
||||
auto opt = input.find(key);
|
||||
if (!opt) {
|
||||
@@ -108,9 +127,22 @@ namespace asst
|
||||
return true;
|
||||
}
|
||||
if (parse_json_as(*opt, output)) {
|
||||
#ifdef ASST_DEBUG
|
||||
// 如果有默认值,检查是否与默认值相同
|
||||
if constexpr (std::constructible_from<OutT, DefaultT>) {
|
||||
if (output == default_val) {
|
||||
Log.warn("Value of", key, "in", repr, "is same as default value");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (output == default_val()) {
|
||||
Log.warn("Value of", key, "in", repr, "is same as default value");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
Log.error("Invalid type of", key, "in", input.to_string());
|
||||
Log.error("Invalid type of", key, "in", repr);
|
||||
return false;
|
||||
}
|
||||
} // namespace asst
|
||||
@@ -119,40 +151,67 @@ const std::unordered_set<std::string>& asst::TaskData::get_templ_required() cons
|
||||
{
|
||||
return m_templ_required;
|
||||
}
|
||||
std::shared_ptr<asst::TaskInfo> asst::TaskData::get_raw(std::string_view name)
|
||||
asst::TaskData::taskptr_t asst::TaskData::get_raw(std::string_view name)
|
||||
{
|
||||
if (!generate_task_and_its_base(name, true)) {
|
||||
if (!generate_task_and_its_base(name, true)) [[unlikely]] {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (auto it = m_raw_all_tasks_info.find(name); it != m_raw_all_tasks_info.cend()) [[likely]] {
|
||||
if (auto it = m_raw_all_tasks_info.find(name); it != m_raw_all_tasks_info.cend()) {
|
||||
return 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]] {
|
||||
size_t name_len = name.find('@');
|
||||
if (name_len == std::string_view::npos) [[unlikely]] {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string_view derived_task_name = name.substr(0, name_len);
|
||||
return _generate_task_info(base_task_iter, derived_task_name);
|
||||
// 隐式 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;
|
||||
}
|
||||
|
||||
std::shared_ptr<asst::TaskInfo> asst::TaskData::get(std::string_view name)
|
||||
asst::TaskData::taskptr_t asst::TaskData::get(std::string_view name)
|
||||
{
|
||||
// 普通 task 或已经生成过的 `@` 型 task
|
||||
// 普通任务 或 已经生成过的高级任务
|
||||
if (auto it = m_all_tasks_info.find(name); it != m_all_tasks_info.cend()) [[likely]] {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return expand_task(name, get_raw(name)).value_or(nullptr);
|
||||
auto raw_task = get_raw(name);
|
||||
auto task = _generate_task_info(raw_task);
|
||||
if (!task) [[unlikely]] {
|
||||
return nullptr;
|
||||
}
|
||||
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);
|
||||
ASST_TASKDATA_GET_IF_BRANCH(sub, true);
|
||||
ASST_TASKDATA_GET_IF_BRANCH(exceeded_next, false);
|
||||
ASST_TASKDATA_GET_IF_BRANCH(on_error_next, false);
|
||||
ASST_TASKDATA_GET_IF_BRANCH(reduce_other_times, true);
|
||||
#undef ASST_TASKDATA_GET_IF_BRANCH
|
||||
|
||||
constexpr size_t MAX_TASKS_SIZE = 65535;
|
||||
if (m_all_tasks_info.size() < MAX_TASKS_SIZE) [[likely]] {
|
||||
// 保存最终生成的任务,下次查询时可以直接返回
|
||||
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 changed ? task : raw_task;
|
||||
}
|
||||
}
|
||||
|
||||
bool asst::TaskData::lazy_parse(const json::value& json)
|
||||
@@ -300,13 +359,308 @@ void asst::TaskData::set_task_base(const std::string_view task_name, std::string
|
||||
clear_tasks();
|
||||
}
|
||||
|
||||
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:
|
||||
// 已经显式生成
|
||||
if (m_raw_all_tasks_info.contains(name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 隐式生成的资源
|
||||
if (size_t p = name.find('@'); p != std::string::npos) {
|
||||
return generate_task_and_its_base(name.substr(p + 1), must_true);
|
||||
}
|
||||
|
||||
m_task_status[name] = NotExists;
|
||||
[[fallthrough]];
|
||||
case NotExists:
|
||||
if (must_true) {
|
||||
// 必须有名字为 name 的资源
|
||||
Log.error("Unknown task:", name);
|
||||
}
|
||||
// 不一定必须有名字为 name 的资源,例如 Roguelike@Abandon 不必有 Abandon.
|
||||
return false;
|
||||
case ToBeGenerate: {
|
||||
if (!m_json_all_tasks_info.contains(name)) [[unlikely]] {
|
||||
// 这段正常情况来说是不可能的,除非有 string_view 引用失效
|
||||
Log.error("Unexcepted ToBeGenerate task:", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_task_status[name] = Generating;
|
||||
|
||||
const json::value& task_json = m_json_all_tasks_info.at(name);
|
||||
|
||||
// BaseTask
|
||||
if (auto opt = task_json.find<std::string>("baseTask")) {
|
||||
std::string base = opt.value();
|
||||
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_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_task(name, "", nullptr, task_json);
|
||||
}
|
||||
[[unlikely]] case Generating:
|
||||
Log.error("Task", name, "is generated cyclically");
|
||||
return false;
|
||||
[[unlikely]] default:
|
||||
Log.error("Task", name, "has unknown status");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (default_ptr == nullptr) {
|
||||
default_ptr = default_task_info_ptr;
|
||||
task_prefix = "";
|
||||
}
|
||||
|
||||
// 获取 algorithm 并按照 algorithm 生成 TaskInfo
|
||||
AlgorithmType algorithm;
|
||||
get_value_or(name, task_json, "algorithm", algorithm, default_ptr->algorithm);
|
||||
|
||||
taskptr_t task_ptr = nullptr;
|
||||
switch (algorithm) {
|
||||
case AlgorithmType::MatchTemplate:
|
||||
task_ptr = generate_match_task_info(name, task_json, std::dynamic_pointer_cast<MatchTaskInfo>(default_ptr));
|
||||
break;
|
||||
case AlgorithmType::OcrDetect:
|
||||
task_ptr = generate_ocr_task_info(name, task_json, std::dynamic_pointer_cast<OcrTaskInfo>(default_ptr));
|
||||
break;
|
||||
case AlgorithmType::Hash:
|
||||
task_ptr = generate_hash_task_info(name, task_json, std::dynamic_pointer_cast<HashTaskInfo>(default_ptr));
|
||||
break;
|
||||
case AlgorithmType::JustReturn:
|
||||
task_ptr = std::make_shared<TaskInfo>();
|
||||
break;
|
||||
default:
|
||||
Log.error("Unknown algorithm in task", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (task_ptr == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 不管什么algorithm,都有基础成员(next, roi, 等等)
|
||||
if (!append_base_task_info(task_ptr, name, task_json, default_ptr, task_prefix)) {
|
||||
return nullptr;
|
||||
}
|
||||
task_ptr->algorithm = algorithm;
|
||||
task_ptr->name = name;
|
||||
return task_ptr;
|
||||
}
|
||||
|
||||
asst::TaskData::taskptr_t asst::TaskData::generate_match_task_info(std::string_view name, const json::value& task_json,
|
||||
std::shared_ptr<MatchTaskInfo> default_ptr)
|
||||
{
|
||||
if (default_ptr == nullptr) {
|
||||
default_ptr = default_match_task_info_ptr;
|
||||
}
|
||||
auto match_task_info_ptr = std::make_shared<MatchTaskInfo>();
|
||||
if (!get_value_or(name, task_json, "template", match_task_info_ptr->templ_names,
|
||||
[&]() { return std::vector { std::string(name) + ".png" }; })) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
m_templ_required.insert(match_task_info_ptr->templ_names.begin(), match_task_info_ptr->templ_names.end());
|
||||
|
||||
// 其余若留空则继承模板任务
|
||||
|
||||
auto threshold_opt = task_json.find("templThreshold");
|
||||
if (!threshold_opt) {
|
||||
match_task_info_ptr->templ_thresholds = default_ptr->templ_thresholds;
|
||||
match_task_info_ptr->templ_thresholds.resize(match_task_info_ptr->templ_names.size(),
|
||||
default_ptr->templ_thresholds.back());
|
||||
}
|
||||
else if (threshold_opt->is_number()) {
|
||||
// 单个数值时,所有模板都使用这个阈值
|
||||
match_task_info_ptr->templ_thresholds.resize(match_task_info_ptr->templ_names.size(),
|
||||
threshold_opt->as_double());
|
||||
}
|
||||
else if (threshold_opt->is_array()) {
|
||||
ranges::copy(threshold_opt->as_array() | views::transform(&ranges::range_value_t<json::array>::as_double),
|
||||
std::back_inserter(match_task_info_ptr->templ_thresholds));
|
||||
}
|
||||
else {
|
||||
Log.error("Invalid templThreshold type in task", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (match_task_info_ptr->templ_names.size() != match_task_info_ptr->templ_thresholds.size()) {
|
||||
Log.error("Template count and templThreshold count not match in task", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (match_task_info_ptr->templ_names.size() == 0 || match_task_info_ptr->templ_thresholds.size() == 0) {
|
||||
Log.error("Template or templThreshold is empty in task", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
get_value_or(name, task_json, "maskRange", match_task_info_ptr->mask_range, default_ptr->mask_range);
|
||||
return match_task_info_ptr;
|
||||
}
|
||||
|
||||
asst::TaskData::taskptr_t asst::TaskData::generate_ocr_task_info(std::string_view name, const json::value& task_json,
|
||||
std::shared_ptr<OcrTaskInfo> default_ptr)
|
||||
{
|
||||
if (default_ptr == nullptr) {
|
||||
default_ptr = default_ocr_task_info_ptr;
|
||||
}
|
||||
auto ocr_task_info_ptr = std::make_shared<OcrTaskInfo>();
|
||||
|
||||
// text 不允许为字符串,必须是字符串数组,不能用 get_value_or
|
||||
auto array_opt = task_json.find<json::array>("text");
|
||||
ocr_task_info_ptr->text = array_opt ? to_string_list(array_opt.value()) : default_ptr->text;
|
||||
#ifdef ASST_DEBUG
|
||||
if (!array_opt && default_ptr->text.empty()) {
|
||||
Log.warn("Ocr task", name, "has implicit empty text.");
|
||||
}
|
||||
#endif
|
||||
get_value_or(name, task_json, "fullMatch", ocr_task_info_ptr->full_match, default_ptr->full_match);
|
||||
get_value_or(name, task_json, "isAscii", ocr_task_info_ptr->is_ascii, default_ptr->is_ascii);
|
||||
get_value_or(name, task_json, "withoutDet", ocr_task_info_ptr->without_det, default_ptr->without_det);
|
||||
get_value_or(name, task_json, "replaceFull", ocr_task_info_ptr->replace_full, default_ptr->replace_full);
|
||||
get_value_or(name, task_json, "ocrReplace", ocr_task_info_ptr->replace_map, default_ptr->replace_map);
|
||||
return ocr_task_info_ptr;
|
||||
}
|
||||
|
||||
asst::TaskData::taskptr_t asst::TaskData::generate_hash_task_info(std::string_view name, const json::value& task_json,
|
||||
std::shared_ptr<HashTaskInfo> default_ptr)
|
||||
{
|
||||
if (default_ptr == nullptr) {
|
||||
default_ptr = default_hash_task_info_ptr;
|
||||
}
|
||||
auto hash_task_info_ptr = std::make_shared<HashTaskInfo>();
|
||||
// hash 不允许为字符串,必须是字符串数组,不能用 get_value_or
|
||||
auto array_opt = task_json.find<json::array>("hash");
|
||||
hash_task_info_ptr->hashes = array_opt ? to_string_list(array_opt.value()) : default_ptr->hashes;
|
||||
#ifdef ASST_DEBUG
|
||||
if (!array_opt && default_ptr->hashes.empty()) {
|
||||
Log.warn("Hash task", name, "has implicit empty hashes.");
|
||||
}
|
||||
#endif
|
||||
get_value_or(name, task_json, "threshold", hash_task_info_ptr->dist_threshold, default_ptr->dist_threshold);
|
||||
get_value_or(name, task_json, "maskRange", hash_task_info_ptr->mask_range, default_ptr->mask_range);
|
||||
get_value_or(name, task_json, "bound", hash_task_info_ptr->bound, default_ptr->bound);
|
||||
return hash_task_info_ptr;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (default_ptr == nullptr) {
|
||||
default_ptr = default_task_info_ptr;
|
||||
}
|
||||
|
||||
get_value_or(name, task_json, "action", task_info_ptr->action, default_ptr->action);
|
||||
get_value_or(name, task_json, "cache", task_info_ptr->cache, default_ptr->cache);
|
||||
get_value_or(name, task_json, "maxTimes", task_info_ptr->max_times, default_ptr->max_times);
|
||||
get_value_or(name, task_json, "exceededNext", task_info_ptr->exceeded_next,
|
||||
[&]() { return append_prefix(default_ptr->exceeded_next, task_prefix); });
|
||||
get_value_or(name, task_json, "onErrorNext", task_info_ptr->on_error_next,
|
||||
[&]() { return append_prefix(default_ptr->on_error_next, task_prefix); });
|
||||
get_value_or(name, task_json, "preDelay", task_info_ptr->pre_delay, default_ptr->pre_delay);
|
||||
get_value_or(name, task_json, "postDelay", task_info_ptr->post_delay, default_ptr->post_delay);
|
||||
get_value_or(name, task_json, "reduceOtherTimes", task_info_ptr->reduce_other_times,
|
||||
[&]() { return append_prefix(default_ptr->reduce_other_times, task_prefix); });
|
||||
get_value_or(name, task_json, "roi", task_info_ptr->roi, default_ptr->roi);
|
||||
get_value_or(name, task_json, "sub", task_info_ptr->sub,
|
||||
[&]() { return append_prefix(default_ptr->sub, task_prefix); });
|
||||
get_value_or(name, task_json, "subErrorIgnored", task_info_ptr->sub_error_ignored, default_ptr->sub_error_ignored);
|
||||
get_value_or(name, task_json, "next", task_info_ptr->next,
|
||||
[&]() { return append_prefix(default_ptr->next, task_prefix); });
|
||||
get_value_or(name, task_json, "rectMove", task_info_ptr->rect_move, default_ptr->rect_move);
|
||||
get_value_or(name, task_json, "specificRect", task_info_ptr->specific_rect, default_ptr->specific_rect);
|
||||
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;
|
||||
}
|
||||
|
||||
std::shared_ptr<asst::MatchTaskInfo> asst::TaskData::_default_match_task_info()
|
||||
{
|
||||
auto match_task_info_ptr = std::make_shared<MatchTaskInfo>();
|
||||
match_task_info_ptr->templ_names = { "__INVALID__" };
|
||||
match_task_info_ptr->templ_thresholds = { TemplThresholdDefault };
|
||||
|
||||
return match_task_info_ptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<asst::OcrTaskInfo> asst::TaskData::_default_ocr_task_info()
|
||||
{
|
||||
auto ocr_task_info_ptr = std::make_shared<OcrTaskInfo>();
|
||||
ocr_task_info_ptr->full_match = false;
|
||||
ocr_task_info_ptr->is_ascii = false;
|
||||
ocr_task_info_ptr->without_det = false;
|
||||
ocr_task_info_ptr->replace_full = false;
|
||||
|
||||
return ocr_task_info_ptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<asst::HashTaskInfo> asst::TaskData::_default_hash_task_info()
|
||||
{
|
||||
auto hash_task_info_ptr = std::make_shared<HashTaskInfo>();
|
||||
hash_task_info_ptr->dist_threshold = 0;
|
||||
hash_task_info_ptr->bound = true;
|
||||
|
||||
return hash_task_info_ptr;
|
||||
}
|
||||
|
||||
asst::TaskData::taskptr_t asst::TaskData::_default_task_info()
|
||||
{
|
||||
auto task_info_ptr = std::make_shared<TaskInfo>();
|
||||
task_info_ptr->algorithm = AlgorithmType::MatchTemplate;
|
||||
task_info_ptr->action = ProcessTaskAction::DoNothing;
|
||||
task_info_ptr->cache = true;
|
||||
task_info_ptr->max_times = INT_MAX;
|
||||
task_info_ptr->pre_delay = 0;
|
||||
task_info_ptr->post_delay = 0;
|
||||
task_info_ptr->roi = Rect();
|
||||
task_info_ptr->sub_error_ignored = false;
|
||||
task_info_ptr->rect_move = Rect();
|
||||
task_info_ptr->specific_rect = Rect();
|
||||
|
||||
return task_info_ptr;
|
||||
}
|
||||
|
||||
// new_tasks 是目的任务列表
|
||||
// raw_tasks 是源任务表达式列表
|
||||
// task_name 是任务名
|
||||
// task_changed 记录 raw_tasks 在转换为 new_tasks 时是否发生变化
|
||||
// multi 表示是否允许重复
|
||||
bool asst::TaskData::explain_tasks(tasklist_t& new_tasks, const tasklist_t& raw_tasks, std::string_view task_name,
|
||||
bool& task_changed, bool multi)
|
||||
// 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<std::string_view> tasks_set; // 记录任务列表中已有的任务(内容元素与 new_tasks 基本一致)
|
||||
|
||||
@@ -324,7 +678,6 @@ bool asst::TaskData::explain_tasks(tasklist_t& new_tasks, const tasklist_t& raw_
|
||||
[[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;
|
||||
@@ -356,7 +709,7 @@ bool asst::TaskData::explain_tasks(tasklist_t& new_tasks, const tasklist_t& raw_
|
||||
"none", // 18
|
||||
};
|
||||
|
||||
[[maybe_unused]] auto is_symbl_name = [&](symbl_t x) { return x >= symbl_name_start; };
|
||||
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:
|
||||
@@ -464,8 +817,8 @@ bool asst::TaskData::explain_tasks(tasklist_t& new_tasks, const tasklist_t& raw_
|
||||
else if (type == #t) \
|
||||
{ \
|
||||
bool task_changed = false; \
|
||||
if (!explain_tasks(*ret, other_task_info_ptr->t, task_name, task_changed, m)) { \
|
||||
Log.error("Failed to explain task", task + "->" #t, "while perform op", symbl_table[op], "in", task_expr, \
|
||||
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; \
|
||||
} \
|
||||
@@ -500,7 +853,7 @@ bool asst::TaskData::explain_tasks(tasklist_t& new_tasks, const tasklist_t& raw_
|
||||
};
|
||||
|
||||
for (std::string_view task_expr : raw_tasks) {
|
||||
if (task_expr.empty() || (!multi && tasks_set.contains(task_expr))) {
|
||||
if (task_expr.empty() || (!allow_duplicate && tasks_set.contains(task_expr))) {
|
||||
task_changed = true;
|
||||
continue;
|
||||
}
|
||||
@@ -581,12 +934,12 @@ bool asst::TaskData::explain_tasks(tasklist_t& new_tasks, const tasklist_t& raw_
|
||||
|
||||
$subtask = $subtask_type ( $tasks )
|
||||
|
||||
$lambda_task = { $subtask , ... } // 推迟实现
|
||||
| { $tasks } // 推迟实现;不写 subtask_type 默认为 sub
|
||||
$lambda_task = { $subtask , ... } // 暂未实现
|
||||
| { $tasks } // 暂未实现;不写 subtask_type 默认为 sub
|
||||
|
||||
$parens = # $sharp_type
|
||||
| $name
|
||||
| $name $lambda_task // 推迟实现
|
||||
| $name $lambda_task // 暂未实现
|
||||
| ( $tasks )
|
||||
|
||||
$top_tasks = $top_tasks @ $parens
|
||||
@@ -605,7 +958,7 @@ bool asst::TaskData::explain_tasks(tasklist_t& new_tasks, const tasklist_t& raw_
|
||||
using decode_func_t = std::function<std::optional<tasklistptr_t>()>;
|
||||
decode_func_t decode_name, decode_tasks, decode_multasks, decode_vtasks, decode_parens;
|
||||
decode_name = [&]() -> std::optional<tasklistptr_t> {
|
||||
if (*cur >= symbl_name_start) {
|
||||
if (is_symbl_name(*cur)) {
|
||||
return std::make_shared<tasklist_t>(tasklist_t { symbols_table[*(cur++)] });
|
||||
}
|
||||
return std::nullopt;
|
||||
@@ -665,7 +1018,7 @@ bool asst::TaskData::explain_tasks(tasklist_t& new_tasks, const tasklist_t& raw_
|
||||
if (!r) return std::nullopt;
|
||||
return perform_op(task_expr, symbl_sharp, nullptr, *r);
|
||||
}
|
||||
if (*cur >= symbl_name_start) {
|
||||
if (is_symbl_name(*cur)) {
|
||||
return decode_name();
|
||||
}
|
||||
Log.error("Invalid symbol", *cur, "at", cur - symbol_stream.cbegin(), "in", symbol_stream);
|
||||
@@ -677,7 +1030,7 @@ bool asst::TaskData::explain_tasks(tasklist_t& new_tasks, const tasklist_t& raw_
|
||||
}
|
||||
|
||||
for (const auto& task : **opt) {
|
||||
if (task.empty() || (!multi && tasks_set.contains(task))) {
|
||||
if (task.empty() || (!allow_duplicate && tasks_set.contains(task))) {
|
||||
task_changed = true;
|
||||
continue;
|
||||
}
|
||||
@@ -691,353 +1044,6 @@ bool asst::TaskData::explain_tasks(tasklist_t& new_tasks, const tasklist_t& raw_
|
||||
return 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:
|
||||
// 已经显式生成
|
||||
if (m_raw_all_tasks_info.contains(name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 隐式生成的资源
|
||||
if (size_t p = name.find('@'); p != std::string::npos) {
|
||||
return generate_task_and_its_base(name.substr(p + 1), must_true);
|
||||
}
|
||||
|
||||
m_task_status[name] = NotExists;
|
||||
[[fallthrough]];
|
||||
case NotExists:
|
||||
if (must_true) {
|
||||
// 必须有名字为 name 的资源
|
||||
Log.error("Unknown task:", name);
|
||||
}
|
||||
// 不一定必须有名字为 name 的资源,例如 Roguelike@Abandon 不必有 Abandon.
|
||||
return false;
|
||||
case ToBeGenerate: {
|
||||
if (!m_json_all_tasks_info.contains(name)) [[unlikely]] {
|
||||
// 这段正常情况来说是不可能的,除非有 string_view 引用失效
|
||||
Log.error("Unexcepted ToBeGenerate task:", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_task_status[name] = Generating;
|
||||
|
||||
const json::value& task_json = m_json_all_tasks_info.at(name);
|
||||
|
||||
// BaseTask
|
||||
if (auto opt = task_json.find<std::string>("baseTask")) {
|
||||
std::string base = opt.value();
|
||||
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_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_task(name, "", nullptr, task_json);
|
||||
}
|
||||
[[unlikely]] case Generating:
|
||||
Log.error("Task", name, "is generated cyclically");
|
||||
return false;
|
||||
[[unlikely]] default:
|
||||
Log.error("Task", name, "has unknown status");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<asst::TaskData::taskptr_t> asst::TaskData::expand_task(std::string_view name, taskptr_t old_task)
|
||||
{
|
||||
if (old_task == nullptr) [[unlikely]] {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto task_info = _generate_task_info(old_task); // 复制一份
|
||||
bool task_changed = false; // 任务列表是否发生变化
|
||||
#define ASST_TASKDATA_EXPEND_TASK_IF_BRANCH(type, m) \
|
||||
task_info->type.clear(); \
|
||||
if (!explain_tasks(task_info->type, old_task->type, name, task_changed, m)) [[unlikely]] { \
|
||||
Log.error("Generate task_list", std::string(name) + "->" #type, "failed."); \
|
||||
return std::nullopt; \
|
||||
}
|
||||
ASST_TASKDATA_EXPEND_TASK_IF_BRANCH(next, false);
|
||||
ASST_TASKDATA_EXPEND_TASK_IF_BRANCH(sub, true);
|
||||
ASST_TASKDATA_EXPEND_TASK_IF_BRANCH(exceeded_next, false);
|
||||
ASST_TASKDATA_EXPEND_TASK_IF_BRANCH(on_error_next, false);
|
||||
ASST_TASKDATA_EXPEND_TASK_IF_BRANCH(reduce_other_times, true);
|
||||
#undef ASST_TASKDATA_EXPEND_TASK_IF_BRANCH
|
||||
|
||||
// tasks 个数超过上限时不再 emplace,返回临时值
|
||||
constexpr size_t MAX_TASKS_SIZE = 65535;
|
||||
if (m_all_tasks_info.size() < MAX_TASKS_SIZE) [[likely]] {
|
||||
return insert_or_assign_task(name, task_changed ? task_info : old_task).first->second;
|
||||
}
|
||||
else {
|
||||
#ifdef ASST_DEBUG
|
||||
Log.debug("Task count has exceeded the upper limit:", MAX_TASKS_SIZE, "current task:", name);
|
||||
#endif // ASST_DEBUG
|
||||
return task_changed ? task_info : old_task;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (default_ptr == nullptr) {
|
||||
default_ptr = default_task_info_ptr;
|
||||
task_prefix = "";
|
||||
}
|
||||
|
||||
// 获取 algorithm 并按照 algorithm 生成 TaskInfo
|
||||
auto algorithm = default_ptr->algorithm;
|
||||
taskptr_t default_derived_ptr = default_ptr;
|
||||
if (auto opt = task_json.find<std::string>("algorithm")) {
|
||||
std::string algorithm_str = opt.value();
|
||||
algorithm = get_algorithm_type(algorithm_str);
|
||||
if (default_ptr->algorithm != algorithm) {
|
||||
// 相同 algorithm 时才继承派生类成员
|
||||
default_derived_ptr = nullptr;
|
||||
}
|
||||
}
|
||||
taskptr_t task_info_ptr = nullptr;
|
||||
switch (algorithm) {
|
||||
case AlgorithmType::MatchTemplate:
|
||||
task_info_ptr =
|
||||
generate_match_task_info(name, task_json, std::dynamic_pointer_cast<MatchTaskInfo>(default_derived_ptr));
|
||||
break;
|
||||
case AlgorithmType::OcrDetect:
|
||||
task_info_ptr =
|
||||
generate_ocr_task_info(name, task_json, std::dynamic_pointer_cast<OcrTaskInfo>(default_derived_ptr));
|
||||
break;
|
||||
case AlgorithmType::Hash:
|
||||
task_info_ptr =
|
||||
generate_hash_task_info(name, task_json, std::dynamic_pointer_cast<HashTaskInfo>(default_derived_ptr));
|
||||
break;
|
||||
case AlgorithmType::JustReturn:
|
||||
task_info_ptr = std::make_shared<TaskInfo>();
|
||||
break;
|
||||
default:
|
||||
Log.error("Unknown algorithm in task", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (task_info_ptr == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 不管什么algorithm,都有基础成员(next, roi, 等等)
|
||||
if (!append_base_task_info(task_info_ptr, name, task_json, default_ptr, task_prefix)) {
|
||||
return nullptr;
|
||||
}
|
||||
task_info_ptr->algorithm = algorithm;
|
||||
task_info_ptr->name = name;
|
||||
return task_info_ptr;
|
||||
}
|
||||
|
||||
asst::TaskData::taskptr_t asst::TaskData::generate_match_task_info(std::string_view name, const json::value& task_json,
|
||||
std::shared_ptr<MatchTaskInfo> default_ptr)
|
||||
{
|
||||
if (default_ptr == nullptr) {
|
||||
default_ptr = default_match_task_info_ptr;
|
||||
}
|
||||
auto match_task_info_ptr = std::make_shared<MatchTaskInfo>();
|
||||
if (!get_and_check_value(task_json, "template", match_task_info_ptr->templ_names,
|
||||
[&]() { return std::vector { std::string(name) + ".png" }; })) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
m_templ_required.insert(match_task_info_ptr->templ_names.begin(), match_task_info_ptr->templ_names.end());
|
||||
|
||||
// 其余若留空则继承模板任务
|
||||
|
||||
auto threshold_opt = task_json.find("templThreshold");
|
||||
if (!threshold_opt) {
|
||||
match_task_info_ptr->templ_thresholds = default_ptr->templ_thresholds;
|
||||
match_task_info_ptr->templ_thresholds.resize(match_task_info_ptr->templ_names.size(),
|
||||
default_ptr->templ_thresholds.back());
|
||||
}
|
||||
else if (threshold_opt->is_number()) {
|
||||
// 单个数值时,所有模板都使用这个阈值
|
||||
match_task_info_ptr->templ_thresholds.resize(match_task_info_ptr->templ_names.size(),
|
||||
threshold_opt->as_double());
|
||||
}
|
||||
else if (threshold_opt->is_array()) {
|
||||
ranges::copy(threshold_opt->as_array() | views::transform(&ranges::range_value_t<json::array>::as_double),
|
||||
std::back_inserter(match_task_info_ptr->templ_thresholds));
|
||||
}
|
||||
else {
|
||||
Log.error("Invalid templThreshold type in task", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (match_task_info_ptr->templ_names.size() != match_task_info_ptr->templ_thresholds.size()) {
|
||||
Log.error("Template count and templThreshold count not match in task", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (match_task_info_ptr->templ_names.size() == 0 || match_task_info_ptr->templ_thresholds.size() == 0) {
|
||||
Log.error("Template or templThreshold is empty in task", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
get_and_check_value(task_json, "maskRange", match_task_info_ptr->mask_range, default_ptr->mask_range);
|
||||
return match_task_info_ptr;
|
||||
}
|
||||
|
||||
asst::TaskData::taskptr_t asst::TaskData::generate_ocr_task_info([[maybe_unused]] std::string_view name,
|
||||
const json::value& task_json,
|
||||
std::shared_ptr<OcrTaskInfo> default_ptr)
|
||||
{
|
||||
if (default_ptr == nullptr) {
|
||||
default_ptr = default_ocr_task_info_ptr;
|
||||
}
|
||||
auto ocr_task_info_ptr = std::make_shared<OcrTaskInfo>();
|
||||
|
||||
// text 不允许为字符串,必须是字符串数组,不能用 get_and_check_value
|
||||
auto array_opt = task_json.find<json::array>("text");
|
||||
ocr_task_info_ptr->text = array_opt ? to_string_list(array_opt.value()) : default_ptr->text;
|
||||
#ifdef ASST_DEBUG
|
||||
if (!array_opt && default_ptr->text.empty()) {
|
||||
Log.warn("Ocr task", name, "has implicit empty text.");
|
||||
}
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
|
||||
asst::TaskData::taskptr_t asst::TaskData::generate_hash_task_info([[maybe_unused]] std::string_view name,
|
||||
const json::value& task_json,
|
||||
std::shared_ptr<HashTaskInfo> default_ptr)
|
||||
{
|
||||
if (default_ptr == nullptr) {
|
||||
default_ptr = default_hash_task_info_ptr;
|
||||
}
|
||||
auto hash_task_info_ptr = std::make_shared<HashTaskInfo>();
|
||||
// hash 不允许为字符串,必须是字符串数组,不能用 get_and_check_value
|
||||
auto array_opt = task_json.find<json::array>("hash");
|
||||
hash_task_info_ptr->hashes = array_opt ? to_string_list(array_opt.value()) : default_ptr->hashes;
|
||||
#ifdef ASST_DEBUG
|
||||
if (!array_opt && default_ptr->hashes.empty()) {
|
||||
Log.warn("Hash task", name, "has implicit empty hashes.");
|
||||
}
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (default_ptr == nullptr) {
|
||||
default_ptr = default_task_info_ptr;
|
||||
}
|
||||
if (auto opt = task_json.find<std::string>("action")) {
|
||||
std::string action = opt.value();
|
||||
task_info_ptr->action = get_action_type(action);
|
||||
if (task_info_ptr->action == ProcessTaskAction::Invalid) [[unlikely]] {
|
||||
Log.error("Unknown action:", action, ", Task:", name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
task_info_ptr->action = default_ptr->action;
|
||||
}
|
||||
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 (auto [x, y, w, h] = task_info_ptr->roi; x + w > WindowWidthDefault || y + h > WindowHeightDefault) {
|
||||
Log.warn(name, "roi is out of bounds");
|
||||
}
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
|
||||
std::shared_ptr<asst::MatchTaskInfo> asst::TaskData::_default_match_task_info()
|
||||
{
|
||||
auto match_task_info_ptr = std::make_shared<MatchTaskInfo>();
|
||||
match_task_info_ptr->templ_names = { "__INVALID__" };
|
||||
match_task_info_ptr->templ_thresholds = { TemplThresholdDefault };
|
||||
|
||||
return match_task_info_ptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<asst::OcrTaskInfo> asst::TaskData::_default_ocr_task_info()
|
||||
{
|
||||
auto ocr_task_info_ptr = std::make_shared<OcrTaskInfo>();
|
||||
ocr_task_info_ptr->full_match = false;
|
||||
ocr_task_info_ptr->is_ascii = false;
|
||||
ocr_task_info_ptr->without_det = false;
|
||||
ocr_task_info_ptr->replace_full = false;
|
||||
|
||||
return ocr_task_info_ptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<asst::HashTaskInfo> asst::TaskData::_default_hash_task_info()
|
||||
{
|
||||
auto hash_task_info_ptr = std::make_shared<HashTaskInfo>();
|
||||
hash_task_info_ptr->dist_threshold = 0;
|
||||
hash_task_info_ptr->bound = true;
|
||||
|
||||
return hash_task_info_ptr;
|
||||
}
|
||||
|
||||
asst::TaskData::taskptr_t asst::TaskData::_default_task_info()
|
||||
{
|
||||
auto task_info_ptr = std::make_shared<TaskInfo>();
|
||||
task_info_ptr->algorithm = AlgorithmType::MatchTemplate;
|
||||
task_info_ptr->action = ProcessTaskAction::DoNothing;
|
||||
task_info_ptr->cache = true;
|
||||
task_info_ptr->max_times = INT_MAX;
|
||||
task_info_ptr->pre_delay = 0;
|
||||
task_info_ptr->post_delay = 0;
|
||||
task_info_ptr->roi = Rect();
|
||||
task_info_ptr->sub_error_ignored = false;
|
||||
task_info_ptr->rect_move = Rect();
|
||||
task_info_ptr->specific_rect = Rect();
|
||||
|
||||
return task_info_ptr;
|
||||
}
|
||||
|
||||
#ifdef ASST_DEBUG
|
||||
// 为了解决类似 beddc7c828126c678391e0b4da288db6d2c2d58a 导致的问题,加载的时候做一个语法检查
|
||||
// 主要是处理是否包含未知键值的问题
|
||||
@@ -1048,26 +1054,25 @@ bool asst::TaskData::syntax_check(std::string_view task_name, const json::value&
|
||||
static const std::unordered_map<AlgorithmType, std::unordered_set<std::string>> allowed_key_under_algorithm = {
|
||||
{ AlgorithmType::Invalid,
|
||||
{
|
||||
"action", "algorithm", "baseTask", "cache", "exceededNext", "fullMatch",
|
||||
"hash", "isAscii", "maskRange", "maxTimes", "next", "ocrReplace",
|
||||
"onErrorNext", "postDelay", "preDelay", "rectMove", "reduceOtherTimes", "replaceFull", "roi",
|
||||
"specialParams", "sub", "subErrorIgnored", "templThreshold", "template", "text",
|
||||
"threshold", "withoutDet",
|
||||
"action", "algorithm", "baseTask", "cache", "exceededNext", "fullMatch",
|
||||
"hash", "isAscii", "maskRange", "maxTimes", "next", "ocrReplace",
|
||||
"onErrorNext", "postDelay", "preDelay", "rectMove", "reduceOtherTimes", "replaceFull",
|
||||
"roi", "specialParams", "sub", "subErrorIgnored", "templThreshold", "template",
|
||||
"text", "threshold", "withoutDet",
|
||||
} },
|
||||
{ AlgorithmType::MatchTemplate,
|
||||
{
|
||||
"action", "algorithm", "baseTask", "cache", "exceededNext", "maskRange",
|
||||
"maxTimes", "next", "onErrorNext", "postDelay", "preDelay", "rectMove",
|
||||
"reduceOtherTimes", "roi", "sub", "subErrorIgnored", "templThreshold", "template",
|
||||
"specialParams"
|
||||
"specialParams",
|
||||
} },
|
||||
{ AlgorithmType::OcrDetect,
|
||||
{
|
||||
"action", "algorithm", "baseTask", "cache", "exceededNext",
|
||||
"fullMatch", "isAscii", "maxTimes", "next", "ocrReplace",
|
||||
"onErrorNext", "postDelay", "preDelay", "rectMove", "reduceOtherTimes", "replaceFull",
|
||||
"roi", "sub", "subErrorIgnored", "text", "withoutDet",
|
||||
"specialParams"
|
||||
"action", "algorithm", "baseTask", "cache", "exceededNext", "fullMatch",
|
||||
"isAscii", "maxTimes", "next", "ocrReplace", "onErrorNext", "postDelay",
|
||||
"preDelay", "rectMove", "reduceOtherTimes", "replaceFull", "roi", "sub",
|
||||
"subErrorIgnored", "text", "withoutDet", "specialParams",
|
||||
} },
|
||||
{ AlgorithmType::JustReturn,
|
||||
{
|
||||
@@ -1086,10 +1091,7 @@ bool asst::TaskData::syntax_check(std::string_view task_name, const json::value&
|
||||
// clang-format on
|
||||
|
||||
static const std::unordered_map<ProcessTaskAction, std::unordered_set<std::string>> allowed_key_under_action = {
|
||||
{ ProcessTaskAction::ClickRect,
|
||||
{
|
||||
"specificRect",
|
||||
} },
|
||||
{ ProcessTaskAction::ClickRect, { "specificRect" } },
|
||||
{ ProcessTaskAction::Swipe, { "specificRect", "rectMove" } },
|
||||
};
|
||||
|
||||
|
||||
@@ -60,15 +60,14 @@ namespace asst
|
||||
}
|
||||
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_view)
|
||||
static tasklist_t append_prefix(const tasklist_t& base_task_list, std::string_view task_prefix)
|
||||
{
|
||||
if (task_prefix_view.ends_with('@')) [[unlikely]] {
|
||||
task_prefix_view.remove_suffix(1);
|
||||
if (task_prefix.ends_with('@')) [[unlikely]] {
|
||||
task_prefix.remove_suffix(1);
|
||||
}
|
||||
if (task_prefix_view.empty()) {
|
||||
if (task_prefix.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;
|
||||
@@ -77,35 +76,37 @@ namespace asst
|
||||
// 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]] {
|
||||
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, task_prefix));
|
||||
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, std::string_view task_prefix = "")
|
||||
static taskptr_t _generate_task_info(const taskptr_t& base_ptr)
|
||||
{
|
||||
taskptr_t task_info_ptr;
|
||||
if (!base_ptr) [[unlikely]] {
|
||||
return nullptr;
|
||||
}
|
||||
switch (base_ptr->algorithm) {
|
||||
case AlgorithmType::MatchTemplate:
|
||||
task_info_ptr = std::make_shared<MatchTaskInfo>(*std::dynamic_pointer_cast<MatchTaskInfo>(base_ptr));
|
||||
break;
|
||||
return std::make_shared<MatchTaskInfo>(*std::dynamic_pointer_cast<MatchTaskInfo>(base_ptr));
|
||||
case AlgorithmType::OcrDetect:
|
||||
task_info_ptr = std::make_shared<OcrTaskInfo>(*std::dynamic_pointer_cast<OcrTaskInfo>(base_ptr));
|
||||
break;
|
||||
return std::make_shared<OcrTaskInfo>(*std::dynamic_pointer_cast<OcrTaskInfo>(base_ptr));
|
||||
case AlgorithmType::Hash:
|
||||
task_info_ptr = std::make_shared<HashTaskInfo>(*std::dynamic_pointer_cast<HashTaskInfo>(base_ptr));
|
||||
break;
|
||||
return std::make_shared<HashTaskInfo>(*std::dynamic_pointer_cast<HashTaskInfo>(base_ptr));
|
||||
default:
|
||||
task_info_ptr = std::make_shared<TaskInfo>(*base_ptr);
|
||||
break;
|
||||
return std::make_shared<TaskInfo>(*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);
|
||||
@@ -114,7 +115,6 @@ namespace asst
|
||||
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 <ranges::forward_range ListType>
|
||||
@@ -138,14 +138,13 @@ namespace asst
|
||||
{
|
||||
return m_all_tasks_info.insert_or_assign(task_name_view(task_name), task_info_ptr);
|
||||
}
|
||||
bool explain_tasks(tasklist_t& new_tasks, const tasklist_t& raw_tasks, std::string_view name,
|
||||
bool& task_changed, bool multi);
|
||||
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);
|
||||
std::optional<taskptr_t> expand_task(std::string_view name, taskptr_t old_task);
|
||||
#ifdef ASST_DEBUG
|
||||
bool syntax_check(std::string_view task_name, const json::value& task_json);
|
||||
#endif
|
||||
std::shared_ptr<TaskInfo> get_raw(std::string_view name);
|
||||
taskptr_t get_raw(std::string_view name);
|
||||
template <typename TargetTaskInfoType>
|
||||
requires(std::derived_from<TargetTaskInfoType, TaskInfo> &&
|
||||
!std::same_as<TargetTaskInfoType, TaskInfo>) // Parameter must be a TaskInfo
|
||||
@@ -161,7 +160,7 @@ namespace asst
|
||||
void set_task_base(const std::string_view task_name, std::string base_task_name);
|
||||
bool lazy_parse(const json::value& json);
|
||||
|
||||
std::shared_ptr<TaskInfo> get(std::string_view name);
|
||||
taskptr_t get(std::string_view name);
|
||||
template <typename TargetTaskInfoType>
|
||||
requires(std::derived_from<TargetTaskInfoType, TaskInfo> &&
|
||||
!std::same_as<TargetTaskInfoType, TaskInfo>) // Parameter must be a TaskInfo
|
||||
@@ -180,20 +179,20 @@ namespace asst
|
||||
protected:
|
||||
enum TaskStatus
|
||||
{
|
||||
NotToBeGenerate = 0, // 已经显式生成 或 不是待显式生成 的资源
|
||||
ToBeGenerate, // 待生成 的资源
|
||||
Generating, // 正在生成 的资源
|
||||
NotExists, // 不存在的资源
|
||||
NotToBeGenerate = 0, // 已经显式生成 或 不是待显式生成 的任务
|
||||
ToBeGenerate, // 待生成 的任务
|
||||
Generating, // 正在生成 的任务
|
||||
NotExists, // 不存在的任务
|
||||
};
|
||||
|
||||
virtual bool parse(const json::value& json) override;
|
||||
|
||||
std::unordered_set<std::string> m_task_names;
|
||||
std::unordered_map<std::string_view, taskptr_t> m_raw_all_tasks_info;
|
||||
std::unordered_map<std::string_view, taskptr_t> m_all_tasks_info;
|
||||
std::unordered_map<std::string_view, json::object> m_json_all_tasks_info;
|
||||
std::unordered_set<std::string> m_templ_required;
|
||||
std::unordered_map<std::string_view, TaskStatus> m_task_status;
|
||||
std::unordered_map<std::string_view, json::object> m_json_all_tasks_info; // 原始的 json 信息
|
||||
std::unordered_map<std::string_view, taskptr_t> m_raw_all_tasks_info; // 未展开虚任务的任务信息
|
||||
std::unordered_map<std::string_view, taskptr_t> m_all_tasks_info; // 已展开虚任务的任务信息
|
||||
};
|
||||
|
||||
inline static auto& Task = TaskData::get_instance();
|
||||
|
||||
Reference in New Issue
Block a user