Skip to content

Commit

Permalink
feat: Controller recording
Browse files Browse the repository at this point in the history
  • Loading branch information
MistEO committed Oct 25, 2023
1 parent 544d12b commit a0ec741
Show file tree
Hide file tree
Showing 3 changed files with 273 additions and 13 deletions.
4 changes: 4 additions & 0 deletions include/MaaFramework/MaaDef.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ enum MaaCtrlOptionEnum
// For StopApp
// value: string, eg: "com.hypergryph.arknights"; val_size: string length
MaaCtrlOption_DefaultAppPackage = 4,

// Dump all screenshots and actions
// value: bool, eg: true; val_size: sizeof(bool)
MaaCtrlOption_Recording = 5,
};

typedef MaaOption MaaInstOption;
Expand Down
264 changes: 251 additions & 13 deletions source/MaaFramework/Controller/ControllerMgr.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include "ControllerMgr.h"

#include "MaaFramework/MaaMsg.h"
#include "Option/GlobalOptionMgr.h"
#include "Resource/ResourceMgr.h"
#include "Utils/ImageIo.h"
#include "Utils/NoWarningCV.hpp"

#include <tuple>
Expand Down Expand Up @@ -43,6 +45,9 @@ bool ControllerMgr::set_option(MaaCtrlOption key, MaaOptionValue value, MaaOptio
case MaaCtrlOption_DefaultAppPackage:
return set_default_app_package(value, val_size);

case MaaCtrlOption_Recording:
return set_recording(value, val_size);

default:
LogError << "Unknown key" << VAR(key) << VAR(value);
return false;
Expand Down Expand Up @@ -223,6 +228,227 @@ bool ControllerMgr::stop_app(const std::string& package)
return status(id) == MaaStatus_Success;
}

bool ControllerMgr::handle_connect()
{
std::chrono::steady_clock::time_point start_time;
if (recording_) {
start_time = std::chrono::steady_clock::now();
}

connected_ = _connect();

if (recording_) {
json::value info {
{ "type", "connect" },
{ "success", connected_ },
};
append_recording(std::move(info), start_time, connected_);
}

return connected_;
}

bool ControllerMgr::handle_click(const ClickParam& param)
{
std::chrono::steady_clock::time_point start_time;
if (recording_) {
start_time = std::chrono::steady_clock::now();
}

bool ret = _click(param);

if (recording_) {
json::value info = {
{ "type", "click" },
{ "x", param.x },
{ "y", param.y },
};
append_recording(std::move(info), start_time, ret);
}

return ret;
}

bool ControllerMgr::handle_swipe(const SwipeParam& param)
{
std::chrono::steady_clock::time_point start_time;
if (recording_) {
start_time = std::chrono::steady_clock::now();
}

bool ret = _swipe(param);

if (recording_) {
json::value info = {
{ "type", "swipe" }, { "x1", param.x1 }, { "y1", param.y1 },
{ "x2", param.x2 }, { "y2", param.y2 }, { "duration", param.duration },
};
append_recording(std::move(info), start_time, ret);
}

return ret;
}

bool ControllerMgr::handle_touch_down(const TouchParam& param)
{
std::chrono::steady_clock::time_point start_time;
if (recording_) {
start_time = std::chrono::steady_clock::now();
}

bool ret = _touch_down(param);

if (recording_) {
json::value info = {
{ "type", "touch_down" }, { "contact", param.contact }, { "x", param.x },
{ "y", param.y }, { "pressure", param.pressure },
};
append_recording(std::move(info), start_time, ret);
}

return ret;
}

bool ControllerMgr::handle_touch_move(const TouchParam& param)
{
std::chrono::steady_clock::time_point start_time;
if (recording_) {
start_time = std::chrono::steady_clock::now();
}

bool ret = _touch_move(param);

if (recording_) {
json::value info = {
{ "type", "touch_move" }, { "contact", param.contact }, { "x", param.x },
{ "y", param.y }, { "pressure", param.pressure },
};
append_recording(std::move(info), start_time, ret);
}

return ret;
}

bool ControllerMgr::handle_touch_up(const TouchParam& param)
{
std::chrono::steady_clock::time_point start_time;
if (recording_) {
start_time = std::chrono::steady_clock::now();
}

bool ret = _touch_up(param);

if (recording_) {
json::value info = {
{ "type", "touch_up" },
{ "contact", param.contact },
};
append_recording(std::move(info), start_time, ret);
}

return ret;
}

bool ControllerMgr::handle_press_key(const PressKeyParam& param)
{
std::chrono::steady_clock::time_point start_time;
if (recording_) {
start_time = std::chrono::steady_clock::now();
}

bool ret = _press_key(param);

if (recording_) {
json::value info = {
{ "type", "press_key" },
{ "keycode", param.keycode },
};
append_recording(std::move(info), start_time, ret);
}

return ret;
}

bool ControllerMgr::handle_screencap()
{
std::chrono::steady_clock::time_point start_time;
if (recording_) {
start_time = std::chrono::steady_clock::now();
}

bool ret = postproc_screenshot(_screencap());

if (recording_) {
auto image_relative_path = path("Screenshot") / path(now_filestem() + ".png");
auto image_path = recording_path_.parent_path() / image_relative_path;
MAA_NS::imwrite(image_path, image_);

json::value info = {
{ "type", "screencap" },
{ "path", path_to_utf8_string(image_relative_path) },
};
append_recording(std::move(info), start_time, ret);
}

return ret;
}

