Merge pull request #1715 from MaaAssistantArknights/refactor/report

refactor: 重构数据汇报模块,新增支持企鹅物流的幂等机制
This commit is contained in:
MistEO
2022-08-31 22:50:25 +08:00
committed by GitHub
11 changed files with 444 additions and 108 deletions

View File

@@ -14,7 +14,7 @@
"adbExtraSwipeDuration_Doc": "额外的滑动持续时间adb有bug同样的参数偶尔会划得非常远。额外做一个短程滑动把之前的停下来。若小于0则关闭额外滑动功能",
"penguinReport": {
"Doc": "企鹅物流汇报: https://penguin-stats.cn/",
"cmdFormat": "curl -H \"Content-Type: application/json\" -s -S -m 5 -i -d \"[body]\" \"https://penguin-stats.cn/PenguinStats/api/v2/report\" [extra]",
"cmdFormat": "curl -H \"Content-Type: application/json\" -s -S -m 10 -i -d \"[body]\" \"https://penguin-stats.io/PenguinStats/api/v2/report\" [extra]",
"cmdFormat_Doc": "命令格式,想打印详细信息可以尝试添加 -v -i"
},
"yituliuReport": {

View File

@@ -0,0 +1,131 @@
#pragma once
#include "AsstRanges.hpp"
#include "AsstUtils.hpp"
#include <cctype>
#include <string>
#include <unordered_map>
#include <unordered_set>
namespace asst::http
{
class Response : public std::string
{
private:
std::string m_last_error;
unsigned m_status_code = 0;
std::string_view m_protocol_version;
std::string_view m_status_code_info;
std::string_view m_data;
std::unordered_map<std::string, std::string_view> m_headers;
bool _analyze_status_line(std::string_view status_line)
{
size_t _word_count = 0;
for (const std::string_view& word : views::split(status_line, ' ') | views::transform([](auto str_range) {
return utils::view_cast<std::string_view>(str_range);
})) {
++_word_count;
if (_word_count == 1) {
static const std::unordered_set<std::string_view> accepted_protocol_version = { "HTTP/1.1" };
if (!accepted_protocol_version.contains(utils::view_cast<std::string_view>(word))) {
m_last_error = "unsupport protocol version";
return false;
}
m_protocol_version = utils::view_cast<std::string_view>(word);
}
else if (_word_count == 2) {
if (word.length() != 3) {
m_last_error = "status code length is not 3";
return false;
}
if (!ranges::all_of(word, [](char c) -> bool { return std::isdigit(c); })) {
return false;
}
m_status_code = std::atoi(word.data());
}
else {
m_status_code_info = std::string_view(word.begin(), status_line.end());
return true;
}
}
if (_word_count == 2) {
m_status_code_info = "";
return true;
}
m_last_error = "status line too short";
return false;
}
bool _analyze_headers_line(std::string_view status_line)
{
size_t _colon_pos = status_line.find(':');
if (_colon_pos == status_line.npos) {
m_last_error = "connot decode header `" + std::string(status_line) + "`";
return false;
}
m_headers[utils::view_cast<std::string>(status_line.substr(0, _colon_pos) | views::transform([](char c) {
return static_cast<char>(std::tolower(c));
}))] =
status_line.substr(_colon_pos + 1 + status_line.substr(_colon_pos + 1).find_first_not_of(' '));
return true;
}
public:
template <typename... Args>
requires std::is_constructible_v<std::string, Args...> Response(Args&&... args)
: std::string(std::forward<Args>(args)...)
{
bool _is_status_line = true;
// 这里的 \r\n 处理很奇怪,因为 views::split 好像不支持 string只能 char
for (const auto& line : views::split(*this, '\n')) {
std::string_view line_view;
if (utils::view_cast<std::string_view>(line).ends_with('\r')) {
line_view = utils::view_cast<std::string_view>(line | views::take(line.size() - 1));
}
else {
line_view = utils::view_cast<std::string_view>(line);
}
// status
if (_is_status_line) {
if (!_analyze_status_line(line_view)) {
return;
}
_is_status_line = false;
}
// headers
else if (!line_view.empty()) {
if (!_analyze_headers_line(line_view)) {
return;
}
}
// data
else {
m_data = std::string_view(line.begin(), this->end());
return;
}
}
}
unsigned status_code() const { return m_status_code; }
std::string_view protocol_version() const { return m_protocol_version; }
std::string_view status_code_info() const { return m_status_code_info; }
std::string_view data() const { return m_data; }
std::optional<std::string_view> find_header(const std::string& key) const
{
if (auto iter = m_headers.find(key); iter != m_headers.cend()) {
return iter->second;
}
else {
return std::nullopt;
}
}
const auto& headers() const { return m_headers; }
const std::string& get_last_error() const noexcept { return m_last_error; }
bool success() const { return m_status_code == 200; }
bool status_2xx() const { return m_status_code >= 200 && m_status_code < 300; }
bool status_3xx() const { return m_status_code >= 300 && m_status_code < 400; }
bool status_4xx() const { return m_status_code >= 400 && m_status_code < 500; }
bool status_5xx() const { return m_status_code >= 500 && m_status_code < 600; }
};
} // namespace asst::http

View File

@@ -1,6 +1,7 @@
#pragma once
#include "AsstConf.h"
#include "AsstRanges.hpp"
#include <fstream>
#include <sstream>
@@ -29,6 +30,13 @@ namespace asst::utils
struct integral_constant_at_template_instantiation : std::integral_constant<T, Val>
{};
template <typename dst_t, typename src_t>
requires ranges::range<src_t> && ranges::range<dst_t>
dst_t view_cast(const src_t& src)
{
return dst_t(src.begin(), src.end());
}
inline void _string_replace_all(std::string& str, const std::string_view& old_value,
const std::string_view& new_value)
{

View File

@@ -7,6 +7,7 @@
#include "ProcessTask.h"
#include "RecruitConfiger.h"
#include "RecruitImageAnalyzer.h"
#include "ReportDataTask.h"
#include "Resource.h"
#include "AsstRanges.hpp"
@@ -454,16 +455,24 @@ asst::AutoRecruitTask::calc_task_result_type asst::AutoRecruitTask::recruit_calc
callback(AsstMsg::SubTaskExtraInfo, cb_info);
}
bool to_report = false;
if (!is_calc_only_task()) {
// report if the slot is clean
if (!m_dirty_slots.contains(index)) {
async_upload_result(info["details"]);
to_report = true;
m_dirty_slots.emplace(index); // mark as dirty
}
else
Log.info("will not report, dirty slots are", m_dirty_slots);
}
#ifdef ASST_DEBUG
to_report = true;
#endif
if (to_report) {
upload_result(info["details"]);
}
if (need_exit()) return {};
// refresh
@@ -664,16 +673,14 @@ bool asst::AutoRecruitTask::hire_all()
return true;
}
void asst::AutoRecruitTask::async_upload_result(const json::value& details)
void asst::AutoRecruitTask::upload_result(const json::value& details)
{
LogTraceFunction;
if (m_upload_to_penguin) {
auto upload_future = std::async(std::launch::async, &AutoRecruitTask::upload_to_penguin, this, details);
m_upload_pending.emplace_back(std::move(upload_future));
upload_to_penguin(details);
}
if (m_upload_to_yituliu) {
auto upload_future = std::async(std::launch::async, &AutoRecruitTask::upload_to_yituliu, this, details);
m_upload_pending.emplace_back(std::move(upload_future));
upload_to_yituliu(details);
}
}
@@ -681,12 +688,6 @@ void asst::AutoRecruitTask::upload_to_penguin(const json::value& details)
{
LogTraceFunction;
auto& opt = Resrc.cfg().get_options();
json::value cb_info = basic_info();
cb_info["subtask"] = "ReportToPenguinStats";
callback(AsstMsg::SubTaskStart, cb_info);
json::value body;
body["server"] = m_server;
body["stageId"] = "recruit";
@@ -701,85 +702,69 @@ void asst::AutoRecruitTask::upload_to_penguin(const json::value& details)
body["source"] = "MeoAssistant";
body["version"] = Version;
std::string body_escape = utils::string_replace_all_batch(body.to_string(), { { "\"", "\\\"" } });
#ifdef _WIN32
std::string body_escapes = utils::utf8_to_unicode_escape(body_escape);
#else
std::string body_escapes = body_escape;
#endif
std::string extra_param;
if (!m_penguin_id.empty()) {
extra_param = "-H \"authorization: PenguinID " + m_penguin_id + "\"";
}
std::string cmd_line = utils::string_replace_all_batch(opt.penguin_report.cmd_format,
{ { "[body]", body_escapes }, { "[extra]", extra_param } });
Log.info("request_penguin |", cmd_line);
std::string response = utils::callcmd(cmd_line);
Log.info("response:\n", response);
cb_info["details"]["response"] = response;
static const std::regex http_ok_regex(R"(HTTP/.+ 200)");
if (std::regex_search(response, http_ok_regex)) {
callback(AsstMsg::SubTaskCompleted, cb_info);
}
else {
cb_info["why"] = "上报失败";
callback(AsstMsg::SubTaskError, cb_info);
if (!m_report_penguin_task_ptr) {
m_report_penguin_task_ptr =
std::make_shared<ReportDataTask>(report_penguin_callback, static_cast<void*>(this), m_task_chain);
}
static const std::regex penguinid_regex(R"(X-Penguin-Set-Penguinid: (\d+))");
std::smatch penguinid_sm;
if (std::regex_search(response, penguinid_sm, penguinid_regex)) {
json::value id_info = basic_info_with_what("PenguinId");
m_penguin_id = penguinid_sm[1];
id_info["details"]["id"] = m_penguin_id;
callback(AsstMsg::SubTaskExtraInfo, id_info);
}
m_report_penguin_task_ptr->set_report_type(ReportType::PenguinStats)
.set_body(body.to_string())
.set_extra_param(extra_param)
.set_retry_times(5)
.run();
}
void asst::AutoRecruitTask::upload_to_yituliu(const json::value& details)
{
LogTraceFunction;
auto& opt = Resrc.cfg().get_options();
json::value cb_info = basic_info();
cb_info["subtask"] = "ReportToYituliu";
callback(AsstMsg::SubTaskStart, cb_info);
json::value body = details;
body["server"] = m_server;
body["source"] = "MeoAssistant";
body["version"] = Version;
body["uuid"] = m_yituliu_id;
std::string body_escape = utils::string_replace_all_batch(body.to_string(), { { "\"", "\\\"" } });
#ifdef _WIN32
std::string body_escapes = utils::utf8_to_unicode_escape(body_escape);
#else
std::string body_escapes = body_escape;
#endif
std::string cmd_line = utils::string_replace_all_batch(opt.yituliu_report.cmd_format,
{ { "[body]", body_escapes }, { "[extra]", "" } });
Log.trace("request_yituliu |", cmd_line);
std::string response = utils::callcmd(cmd_line);
Log.trace("request_yituliu | response:\n", response);
cb_info["details"]["response"] = response;
static const std::regex http_ok_regex(R"(HTTP/.+ 200)");
if (std::regex_search(response, http_ok_regex)) {
callback(AsstMsg::SubTaskCompleted, cb_info);
}
else {
cb_info["why"] = "上报失败";
callback(AsstMsg::SubTaskError, cb_info);
if (!m_report_yituliu_task_ptr) {
m_report_yituliu_task_ptr =
std::make_shared<ReportDataTask>(report_yituliu_callback, static_cast<void*>(this), m_task_chain);
}
m_report_yituliu_task_ptr->set_report_type(ReportType::YituliuBigData)
.set_body(body.to_string())
.set_retry_times(1)
.run();
}
void asst::AutoRecruitTask::report_penguin_callback(AsstMsg msg, const json::value& detail, void* custom_arg)
{
LogTraceFunction;
auto p_this = static_cast<AutoRecruitTask*>(custom_arg);
if (!p_this) {
return;
}
if (msg == AsstMsg::SubTaskExtraInfo && detail.get("what", std::string()) == "PenguinId") {
std::string id = detail.get("details", "id", std::string());
p_this->m_penguin_id = id;
}
p_this->callback(msg, detail);
}
void asst::AutoRecruitTask::report_yituliu_callback(AsstMsg msg, const json::value& detail, void* custom_arg)
{
LogTraceFunction;
auto p_this = static_cast<AutoRecruitTask*>(custom_arg);
if (!p_this) {
return;
}
p_this->callback(msg, detail);
}

View File

@@ -3,13 +3,14 @@
#include "AsstTypes.h"
#include <future>
#include <optional>
#include <set>
#include <vector>
namespace asst
{
class ReportDataTask;
class AutoRecruitTask final : public AbstractTask
{
public:
@@ -46,9 +47,11 @@ namespace asst
bool initialize_dirty_slot_info(const cv::Mat&);
static std::vector<TextRect> start_recruit_analyze(const cv::Mat& image);
void async_upload_result(const json::value& details);
void upload_result(const json::value& details);
void upload_to_penguin(const json::value& details);
void upload_to_yituliu(const json::value& details);
static void report_penguin_callback(AsstMsg msg, const json::value& detail, void* custom_arg);
static void report_yituliu_callback(AsstMsg msg, const json::value& detail, void* custom_arg);
using slot_index = size_t;
@@ -88,7 +91,8 @@ namespace asst
bool m_upload_to_yituliu = false;
std::string m_penguin_id;
std::string m_yituliu_id;
std::vector<std::future<void>> m_upload_pending;
std::shared_ptr<ReportDataTask> m_report_penguin_task_ptr = nullptr;
std::shared_ptr<ReportDataTask> m_report_yituliu_task_ptr = nullptr;
static slot_index slot_index_from_rect(const Rect& r)
{

View File

@@ -19,6 +19,7 @@
<ClInclude Include="AbstractTask.h" />
<ClInclude Include="AbstractTaskPlugin.h" />
<ClInclude Include="Assistant.h" />
<ClInclude Include="AsstHttp.hpp" />
<ClInclude Include="AsstInfrastDef.h" />
<ClInclude Include="AsstUtils.hpp" />
<ClInclude Include="AsstTypes.h" />
@@ -40,6 +41,7 @@
<ClInclude Include="GameCrashRestartTaskPlugin.h" />
<ClInclude Include="NoWarningCV.h" />
<ClInclude Include="NoWarningCVMat.h" />
<ClInclude Include="ReportDataTask.h" />
<ClInclude Include="RoguelikeControlTaskPlugin.h" />
<ClInclude Include="RoguelikeCustomStartTaskPlugin.h" />
<ClInclude Include="RoguelikeRecruitConfiger.h" />
@@ -131,6 +133,7 @@
<ClCompile Include="DepotTask.cpp" />
<ClCompile Include="DrGrandetTaskPlugin.cpp" />
<ClCompile Include="GameCrashRestartTaskPlugin.cpp" />
<ClCompile Include="ReportDataTask.cpp" />
<ClCompile Include="RoguelikeControlTaskPlugin.cpp" />
<ClCompile Include="RoguelikeCustomStartTaskPlugin.cpp" />
<ClCompile Include="RoguelikeRecruitConfiger.cpp" />

View File

@@ -384,6 +384,12 @@
<ClInclude Include="DrGrandetTaskPlugin.h">
<Filter>头文件\Task\Plugin</Filter>
</ClInclude>
<ClInclude Include="ReportDataTask.h">
<Filter>头文件\Task\Sub</Filter>
</ClInclude>
<ClInclude Include="AsstHttp.hpp">
<Filter>头文件\Utils</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Controller.cpp">
@@ -656,6 +662,9 @@
<ClCompile Include="DrGrandetTaskPlugin.cpp">
<Filter>源文件\Task\Plugin</Filter>
</ClCompile>
<ClCompile Include="ReportDataTask.cpp">
<Filter>源文件\Task\Sub</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\resource\config.json">

View File

@@ -0,0 +1,148 @@
#include "ReportDataTask.h"
#include "AsstUtils.hpp"
#include "Logger.hpp"
#include "Resource.h"
#include <meojson/json.hpp>
#include <chrono>
#include <functional>
#include <random>
#include <sstream>
std::vector<std::future<void>> asst::ReportDataTask::m_upload_pending;
asst::ReportDataTask& asst::ReportDataTask::set_report_type(ReportType type)
{
m_report_type = type;
return *this;
}
asst::ReportDataTask& asst::ReportDataTask::set_body(std::string body)
{
m_body = std::move(body);
return *this;
}
asst::ReportDataTask& asst::ReportDataTask::set_extra_param(std::string extra_param)
{
m_extra_param = std::move(extra_param);
return *this;
}
bool asst::ReportDataTask::_run()
{
LogTraceFunction;
std::function<void(void)> report_func;
switch (m_report_type) {
case ReportType::PenguinStats:
report_func = std::bind(&ReportDataTask::report_to_penguin, this);
break;
case ReportType::YituliuBigData:
report_func = std::bind(&ReportDataTask::report_to_yituliu, this);
break;
default:
return false;
}
auto upload_future = std::async(std::launch::async, report_func);
m_upload_pending.emplace_back(std::move(upload_future));
return true;
}
void asst::ReportDataTask::report_to_penguin()
{
using namespace std::chrono;
LogTraceFunction;
auto epoch = time_point_cast<nanoseconds>(steady_clock::now()).time_since_epoch();
long long tick = duration_cast<nanoseconds>(epoch).count();
static std::default_random_engine rand_engine(std::random_device {}());
static std::uniform_int_distribution<uint64_t> rand_uni(0, UINT64_MAX);
uint64_t rand = rand_uni(rand_engine);
std::stringstream key_ss;
key_ss << "MAA" << std::hex << tick << rand;
std::string key = key_ss.str();
Log.info("X-Penguin-Idempotency-Key:", key);
m_extra_param += " -H \"X-Penguin-Idempotency-Key: " + std::move(key) + "\"";
http::Response response = report("ReportToPenguinStats", Resrc.cfg().get_options().penguin_report.cmd_format);
if (response.success()) [[unlikely]] {
if (auto penguinid_opt = response.find_header("x-penguin-set-penguinid")) {
json::value id_info = basic_info_with_what("PenguinId");
id_info["details"]["id"] = std::string(penguinid_opt.value());
callback(AsstMsg::SubTaskExtraInfo, id_info);
}
}
}
void asst::ReportDataTask::report_to_yituliu()
{
LogTraceFunction;
report("ReportToYituliu", Resrc.cfg().get_options().yituliu_report.cmd_format);
}
asst::http::Response asst::ReportDataTask::report(const std::string& subtask, const std::string& format,
HttpResponsePred success_cond, HttpResponsePred retry_cond)
{
LogTraceFunction;
json::value cb_info = basic_info();
cb_info["subtask"] = subtask;
callback(AsstMsg::SubTaskStart, cb_info);
http::Response response;
for (int i = 0; i <= m_retry_times; ++i) {
response = escape_and_request(format);
if (success_cond(response)) {
callback(AsstMsg::SubTaskCompleted, cb_info);
return response;
}
else if (!retry_cond(response)) {
break;
}
}
cb_info["why"] = "上报失败";
cb_info["details"] = json::object { { "error", response.get_last_error() },
{ "status_code", response.status_code() },
{ "status_code_info", std::string(response.status_code_info()) },
{ "response", std::string(response.data()) } };
callback(AsstMsg::SubTaskError, cb_info);
return response;
}
asst::http::Response asst::ReportDataTask::escape_and_request(const std::string& format)
{
LogTraceFunction;
std::string body_escape = utils::string_replace_all_batch(m_body, { { "\"", "\\\"" } });
#ifdef _WIN32
std::string body_escapes = utils::utf8_to_unicode_escape(body_escape);
#else
std::string body_escapes = body_escape;
#endif
std::string cmd_line =
utils::string_replace_all_batch(format, { { "[body]", body_escapes }, { "[extra]", m_extra_param } });
Log.info("request:\n", cmd_line);
http::Response response = utils::callcmd(cmd_line);
Log.info("response:\n", response);
return response;
}

View File

@@ -0,0 +1,49 @@
#pragma once
#include "AbstractTask.h"
#include "AsstHttp.hpp"
#include "AsstUtils.hpp"
#include <cctype>
#include <future>
#include <unordered_set>
namespace asst
{
enum class ReportType
{
Invaild,
PenguinStats,
YituliuBigData,
};
class ReportDataTask : public AbstractTask
{
public:
using AbstractTask::AbstractTask;
virtual ~ReportDataTask() noexcept override = default;
ReportDataTask& set_report_type(ReportType type);
ReportDataTask& set_body(std::string body);
ReportDataTask& set_extra_param(std::string extra_param);
protected:
using HttpResponsePred = std::function<bool(const http::Response&)>;
virtual bool _run() override;
void report_to_penguin();
void report_to_yituliu();
http::Response escape_and_request(const std::string& format);
http::Response report(
const std::string& subtask, const std::string& format,
HttpResponsePred success_cond = [](const http::Response& response) -> bool { return response.success(); },
HttpResponsePred retry_cond = [](const http::Response& response) -> bool { return response.status_5xx(); });
ReportType m_report_type = ReportType::Invaild;
std::string m_body;
std::string m_extra_param;
static std::vector<std::future<void>> m_upload_pending;
};
} // namespace asst

View File

@@ -8,6 +8,7 @@
#include "Controller.h"
#include "Logger.hpp"
#include "ProcessTask.h"
#include "ReportDataTask.h"
#include "Resource.h"
#include "RuntimeStatus.h"
#include "StageDropsImageAnalyzer.h"
@@ -90,8 +91,7 @@ bool asst::StageDropsTaskPlugin::_run()
}
if (m_enable_penguid && !m_is_annihilation) {
auto upload_future = std::async(std::launch::async, &StageDropsTaskPlugin::upload_to_penguin, this);
m_upload_pending.emplace_back(std::move(upload_future));
upload_to_penguin();
}
return true;
@@ -205,11 +205,8 @@ void asst::StageDropsTaskPlugin::upload_to_penguin()
{
LogTraceFunction;
auto& opt = Resrc.cfg().get_options();
json::value cb_info = basic_info();
cb_info["subtask"] = "ReportToPenguinStats";
callback(AsstMsg::SubTaskStart, cb_info);
// Doc: https://developer.penguin-stats_vec.io/public-api/api-v2-instruction/report-api
std::string stage_id = m_cur_info_json.get("stage", "stageId", std::string());
@@ -251,38 +248,38 @@ void asst::StageDropsTaskPlugin::upload_to_penguin()
body["source"] = "MeoAssistant";
body["version"] = Version;
std::string body_escape = utils::string_replace_all(body.to_string(), "\"", "\\\"");
std::string cmd_line = utils::string_replace_all(opt.penguin_report.cmd_format, "[body]", body_escape);
std::string extra_param;
if (!m_penguin_id.empty()) {
extra_param = "-H \"authorization: PenguinID " + m_penguin_id + "\"";
}
cmd_line = utils::string_replace_all(cmd_line, "[extra]", extra_param);
Log.info("request_penguin |", cmd_line);
std::string response = utils::callcmd(cmd_line);
Log.info("response:\n", response);
cb_info["details"]["response"] = response;
static const std::regex http_ok_regex(R"(HTTP/.+ 200)");
if (std::regex_search(response, http_ok_regex)) {
callback(AsstMsg::SubTaskCompleted, cb_info);
}
else {
cb_info["why"] = "上报失败";
callback(AsstMsg::SubTaskError, cb_info);
if (!m_report_penguin_task_ptr) {
m_report_penguin_task_ptr =
std::make_shared<ReportDataTask>(report_penguin_callback, static_cast<void*>(this), m_task_chain);
}
static const std::regex penguinid_regex(R"(X-Penguin-Set-Penguinid: (\d+))");
std::smatch penguinid_sm;
if (std::regex_search(response, penguinid_sm, penguinid_regex)) {
json::value id_info = basic_info_with_what("PenguinId");
m_penguin_id = penguinid_sm[1];
id_info["details"]["id"] = m_penguin_id;
callback(AsstMsg::SubTaskExtraInfo, id_info);
m_report_penguin_task_ptr->set_report_type(ReportType::PenguinStats)
.set_body(body.to_string())
.set_extra_param(extra_param)
.set_retry_times(5)
.run();
}
void asst::StageDropsTaskPlugin::report_penguin_callback(AsstMsg msg, const json::value& detail, void* custom_arg)
{
LogTraceFunction;
auto p_this = static_cast<StageDropsTaskPlugin*>(custom_arg);
if (!p_this) {
return;
}
if (msg == AsstMsg::SubTaskExtraInfo && detail.get("what", std::string()) == "PenguinId") {
std::string id = detail.get("details", "id", std::string());
p_this->m_penguin_id = id;
}
p_this->callback(msg, detail);
}
bool asst::StageDropsTaskPlugin::check_stage_valid()

View File

@@ -1,7 +1,7 @@
#pragma once
#include "AbstractTaskPlugin.h"
#include <future>
#include <memory>
#include <vector>
#include <meojson/json.hpp>
@@ -11,6 +11,7 @@
namespace asst
{
class ProcessTask;
class ReportDataTask;
class StageDropsTaskPlugin final : public AbstractTaskPlugin
{
@@ -36,6 +37,7 @@ namespace asst
bool check_specify_quantity() const;
void stop_task();
void upload_to_penguin();
static void report_penguin_callback(AsstMsg msg, const json::value& detail, void* custom_arg);
static constexpr int64_t RecognitionTimeOffset = 20;
@@ -48,8 +50,8 @@ namespace asst
mutable bool m_is_annihilation = false;
bool m_start_button_delay_is_set = false;
std::vector<std::future<void>> m_upload_pending;
ProcessTask* m_cast_ptr = nullptr;
std::shared_ptr<ReportDataTask> m_report_penguin_task_ptr = nullptr;
bool m_enable_penguid = false;
std::string m_penguin_id;
std::string m_server = "CN";