refactor: 重构干员头像缓存机制

This commit is contained in:
MistEO
2023-01-12 22:50:42 +08:00
parent dec8a1a45a
commit a5523a83c7
12 changed files with 137 additions and 106 deletions

View 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);
}

View 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();
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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);

View File

@@ -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
{

View File

@@ -31,7 +31,6 @@ namespace asst
bool do_once();
bool calc_stage_info();
void load_cache();
void all_melee_retreat();