bool ControllerMgr::handle_start_app(const AppParam& param)
{
std::chrono::steady_clock::time_point start_time;
if (recording_) {
start_time = std::chrono::steady_clock::now();
}

bool ret = _start_app(param);
clear_target_image_size();

if (recording_) {
json::value info = {
{ "type", "start_app" },
{ "package", param.package },
};
append_recording(std::move(info), start_time, ret);
}
return ret;
}

bool ControllerMgr::handle_stop_app(const AppParam& param)
{
std::chrono::steady_clock::time_point start_time;
if (recording_) {
start_time = std::chrono::steady_clock::now();
}

bool ret = _stop_app(param);
clear_target_image_size();

if (recording_) {
json::value info = {
{ "type", "stop_app" },
{ "package", param.package },
};
append_recording(std::move(info), start_time, ret);
}
return ret;
}

void ControllerMgr::append_recording(json::value info, const std::chrono::steady_clock::time_point& start_time,
bool success)
{
if (!recording_) {
return;
}

info["time"] = start_time.time_since_epoch().count();
info["cost"] = duration_since(start_time).count();
info["success"] = success;

std::ofstream ofs(recording_path_, std::ios::app);
ofs << info.to_string() << "\n";
ofs.close();
}

cv::Point ControllerMgr::rand_point(const cv::Rect& r)
{
int x = 0, y = 0;
Expand Down Expand Up @@ -268,42 +494,39 @@ bool ControllerMgr::run_action(typename AsyncRunner<Action>::Id id, Action actio

switch (action.type) {
case Action::Type::connect:
ret = _connect();
connected_ = ret;
ret = handle_connect();
break;

case Action::Type::click:
ret = _click(std::get<ClickParam>(action.param));
ret = handle_click(std::get<ClickParam>(action.param));
break;
case Action::Type::swipe:
ret = _swipe(std::get<SwipeParam>(action.param));
ret = handle_swipe(std::get<SwipeParam>(action.param));
break;

case Action::Type::touch_down:
ret = _touch_down(std::get<TouchParam>(action.param));
ret = handle_touch_down(std::get<TouchParam>(action.param));
break;
case Action::Type::touch_move:
ret = _touch_move(std::get<TouchParam>(action.param));
ret = handle_touch_move(std::get<TouchParam>(action.param));
break;
case Action::Type::touch_up:
ret = _touch_up(std::get<TouchParam>(action.param));
ret = handle_touch_up(std::get<TouchParam>(action.param));
break;

case Action::Type::press_key:
ret = _press_key(std::get<PressKeyParam>(action.param));
ret = handle_press_key(std::get<PressKeyParam>(action.param));
break;

case Action::Type::screencap:
ret = postproc_screenshot(_screencap());
ret = handle_screencap();
break;

case Action::Type::start_app:
ret = _start_app(std::get<AppParam>(action.param));
clear_target_image_size();
ret = handle_start_app(std::get<AppParam>(action.param));
break;
case Action::Type::stop_app:
ret = _stop_app(std::get<AppParam>(action.param));
clear_target_image_size();
ret = handle_stop_app(std::get<AppParam>(action.param));
break;

default:
Expand Down Expand Up @@ -452,6 +675,21 @@ bool ControllerMgr::set_default_app_package(MaaOptionValue value, MaaOptionValue
return true;
}

bool ControllerMgr::set_recording(MaaOptionValue value, MaaOptionValueSize val_size)
{
if (val_size != sizeof(recording_)) {
LogError << "invalid value size: " << val_size;
return false;
}
recording_ = *reinterpret_cast<bool*>(value);

auto recording_dir = GlobalOptionMgr::get_instance().logging_path() / "Recording";
std::filesystem::create_directories(recording_dir);
recording_path_ = recording_dir / MAA_FMT::format("MaaRecording_{}.txt", now_filestem());

return true;
}

std::ostream& operator<<(std::ostream& os, const Action& action)
{
switch (action.type) {
Expand Down
18 changes: 18 additions & 0 deletions source/MaaFramework/Controller/ControllerMgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,20 @@ class ControllerMgr : public MaaControllerAPI
protected:
MessageNotifier<MaaControllerCallback> notifier;

private:
bool handle_connect();
bool handle_click(const ClickParam& param);
bool handle_swipe(const SwipeParam& param);
bool handle_touch_down(const TouchParam& param);
bool handle_touch_move(const TouchParam& param);
bool handle_touch_up(const TouchParam& param);
bool handle_press_key(const PressKeyParam& param);
bool handle_screencap();
bool handle_start_app(const AppParam& param);
bool handle_stop_app(const AppParam& param);

void append_recording(json::value info, const std::chrono::steady_clock::time_point& start_time, bool success);

private:
static cv::Point rand_point(const cv::Rect& r);

Expand All @@ -141,6 +155,7 @@ class ControllerMgr : public MaaControllerAPI
bool set_image_target_short_side(MaaOptionValue value, MaaOptionValueSize val_size);
bool set_default_app_package_entry(MaaOptionValue value, MaaOptionValueSize val_size);
bool set_default_app_package(MaaOptionValue value, MaaOptionValueSize val_size);
bool set_recording(MaaOptionValue value, MaaOptionValueSize val_size);

private:
// InstanceInternalAPI* inst_ = nullptr;
Expand All @@ -160,6 +175,9 @@ class ControllerMgr : public MaaControllerAPI
std::string default_app_package_entry_;
std::string default_app_package_;

bool recording_ = false;
std::filesystem::path recording_path_;

std::set<AsyncRunner<Action>::Id> post_ids_;
std::mutex post_ids_mutex_;
std::unique_ptr<AsyncRunner<Action>> action_runner_ = nullptr;
Expand Down

0 comments on commit a0ec741

Please sign in to comment.