mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-15 17:30:27 +08:00
refactor: 重构干员头像缓存机制
This commit is contained in:
69
src/MaaCore/Config/Miscellaneous/AvatarCacheManager.cpp
Normal file
69
src/MaaCore/Config/Miscellaneous/AvatarCacheManager.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "AvatarCacheManager.h"
|
||||
|
||||
#include "BattleDataConfig.h"
|
||||
#include "Utils/ImageIo.hpp"
|
||||
#include "Utils/Logger.hpp"
|
||||
|
||||
bool asst::AvatarCacheManager::load(const std::filesystem::path& path)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
if (!std::filesystem::exists(path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(path)) {
|
||||
if (!entry.is_regular_file()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::filesystem::path& filepath = entry.path();
|
||||
std::string name = utils::path_to_utf8_string(filepath.stem());
|
||||
auto role = BattleData.get_role(name);
|
||||
if (role == battle::Role::Unknown) {
|
||||
Log.warn("unknown oper", name);
|
||||
continue;
|
||||
}
|
||||
Log.trace(filepath);
|
||||
cv::Mat avatar = asst::imread(filepath);
|
||||
if (avatar.empty()) {
|
||||
Log.warn("failed to read", filepath);
|
||||
continue;
|
||||
}
|
||||
m_avatars[role].emplace(name, std::move(avatar));
|
||||
}
|
||||
|
||||
m_path = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
const asst::AvatarCacheManager::AvatarsMap& asst::AvatarCacheManager::get_avatars(battle::Role role)
|
||||
{
|
||||
return m_avatars[role];
|
||||
}
|
||||
|
||||
void asst::AvatarCacheManager::set_avatar(const std::string& name, battle::Role role, const cv::Mat& avatar,
|
||||
bool overlay)
|
||||
{
|
||||
LogTraceFunction;
|
||||
Log.info(__FUNCTION__, name, ", overlay:", overlay);
|
||||
|
||||
if (overlay) {
|
||||
m_avatars[role].insert_or_assign(name, avatar);
|
||||
}
|
||||
else {
|
||||
m_avatars[role].try_emplace(name, avatar);
|
||||
return;
|
||||
}
|
||||
|
||||
if (BattleData.is_name_invalid(name)) {
|
||||
Log.error("invalid name", name);
|
||||
return;
|
||||
}
|
||||
|
||||
std::filesystem::create_directories(m_path);
|
||||
auto path = m_path / utils::path(name + CacheExtension);
|
||||
Log.info(path);
|
||||
|
||||
asst::imwrite(path, avatar);
|
||||
}
|
||||
34
src/MaaCore/Config/Miscellaneous/AvatarCacheManager.h
Normal file
34
src/MaaCore/Config/Miscellaneous/AvatarCacheManager.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#pragma once
|
||||
#include "Config/AbstractResource.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Utils/NoWarningCVMat.h"
|
||||
|
||||
#include "Common/AsstBattleDef.h"
|
||||
#include "Common/AsstTypes.h"
|
||||
|
||||
namespace asst
|
||||
{
|
||||
class AvatarCacheManager final : public SingletonHolder<AvatarCacheManager>, public AbstractResource
|
||||
{
|
||||
public:
|
||||
using AvatarsMap = std::unordered_map<std::string, cv::Mat>;
|
||||
inline static const std::string CacheExtension = ".png";
|
||||
|
||||
public:
|
||||
virtual ~AvatarCacheManager() override = default;
|
||||
|
||||
virtual bool load(const std::filesystem::path& path) override;
|
||||
|
||||
const AvatarsMap& get_avatars(battle::Role role);
|
||||
void set_avatar(const std::string& name, battle::Role role, const cv::Mat& avatar, bool overlay = true);
|
||||
|
||||
private:
|
||||
std::unordered_map<battle::Role, std::unordered_map<std::string, cv::Mat>> m_avatars;
|
||||
std::filesystem::path m_path;
|
||||
};
|
||||
inline static auto& AvatarCache = AvatarCacheManager::get_instance();
|
||||
}
|
||||
@@ -74,6 +74,11 @@ namespace asst
|
||||
return char_iter->second.tokens;
|
||||
}
|
||||
|
||||
bool is_name_invalid(const std::string& name) const
|
||||
{
|
||||
return name.empty() || m_chars.find(name) == m_chars.cend();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool parse(const json::value& json) override;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <future>
|
||||
|
||||
#include "GeneralConfig.h"
|
||||
#include "Miscellaneous/AvatarCacheManager.h"
|
||||
#include "Miscellaneous/BattleDataConfig.h"
|
||||
#include "Miscellaneous/CopilotConfig.h"
|
||||
#include "Miscellaneous/InfrastConfig.h"
|
||||
@@ -44,6 +45,13 @@ bool asst::ResourceLoader::load(const std::filesystem::path& path)
|
||||
} \
|
||||
}
|
||||
|
||||
#define LoadCacheWithoutRet(Config, Dir) \
|
||||
{ \
|
||||
LogTraceScope(std::string("LoadCacheWithoutRet ") + #Config); \
|
||||
auto full_path = UserDir.get() / "cache"_p / Dir; \
|
||||
load_resource<Config>(full_path); \
|
||||
}
|
||||
|
||||
LogTraceFunction;
|
||||
using namespace asst::utils::path_literals;
|
||||
|
||||
@@ -61,6 +69,10 @@ bool asst::ResourceLoader::load(const std::filesystem::path& path)
|
||||
LoadResourceWithTemplAndCheckRet(TaskData, "tasks.json"_p, "template"_p);
|
||||
LoadResourceWithTemplAndCheckRet(InfrastConfig, "infrast.json"_p, "template"_p / "infrast"_p);
|
||||
LoadResourceWithTemplAndCheckRet(ItemConfig, "item_index.json"_p, "template"_p / "items"_p);
|
||||
|
||||
/* load cache */
|
||||
LoadCacheWithoutRet(AvatarCacheManager, "avatars"_p);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -79,6 +91,7 @@ bool asst::ResourceLoader::load(const std::filesystem::path& path)
|
||||
|
||||
#undef LoadTemplByConfigAndCheckRet
|
||||
#undef LoadResourceAndCheckRet
|
||||
#undef LoadCacheWithoutRet
|
||||
|
||||
m_loaded = true;
|
||||
m_loaded &= config_future.get();
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<ClInclude Include="Common\AsstMsg.h" />
|
||||
<ClInclude Include="Common\AsstTypes.h" />
|
||||
<ClInclude Include="Common\AsstVersion.h" />
|
||||
<ClInclude Include="Config\Miscellaneous\AvatarCacheManager.h" />
|
||||
<ClInclude Include="Config\Miscellaneous\SSSCopilotConfig.h" />
|
||||
<ClInclude Include="Controller.h" />
|
||||
<ClInclude Include="InstHelper.h" />
|
||||
@@ -149,6 +150,7 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Assistant.cpp" />
|
||||
<ClCompile Include="AsstCaller.cpp" />
|
||||
<ClCompile Include="Config\Miscellaneous\AvatarCacheManager.cpp" />
|
||||
<ClCompile Include="Config\Miscellaneous\SSSCopilotConfig.cpp" />
|
||||
<ClCompile Include="Controller.cpp" />
|
||||
<ClCompile Include="InstHelper.cpp" />
|
||||
|
||||
@@ -471,6 +471,9 @@
|
||||
<ClInclude Include="Task\Miscellaneous\SSSStageManagerTask.h">
|
||||
<Filter>源文件\Task\Miscellaneous</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Config\Miscellaneous\AvatarCacheManager.h">
|
||||
<Filter>源文件\Config\Miscellaneous</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Vision\AbstractImageAnalyzer.cpp">
|
||||
@@ -773,5 +776,8 @@
|
||||
<ClCompile Include="Task\Miscellaneous\SSSStageManagerTask.cpp">
|
||||
<Filter>源文件\Task\Miscellaneous</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Config\Miscellaneous\AvatarCacheManager.cpp">
|
||||
<Filter>源文件\Config\Miscellaneous</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <future>
|
||||
#include <thread>
|
||||
|
||||
#include "Config/Miscellaneous/AvatarCacheManager.h"
|
||||
#include "Config/Miscellaneous/BattleDataConfig.h"
|
||||
#include "Config/TaskData.h"
|
||||
#include "Controller.h"
|
||||
@@ -40,7 +41,6 @@ void asst::BattleHelper::clear()
|
||||
m_in_battle = false;
|
||||
m_kills = 0;
|
||||
m_total_kills = 0;
|
||||
m_all_deployment_avatars.clear();
|
||||
m_cur_deployment_opers.clear();
|
||||
m_battlefield_opers.clear();
|
||||
m_used_tiles.clear();
|
||||
@@ -60,55 +60,6 @@ bool asst::BattleHelper::calc_tiles_info(const std::string& stage_name, double s
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::BattleHelper::load_avatar_cache(const std::string& name, bool with_token)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
if (name.empty()) {
|
||||
return false;
|
||||
}
|
||||
auto path = avatar_cache_dir() / utils::path(name + CacheExtension);
|
||||
Log.info(path);
|
||||
|
||||
if (!std::filesystem::exists(path)) {
|
||||
Log.info(path, "not exists");
|
||||
return false;
|
||||
}
|
||||
cv::Mat avatar = asst::imread(path);
|
||||
if (avatar.empty()) {
|
||||
Log.info(path, "image is empty");
|
||||
return false;
|
||||
}
|
||||
m_all_deployment_avatars.emplace(name, std::move(avatar));
|
||||
Log.info(path, "loaded");
|
||||
|
||||
if (with_token) {
|
||||
if (auto tokens = BattleData.get_tokens(name); !tokens.empty()) {
|
||||
for (const std::string& token_name : tokens) {
|
||||
load_avatar_cache(token_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void asst::BattleHelper::save_avatar_cache(const std::string& name, const cv::Mat& avatar)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
if (BattleData.get_rarity(name) == 0) {
|
||||
Log.error("wrong oper name", name);
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = avatar_cache_dir() / utils::path(name + CacheExtension);
|
||||
Log.info(path);
|
||||
|
||||
std::filesystem::create_directories(avatar_cache_dir());
|
||||
asst::imwrite(path, avatar);
|
||||
}
|
||||
|
||||
bool asst::BattleHelper::pause()
|
||||
{
|
||||
LogTraceFunction;
|
||||
@@ -181,7 +132,8 @@ bool asst::BattleHelper::update_deployment(bool init, const cv::Mat& reusable)
|
||||
}
|
||||
|
||||
double max_socre = 0;
|
||||
for (const auto& [name, avatar] : m_all_deployment_avatars) {
|
||||
auto& avatar_cache = AvatarCache.get_avatars(oper.role);
|
||||
for (const auto& [name, avatar] : avatar_cache) {
|
||||
avatar_analyzer.set_templ(avatar);
|
||||
if (!avatar_analyzer.analyze()) {
|
||||
continue;
|
||||
@@ -239,14 +191,14 @@ bool asst::BattleHelper::update_deployment(bool init, const cv::Mat& reusable)
|
||||
|
||||
OcrWithPreprocessImageAnalyzer preproc_analyzer;
|
||||
std::string name = analyze(preproc_analyzer);
|
||||
if (name.empty() || is_name_invalid(name)) {
|
||||
if (BattleData.is_name_invalid(name)) {
|
||||
Log.warn("ocr with preprocess got a invalid name, try to use detect model", name);
|
||||
OcrImageAnalyzer det_analyzer;
|
||||
std::string det_name = analyze(det_analyzer);
|
||||
if (det_name.empty()) {
|
||||
Log.warn("ocr with det model failed");
|
||||
}
|
||||
else if (name.empty() || !is_name_invalid(det_name)) {
|
||||
else if (BattleData.is_name_invalid(det_name)) {
|
||||
Log.info("use ocr with det", det_name);
|
||||
name = det_name;
|
||||
}
|
||||
@@ -266,12 +218,11 @@ bool asst::BattleHelper::update_deployment(bool init, const cv::Mat& reusable)
|
||||
// 而且由于 cd 干员头像阈值设置的非常低,为了防止把正确的干员覆盖掉了
|
||||
// 所以不进行覆盖
|
||||
m_cur_deployment_opers.emplace(name, oper);
|
||||
m_all_deployment_avatars.emplace(name, oper.avatar);
|
||||
AvatarCache.set_avatar(name, oper.role, oper.avatar, false);
|
||||
}
|
||||
else {
|
||||
m_cur_deployment_opers.insert_or_assign(name, oper);
|
||||
m_all_deployment_avatars.insert_or_assign(name, oper.avatar);
|
||||
save_avatar_cache(name, oper.avatar);
|
||||
AvatarCache.set_avatar(name, oper.role, oper.avatar);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -641,11 +592,6 @@ bool asst::BattleHelper::move_camera(const std::pair<double, double>& move_loc)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::BattleHelper::is_name_invalid(const std::string& name)
|
||||
{
|
||||
return BattleData.get_rarity(name) <= 0;
|
||||
}
|
||||
|
||||
std::optional<asst::Rect> asst::BattleHelper::get_oper_rect_on_deployment(const std::string& name) const
|
||||
{
|
||||
LogTraceFunction;
|
||||
@@ -658,11 +604,3 @@ std::optional<asst::Rect> asst::BattleHelper::get_oper_rect_on_deployment(const
|
||||
|
||||
return oper_iter->second.rect;
|
||||
}
|
||||
|
||||
const std::filesystem::path& asst::BattleHelper::avatar_cache_dir()
|
||||
{
|
||||
using namespace asst::utils::path_literals;
|
||||
|
||||
static const auto dir = UserDir.get() / "cache"_p / "avatars"_p;
|
||||
return dir;
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@ namespace asst
|
||||
{
|
||||
class BattleHelper
|
||||
{
|
||||
inline static const std::string CacheExtension = ".png";
|
||||
|
||||
public:
|
||||
~BattleHelper() = default;
|
||||
|
||||
@@ -32,8 +30,6 @@ namespace asst
|
||||
virtual bool do_strategic_action(const cv::Mat& reusable = cv::Mat());
|
||||
|
||||
bool calc_tiles_info(const std::string& stage_name, double shift_x = 0, double shift_y = 0);
|
||||
bool load_avatar_cache(const std::string& name, bool with_token = false);
|
||||
void save_avatar_cache(const std::string& name, const cv::Mat& avatar);
|
||||
|
||||
bool pause();
|
||||
bool speed_up();
|
||||
@@ -65,7 +61,6 @@ namespace asst
|
||||
bool cancel_oper_selection();
|
||||
bool move_camera(const std::pair<double, double>& move_loc);
|
||||
|
||||
bool is_name_invalid(const std::string& name);
|
||||
std::optional<Rect> get_oper_rect_on_deployment(const std::string& name) const;
|
||||
|
||||
std::string m_stage_name;
|
||||
@@ -80,15 +75,12 @@ namespace asst
|
||||
int m_total_kills = 0;
|
||||
int m_cost = 0;
|
||||
|
||||
std::map<std::string, cv::Mat> m_all_deployment_avatars;
|
||||
std::map<std::string, battle::DeploymentOper> m_cur_deployment_opers;
|
||||
|
||||
std::map<std::string, Point> m_battlefield_opers;
|
||||
std::map<Point, std::string> m_used_tiles;
|
||||
|
||||
private:
|
||||
static const std::filesystem::path& avatar_cache_dir();
|
||||
|
||||
InstHelper m_inst_helper;
|
||||
};
|
||||
} // namespace asst
|
||||
|
||||
@@ -27,6 +27,7 @@ using namespace asst::battle::copilot;
|
||||
asst::BattleProcessTask::BattleProcessTask(const AsstCallback& callback, Assistant* inst, std::string_view task_chain)
|
||||
: AbstractTask(callback, inst, task_chain), BattleHelper(inst)
|
||||
{}
|
||||
|
||||
bool asst::BattleProcessTask::_run()
|
||||
{
|
||||
LogTraceFunction;
|
||||
@@ -37,7 +38,6 @@ bool asst::BattleProcessTask::_run()
|
||||
return false;
|
||||
}
|
||||
|
||||
load_cache();
|
||||
update_deployment(true);
|
||||
to_group();
|
||||
|
||||
@@ -78,18 +78,6 @@ bool asst::BattleProcessTask::set_stage_name(const std::string& stage_name)
|
||||
return true;
|
||||
}
|
||||
|
||||
void asst::BattleProcessTask::load_cache()
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
for (const auto& oper_list : get_combat_data().groups | views::values) {
|
||||
for (const auto& oper : oper_list) {
|
||||
load_avatar_cache(oper.name, true);
|
||||
}
|
||||
}
|
||||
// TODO: 识别编队,额外加载编队中有的干员的缓存
|
||||
}
|
||||
|
||||
bool asst::BattleProcessTask::to_group()
|
||||
{
|
||||
std::unordered_map<std::string, std::vector<std::string>> groups;
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace asst
|
||||
virtual battle::copilot::CombatData& get_combat_data() { return m_combat_data; }
|
||||
virtual bool need_to_wait_until_end() const { return false; }
|
||||
|
||||
void load_cache();
|
||||
bool to_group();
|
||||
bool do_action(size_t action_index);
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ bool asst::RoguelikeBattleTaskPlugin::_run()
|
||||
if (!calc_stage_info()) {
|
||||
return false;
|
||||
}
|
||||
load_cache();
|
||||
update_deployment(true);
|
||||
speed_up();
|
||||
|
||||
@@ -210,19 +209,6 @@ bool asst::RoguelikeBattleTaskPlugin::calc_stage_info()
|
||||
return true;
|
||||
}
|
||||
|
||||
void asst::RoguelikeBattleTaskPlugin::load_cache()
|
||||
{
|
||||
if (auto overview = status()->get_str(Status::RoguelikeCharOverview)) {
|
||||
json::value json = json::parse(*overview).value_or(json::value());
|
||||
for (const auto& name : json.as_object() | views::keys) {
|
||||
load_avatar_cache(name, true);
|
||||
}
|
||||
}
|
||||
for (const std::string& dice : DiceSet) {
|
||||
load_avatar_cache(dice);
|
||||
}
|
||||
}
|
||||
|
||||
asst::battle::LocationType asst::RoguelikeBattleTaskPlugin::get_oper_location_type(
|
||||
const battle::DeploymentOper& oper) const
|
||||
{
|
||||
|
||||
@@ -31,7 +31,6 @@ namespace asst
|
||||
|
||||
bool do_once();
|
||||
bool calc_stage_info();
|
||||
void load_cache();
|
||||
|
||||
void all_melee_retreat();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user