mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-20 02:55:38 +08:00
refactor.重构哈希计算的调用逻辑
This commit is contained in:
@@ -1925,21 +1925,25 @@
|
||||
]
|
||||
},
|
||||
"InfrastOperNameHash": {
|
||||
"template": "empty.png",
|
||||
"templThreshold_Doc": "作为哈希距离的阈值使用",
|
||||
"templThreshold": 65,
|
||||
"algorithm": "hash",
|
||||
"hash": [],
|
||||
"threshold": 65,
|
||||
"rectMove_Doc": "基于笑脸的位置移动",
|
||||
"rectMove": [
|
||||
0,
|
||||
20,
|
||||
113,
|
||||
22
|
||||
],
|
||||
"maskRange": [
|
||||
100,
|
||||
255
|
||||
]
|
||||
},
|
||||
"InfrastOperFaceHash": {
|
||||
"template": "empty.png",
|
||||
"templThreshold_Doc": "作为哈希距离的阈值使用",
|
||||
"templThreshold": 30,
|
||||
"algorithm": "hash",
|
||||
"hash": [],
|
||||
"threshold": 30,
|
||||
"rectMove_Doc": "基于笑脸的位置移动",
|
||||
"rectMove": [
|
||||
0,
|
||||
|
||||
@@ -50,48 +50,3 @@ asst::Rect asst::AbstractImageAnalyzer::empty_rect_to_full(const Rect& rect, con
|
||||
{
|
||||
return rect.empty() ? Rect(0, 0, image.cols, image.rows) : rect;
|
||||
}
|
||||
|
||||
std::string asst::AbstractImageAnalyzer::calc_text_hash() const
|
||||
{
|
||||
return calc_text_hash(m_roi);
|
||||
}
|
||||
|
||||
std::string asst::AbstractImageAnalyzer::calc_text_hash(const Rect& roi) const
|
||||
{
|
||||
// 从左往右找到第一个白色点
|
||||
Rect white_roi = roi;
|
||||
constexpr static int HashKernelSize = 16;
|
||||
int threshold = 200;
|
||||
bool find_point = false;
|
||||
for (int i = 0; i != white_roi.width && !find_point; ++i) {
|
||||
for (int j = 0; j != white_roi.height && !find_point; ++j) {
|
||||
cv::Point point(white_roi.x + i, white_roi.y + j);
|
||||
auto value = m_image.at<cv::Vec3b>(point);
|
||||
if (value[0] > threshold && value[1] > threshold && value[2] > threshold) {
|
||||
white_roi.x += i;
|
||||
white_roi.width -= i;
|
||||
find_point = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
cv::Mat image_roi = m_image(utils::make_rect<cv::Rect>(white_roi));
|
||||
cv::Mat bin;
|
||||
cv::cvtColor(image_roi, image_roi, cv::COLOR_BGR2GRAY);
|
||||
cv::threshold(image_roi, bin, threshold, 255, cv::THRESH_BINARY);
|
||||
cv::resize(bin, bin, cv::Size(HashKernelSize, HashKernelSize));
|
||||
std::stringstream hash_value;
|
||||
uchar* pix = bin.data;
|
||||
int tmp_dec = 0;
|
||||
for (int ro = 0; ro < 256; ro++) {
|
||||
tmp_dec = tmp_dec << 1;
|
||||
if ((bool)*pix)
|
||||
tmp_dec++;
|
||||
if (ro % 4 == 3) {
|
||||
hash_value << std::hex << tmp_dec;
|
||||
tmp_dec = 0;
|
||||
}
|
||||
pix++;
|
||||
}
|
||||
return hash_value.str();
|
||||
}
|
||||
|
||||
@@ -21,9 +21,6 @@ namespace asst
|
||||
virtual bool analyze() = 0;
|
||||
void correct_roi() noexcept;
|
||||
|
||||
std::string calc_text_hash() const; // 使用m_roi
|
||||
std::string calc_text_hash(const Rect& roi) const; // 使用参数roi
|
||||
|
||||
AbstractImageAnalyzer& operator=(const AbstractImageAnalyzer&) = delete;
|
||||
AbstractImageAnalyzer& operator=(AbstractImageAnalyzer&&) = delete;
|
||||
|
||||
|
||||
@@ -230,10 +230,10 @@ namespace asst
|
||||
struct HashTaskInfo : public TaskInfo
|
||||
{
|
||||
virtual ~HashTaskInfo() = default;
|
||||
std::vector<std::string> hashs; // 多个哈希值
|
||||
int hamming_threshold = 0; // 汉明距离阈值
|
||||
std::pair<int, int> mask_range; // 掩码的二值化范围
|
||||
bool bound = false; // 是否裁剪周围黑边
|
||||
std::vector<std::string> hashs; // 需要多个哈希值
|
||||
int dist_threshold = 0; // 汉明距离阈值
|
||||
std::pair<int, int> mask_range; // 掩码的二值化范围
|
||||
bool bound = false; // 是否裁剪周围黑边
|
||||
};
|
||||
|
||||
struct HandleInfo
|
||||
|
||||
@@ -150,23 +150,6 @@ namespace asst
|
||||
return str;
|
||||
}
|
||||
|
||||
inline int hamming(std::string hash1, std::string hash2)
|
||||
{
|
||||
constexpr static int HammingFlags = 64;
|
||||
|
||||
hash1.insert(hash1.begin(), HammingFlags - hash1.size(), '0');
|
||||
hash2.insert(hash2.begin(), HammingFlags - hash2.size(), '0');
|
||||
int dist = 0;
|
||||
for (int i = 0; i < HammingFlags; i = i + 16) {
|
||||
unsigned long long x = strtoull(hash1.substr(i, 16).c_str(), nullptr, 16) ^ strtoull(hash2.substr(i, 16).c_str(), nullptr, 16);
|
||||
while (x) {
|
||||
dist++;
|
||||
x = x & (x - 1);
|
||||
}
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
|
||||
inline std::string callcmd(const std::string& cmdline)
|
||||
{
|
||||
constexpr int PipeBuffSize = 4096;
|
||||
|
||||
@@ -132,7 +132,7 @@ int asst::BattleImageAnalyzer::oper_cost_analyze(const Rect& roi)
|
||||
hash_analyzer.analyze();
|
||||
|
||||
int cost = 0;
|
||||
for (const std::string& num_name : hash_analyzer.get_result()) {
|
||||
for (const std::string& num_name : hash_analyzer.get_min_dist_name()) {
|
||||
if (num_name.empty()) {
|
||||
Log.error("hash result is empty");
|
||||
return 0;
|
||||
|
||||
@@ -5,13 +5,15 @@
|
||||
bool asst::HashImageAnalyzer::analyze()
|
||||
{
|
||||
m_hash_result.clear();
|
||||
m_hamming_result.clear();
|
||||
m_result.clear();
|
||||
m_min_dist_name.clear();
|
||||
|
||||
cv::Mat roi = m_image(utils::make_rect<cv::Rect>(m_roi));
|
||||
|
||||
if (m_mask_range.first != 0 || m_mask_range.second != 0) {
|
||||
cv::Mat bin;
|
||||
if (roi.channels() == 3) {
|
||||
cv::cvtColor(roi, roi, cv::COLOR_BGR2GRAY);
|
||||
}
|
||||
cv::inRange(roi, m_mask_range.first, m_mask_range.second, bin);
|
||||
roi = bin;
|
||||
}
|
||||
@@ -30,21 +32,16 @@ bool asst::HashImageAnalyzer::analyze()
|
||||
}
|
||||
std::string hash_result = shash(to_hash);
|
||||
|
||||
decltype(m_hamming_result)::value_type cur_hamming;
|
||||
std::string min_hm_name;
|
||||
|
||||
int min_hamming = INT_MAX;
|
||||
int min_dist = INT_MAX;
|
||||
std::string cur_min_dist_name;
|
||||
for (auto&& [name, templ] : m_hash_templates) {
|
||||
int hm = hamming(hash_result, templ);
|
||||
cur_hamming.emplace(name, hm);
|
||||
if (hm < min_hamming) {
|
||||
min_hm_name = name;
|
||||
min_hamming = hm;
|
||||
if (hm < min_dist) {
|
||||
cur_min_dist_name = name;
|
||||
min_dist = hm;
|
||||
}
|
||||
}
|
||||
m_result.emplace_back(min_hm_name);
|
||||
m_hamming_result.emplace_back(std::move(cur_hamming));
|
||||
|
||||
m_min_dist_name.emplace_back(std::move(cur_min_dist_name));
|
||||
m_hash_result.emplace_back(std::move(hash_result));
|
||||
}
|
||||
|
||||
@@ -76,16 +73,26 @@ void asst::HashImageAnalyzer::set_need_bound(bool need_bound) noexcept
|
||||
m_need_bound = need_bound;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& asst::HashImageAnalyzer::get_result() const noexcept
|
||||
const std::vector<std::string>& asst::HashImageAnalyzer::get_min_dist_name() const noexcept
|
||||
{
|
||||
return m_result;
|
||||
return m_min_dist_name;
|
||||
}
|
||||
|
||||
std::string asst::HashImageAnalyzer::shash(const cv::Mat& gray)
|
||||
const std::vector<std::string>& asst::HashImageAnalyzer::get_hash() const noexcept
|
||||
{
|
||||
return m_hash_result;
|
||||
}
|
||||
|
||||
std::string asst::HashImageAnalyzer::shash(const cv::Mat& img)
|
||||
{
|
||||
constexpr static int HashKernelSize = 16;
|
||||
cv::Mat resized;
|
||||
cv::resize(gray, resized, cv::Size(HashKernelSize, HashKernelSize));
|
||||
cv::resize(img, resized, cv::Size(HashKernelSize, HashKernelSize));
|
||||
if (img.channels() == 3) {
|
||||
cv::Mat temp;
|
||||
cv::cvtColor(resized, temp, cv::COLOR_BGR2GRAY);
|
||||
resized = temp;
|
||||
}
|
||||
std::stringstream hash_value;
|
||||
uchar* pix = resized.data;
|
||||
int tmp_dec = 0;
|
||||
|
||||
@@ -19,21 +19,22 @@ namespace asst
|
||||
void set_need_split(bool need_split) noexcept;
|
||||
void set_need_bound(bool need_bound) noexcept;
|
||||
|
||||
const std::vector<std::string>& get_result() const noexcept;
|
||||
const std::vector<std::string>& get_min_dist_name() const noexcept;
|
||||
const std::vector<std::string>& get_hash() const noexcept;
|
||||
|
||||
protected:
|
||||
static std::string shash(const cv::Mat& gray);
|
||||
static std::string shash(const cv::Mat& img);
|
||||
static int hamming(std::string hash1, std::string hash2);
|
||||
static std::vector<cv::Mat> split_bin(const cv::Mat& bin);
|
||||
static cv::Mat bound_bin(const cv::Mat& bin);
|
||||
|
||||
protected:
|
||||
|
||||
std::pair<int, int> m_mask_range;
|
||||
std::unordered_map<std::string, std::string> m_hash_templates;
|
||||
bool m_need_split = false;
|
||||
bool m_need_bound = false;
|
||||
|
||||
std::vector<std::string> m_hash_result;
|
||||
std::vector<std::unordered_map<std::string, int>> m_hamming_result;
|
||||
std::vector<std::string> m_result;
|
||||
std::vector<std::string> m_min_dist_name;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,15 +11,6 @@
|
||||
#include "Resource.h"
|
||||
#include "ProcessTask.h"
|
||||
|
||||
asst::InfrastAbstractTask::InfrastAbstractTask(AsstCallback callback, void* callback_arg, std::string task_chain)
|
||||
: AbstractTask(callback, callback_arg, std::move(task_chain))
|
||||
{
|
||||
m_face_hash_thres = static_cast<int>(std::dynamic_pointer_cast<MatchTaskInfo>(
|
||||
Task.get("InfrastOperFaceHash"))->templ_threshold);
|
||||
m_name_hash_thres = static_cast<int>(std::dynamic_pointer_cast<MatchTaskInfo>(
|
||||
Task.get("InfrastOperNameHash"))->templ_threshold);
|
||||
}
|
||||
|
||||
asst::InfrastAbstractTask& asst::InfrastAbstractTask::set_work_mode(infrast::WorkMode work_mode) noexcept
|
||||
{
|
||||
m_work_mode = work_mode;
|
||||
|
||||
@@ -9,7 +9,6 @@ namespace asst
|
||||
{
|
||||
public:
|
||||
using AbstractTask::AbstractTask;
|
||||
InfrastAbstractTask(AsstCallback callback, void* callback_arg, std::string task_chain);
|
||||
|
||||
virtual ~InfrastAbstractTask() = default;
|
||||
InfrastAbstractTask& set_work_mode(infrast::WorkMode work_mode) noexcept;
|
||||
@@ -44,8 +43,5 @@ namespace asst
|
||||
double m_mood_threshold = 0;
|
||||
mutable std::string m_facility_name_cache;
|
||||
int m_cur_facility_index = 0;
|
||||
|
||||
int m_face_hash_thres = 0;
|
||||
int m_name_hash_thres = 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "InfrastSmileyImageAnalyzer.h"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "HashImageAnalyzer.h"
|
||||
#include "Logger.hpp"
|
||||
#include "Resource.h"
|
||||
|
||||
@@ -208,13 +209,12 @@ void asst::InfrastOperImageAnalyzer::face_hash_analyze()
|
||||
|
||||
const Rect hash_rect_move = Task.get("InfrastOperFaceHash")->rect_move;
|
||||
|
||||
HashImageAnalyzer hash_analyzer(m_image);
|
||||
for (auto&& oper : m_result) {
|
||||
Rect roi = hash_rect_move;
|
||||
roi.x += oper.smiley.rect.x;
|
||||
roi.y += oper.smiley.rect.y;
|
||||
|
||||
cv::Mat image_roi = m_image(utils::make_rect<cv::Rect>(roi));
|
||||
oper.face_hash = hash_calc(image_roi);
|
||||
Rect roi = oper.smiley.rect.move(hash_rect_move);
|
||||
hash_analyzer.set_roi(roi);
|
||||
hash_analyzer.analyze();
|
||||
oper.face_hash = hash_analyzer.get_hash().front();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,43 +222,17 @@ void asst::InfrastOperImageAnalyzer::name_hash_analyze()
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
const Rect hash_rect_move = Task.get("InfrastOperNameHash")->rect_move;
|
||||
|
||||
cv::Mat gray;
|
||||
cv::cvtColor(m_image, gray, cv::COLOR_BGR2GRAY);
|
||||
const auto task_ptr = std::dynamic_pointer_cast<HashTaskInfo>(
|
||||
Task.get("InfrastOperNameHash"));
|
||||
|
||||
HashImageAnalyzer hash_analyzer(m_image);
|
||||
hash_analyzer.set_mask_range(task_ptr->mask_range);
|
||||
hash_analyzer.set_need_bound(true);
|
||||
for (auto&& oper : m_result) {
|
||||
Rect roi = hash_rect_move;
|
||||
roi.x += oper.smiley.rect.x;
|
||||
roi.y += oper.smiley.rect.y;
|
||||
|
||||
constexpr static int threshold = 100;
|
||||
auto check_point = [&](cv::Point point) -> bool {
|
||||
auto value = gray.at<uchar>(point);
|
||||
return value > threshold;
|
||||
};
|
||||
// 找到四个方向上最靠外的白色点,把ROI缩小裁出来
|
||||
int left = -1, right = -1, top = INT_MAX, bottom = -1;
|
||||
for (int i = 0; i != roi.width; ++i) {
|
||||
for (int j = 0; j != roi.height; ++j) {
|
||||
cv::Point point(roi.x + i, roi.y + j);
|
||||
if (check_point(point)) {
|
||||
if (left < 0) {
|
||||
left = i;
|
||||
}
|
||||
right = i;
|
||||
top = (std::min)(top, j);
|
||||
bottom = (std::max)(bottom, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
roi.x += left;
|
||||
roi.width = right - left + 1;
|
||||
roi.y += top;
|
||||
roi.height = bottom - top + 1;
|
||||
|
||||
cv::Mat hash_roi = m_image(utils::make_rect<cv::Rect>(roi));
|
||||
oper.name_hash = hash_calc(hash_roi);
|
||||
Rect roi = oper.smiley.rect.move(task_ptr->rect_move);
|
||||
hash_analyzer.set_roi(roi);
|
||||
hash_analyzer.analyze();
|
||||
oper.name_hash = hash_analyzer.get_hash().front();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,28 +422,3 @@ void asst::InfrastOperImageAnalyzer::doing_analyze()
|
||||
// TODO: infrast::Doing::Resting的识别
|
||||
}
|
||||
}
|
||||
|
||||
std::string asst::InfrastOperImageAnalyzer::hash_calc(const cv::Mat image)
|
||||
{
|
||||
//constexpr static int HashKernelSize = 16;
|
||||
const static cv::Size HashKernel(16, 16);
|
||||
|
||||
cv::Mat hash_img;
|
||||
cv::resize(image, hash_img, HashKernel);
|
||||
cv::cvtColor(hash_img, hash_img, cv::COLOR_BGR2GRAY);
|
||||
|
||||
std::stringstream hash_value;
|
||||
cv::uint8_t* pix = hash_img.data;
|
||||
int tmp_dec = 0;
|
||||
for (int ro = 0; ro < 256; ro++) {
|
||||
tmp_dec = tmp_dec << 1;
|
||||
if (*pix > 127)
|
||||
tmp_dec++;
|
||||
if (ro % 4 == 3) {
|
||||
hash_value << std::hex << tmp_dec;
|
||||
tmp_dec = 0;
|
||||
}
|
||||
pix++;
|
||||
}
|
||||
return hash_value.str();
|
||||
}
|
||||
|
||||
@@ -68,8 +68,6 @@ namespace asst
|
||||
void selected_analyze();
|
||||
void doing_analyze();
|
||||
|
||||
static std::string hash_calc(const cv::Mat image);
|
||||
|
||||
std::string m_facility;
|
||||
std::vector<infrast::Oper> m_result;
|
||||
int m_to_be_calced = All;
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
#include <calculator/calculator.hpp>
|
||||
|
||||
#include "AsstUtils.hpp"
|
||||
#include "Controller.h"
|
||||
#include "InfrastOperImageAnalyzer.h"
|
||||
#include "HashImageAnalyzer.h"
|
||||
#include "Logger.hpp"
|
||||
#include "MatchImageAnalyzer.h"
|
||||
#include "MultiMatchImageAnalyzer.h"
|
||||
@@ -189,6 +189,8 @@ size_t asst::InfrastProductionTask::opers_detect()
|
||||
const auto& cur_all_opers = oper_analyzer.get_result();
|
||||
max_num_of_opers_per_page = (std::max)(max_num_of_opers_per_page, cur_all_opers.size());
|
||||
|
||||
const int face_hash_thres = std::dynamic_pointer_cast<HashTaskInfo>(
|
||||
Task.get("InfrastOperFaceHash"))->dist_threshold;
|
||||
int cur_available_num = static_cast<int>(cur_all_opers.size());
|
||||
for (const auto& cur_oper : cur_all_opers) {
|
||||
if (cur_oper.skills.empty()) {
|
||||
@@ -204,9 +206,9 @@ size_t asst::InfrastProductionTask::opers_detect()
|
||||
m_all_available_opers.cbegin(), m_all_available_opers.cend(),
|
||||
[&](const infrast::Oper& oper) -> bool {
|
||||
// 有可能是同一个干员,比一下hash
|
||||
int dist = utils::hamming(cur_oper.face_hash, oper.face_hash);
|
||||
int dist = HashImageAnalyzer::hamming(cur_oper.face_hash, oper.face_hash);
|
||||
Log.debug("opers_detect hash dist |", dist);
|
||||
return dist < m_face_hash_thres;
|
||||
return dist < face_hash_thres;
|
||||
});
|
||||
// 如果两个的hash距离过小,则认为是同一个干员,不进行插入
|
||||
if (find_iter != m_all_available_opers.cend()) {
|
||||
@@ -291,6 +293,9 @@ bool asst::InfrastProductionTask::optimal_calc()
|
||||
return true;
|
||||
}
|
||||
|
||||
const int name_hash_thres = std::dynamic_pointer_cast<HashTaskInfo>(
|
||||
Task.get("InfrastOperNameHash"))->dist_threshold;
|
||||
|
||||
// 遍历所有组合,找到效率最高的
|
||||
auto& all_group = Resrc.infrast().get_skills_group(facility_name());
|
||||
for (const infrast::SkillsGroup& group : all_group) {
|
||||
@@ -370,9 +375,9 @@ bool asst::InfrastProductionTask::optimal_calc()
|
||||
bool hash_matched = false;
|
||||
if (!opt.possible_hashs.empty()) {
|
||||
for (const auto& [key, hash] : opt.possible_hashs) {
|
||||
int dist = utils::hamming(find_iter->name_hash, hash);
|
||||
int dist = HashImageAnalyzer::hamming(find_iter->name_hash, hash);
|
||||
Log.debug("optimal_calc | name hash dist", dist, hash, find_iter->name_hash);
|
||||
if (dist < m_name_hash_thres) {
|
||||
if (dist < name_hash_thres) {
|
||||
hash_matched = true;
|
||||
break;
|
||||
}
|
||||
@@ -448,6 +453,10 @@ bool asst::InfrastProductionTask::opers_choose()
|
||||
auto& facility_info = Resrc.infrast().get_facility_info(facility_name());
|
||||
int cur_max_num_of_opers = facility_info.max_num_of_opers - m_cur_num_of_lokced_opers;
|
||||
|
||||
const int name_hash_thres = std::dynamic_pointer_cast<HashTaskInfo>(
|
||||
Task.get("InfrastOperNameHash"))->dist_threshold;
|
||||
const int face_hash_thres = std::dynamic_pointer_cast<HashTaskInfo>(
|
||||
Task.get("InfrastOperFaceHash"))->dist_threshold;
|
||||
while (true) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
@@ -496,9 +505,9 @@ bool asst::InfrastProductionTask::opers_choose()
|
||||
else {
|
||||
// 既要技能相同,也要hash相同,双重校验
|
||||
for (const auto& [_, hash] : opt_iter->possible_hashs) {
|
||||
int dist = utils::hamming(lhs.name_hash, hash);
|
||||
int dist = HashImageAnalyzer::hamming(lhs.name_hash, hash);
|
||||
Log.debug("opers_choose | name hash dist", dist);
|
||||
if (dist < m_name_hash_thres) {
|
||||
if (dist < name_hash_thres) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -525,9 +534,9 @@ bool asst::InfrastProductionTask::opers_choose()
|
||||
auto avlb_iter = std::find_if(
|
||||
m_all_available_opers.cbegin(), m_all_available_opers.cend(),
|
||||
[&](const infrast::Oper& lhs) -> bool {
|
||||
int dist = utils::hamming(lhs.face_hash, find_iter->face_hash);
|
||||
int dist = HashImageAnalyzer::hamming(lhs.face_hash, find_iter->face_hash);
|
||||
Log.debug("opers_choose | face hash dist", dist);
|
||||
if (dist < m_face_hash_thres) {
|
||||
if (dist < face_hash_thres) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -103,7 +103,7 @@ bool asst::TaskData::parse(const json::value& json)
|
||||
for (const json::value& hash : task_json.at("hash").as_array()) {
|
||||
hash_task_info_ptr->hashs.emplace_back(hash.as_string());
|
||||
}
|
||||
hash_task_info_ptr->hamming_threshold = task_json.get("threshold", 0.0);
|
||||
hash_task_info_ptr->dist_threshold = task_json.get("threshold", 0.0);
|
||||
if (task_json.exist("maskRange")) {
|
||||
hash_task_info_ptr->mask_range = std::make_pair(
|
||||
task_json.at("maskRange")[0].as_integer(),
|
||||
@@ -217,4 +217,4 @@ bool asst::TaskData::parse(const json::value& json)
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user