feat: LD 截图 (#10581)

* feat: EmulatorExtras 子模块更新

* feat: 写了一坨

* feat: 大概是能用了

* feat: ui 支持 ld 截图增强

* chore: 修改 LdSpecialScreenshot 颜色,调整版本要求描述

* chore: 使用 steady_clock

* chore: 移除未使用变量
This commit is contained in:
uye
2024-09-17 22:52:18 +08:00
committed by GitHub
parent 4734d2600d
commit c4e320526c
20 changed files with 571 additions and 53 deletions

View File

@@ -69,6 +69,8 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Inited/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=iter/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ldconsole/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=LD_0027s/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=leidian/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Lolicon/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=lpwndpl/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ls_005B_0022sub/@EntryIndexedValue">True</s:Boolean>

View File

@@ -216,6 +216,23 @@ void asst::AdbController::init_mumu_extras(const AdbCfg& adb_cfg [[maybe_unused]
#endif
}
void asst::AdbController::init_ld_extras(const AdbCfg& adb_cfg [[maybe_unused]])
{
#if !ASST_WITH_EMULATOR_EXTRAS
Log.error("MaaCore is not compiled with ASST_WITH_EMULATOR_EXTRAS");
#else
if (adb_cfg.extras.empty()) {
LogWarn << "adb_cfg.extras is empty";
return;
}
std::filesystem::path ld_path = utils::path(adb_cfg.extras.get("path", ""));
int ld_index = adb_cfg.extras.get("index", 0);
int ld_pid = adb_cfg.extras.get("pid", 0);
m_ld_extras.init(ld_path, ld_index, ld_pid, m_width, m_height);
#endif
}
void asst::AdbController::close_socket() noexcept
{
m_platform_io->close_socket();
@@ -458,10 +475,10 @@ bool asst::AdbController::screencap(cv::Mat& image_payload, bool allow_reconnect
auto min_cost = milliseconds(LLONG_MAX);
clear_lf_info();
auto start_time = high_resolution_clock::now();
auto start_time = steady_clock::now();
if (m_support_socket && m_server_started
&& screencap(m_adb.screencap_raw_by_nc, decode_raw, allow_reconnect, true, 5000)) {
auto duration = duration_cast<milliseconds>(high_resolution_clock::now() - start_time);
auto duration = duration_cast<milliseconds>(steady_clock::now() - start_time);
if (duration < min_cost) {
m_adb.screencap_method = AdbProperty::ScreencapMethod::RawByNc;
m_inited = true;
@@ -474,9 +491,9 @@ bool asst::AdbController::screencap(cv::Mat& image_payload, bool allow_reconnect
}
clear_lf_info();
start_time = high_resolution_clock::now();
start_time = steady_clock::now();
if (screencap(m_adb.screencap_raw_with_gzip, decode_raw_with_gzip, allow_reconnect)) {
auto duration = duration_cast<milliseconds>(high_resolution_clock::now() - start_time);
auto duration = duration_cast<milliseconds>(steady_clock::now() - start_time);
if (duration < min_cost) {
m_adb.screencap_method = AdbProperty::ScreencapMethod::RawWithGzip;
m_inited = true;
@@ -489,9 +506,9 @@ bool asst::AdbController::screencap(cv::Mat& image_payload, bool allow_reconnect
}
clear_lf_info();
start_time = high_resolution_clock::now();
start_time = steady_clock::now();
if (screencap(m_adb.screencap_encode, decode_encode, allow_reconnect)) {
auto duration = duration_cast<milliseconds>(high_resolution_clock::now() - start_time);
auto duration = duration_cast<milliseconds>(steady_clock::now() - start_time);
if (duration < min_cost) {
m_adb.screencap_method = AdbProperty::ScreencapMethod::Encode;
m_inited = true;
@@ -505,10 +522,10 @@ bool asst::AdbController::screencap(cv::Mat& image_payload, bool allow_reconnect
#if ASST_WITH_EMULATOR_EXTRAS
if (m_mumu_extras.inited()) {
start_time = high_resolution_clock::now();
start_time = steady_clock::now();
if (m_mumu_extras.screencap()) {
auto duration =
duration_cast<milliseconds>(high_resolution_clock::now() - start_time);
duration_cast<milliseconds>(steady_clock::now() - start_time);
if (duration < min_cost) {
m_adb.screencap_method = AdbProperty::ScreencapMethod::MumuExtras;
m_inited = true;
@@ -520,6 +537,22 @@ bool asst::AdbController::screencap(cv::Mat& image_payload, bool allow_reconnect
Log.info("MumuExtras is not supported");
}
}
if (m_ld_extras.inited()) {
start_time = steady_clock::now();
if (m_ld_extras.screencap()) {
auto duration = duration_cast<milliseconds>(steady_clock::now() - start_time);
if (duration < min_cost) {
m_adb.screencap_method = AdbProperty::ScreencapMethod::LDExtras;
m_inited = true;
min_cost = duration;
}
Log.info("LDExtras cost", duration.count(), "ms");
}
else {
Log.info("LDExtras is not supported");
}
}
#endif
static const std::unordered_map<AdbProperty::ScreencapMethod, std::string> MethodName = {
@@ -529,6 +562,7 @@ bool asst::AdbController::screencap(cv::Mat& image_payload, bool allow_reconnect
{ AdbProperty::ScreencapMethod::Encode, "Encode" },
#if ASST_WITH_EMULATOR_EXTRAS
{ AdbProperty::ScreencapMethod::MumuExtras, "MumuExtras" },
{ AdbProperty::ScreencapMethod::LDExtras, "LDExtras" },
#endif
};
Log.info(
@@ -577,6 +611,20 @@ bool asst::AdbController::screencap(cv::Mat& image_payload, bool allow_reconnect
screencap_ret = img_opt.has_value();
}
if (screencap_ret) {
image_payload = img_opt.value();
}
} break;
case AdbProperty::ScreencapMethod::LDExtras: {
auto img_opt = m_ld_extras.screencap();
screencap_ret = img_opt.has_value();
if (!screencap_ret && allow_reconnect) {
m_ld_extras.reload();
img_opt = m_ld_extras.screencap();
screencap_ret = img_opt.has_value();
}
if (screencap_ret) {
image_payload = img_opt.value();
}
@@ -986,6 +1034,9 @@ bool asst::AdbController::connect(
if (config == "MuMuEmulator12") {
init_mumu_extras(adb_cfg);
}
else if (config == "LDPlayer") {
init_ld_extras(adb_cfg);
}
if (need_exit()) {
return false;

View File

@@ -13,6 +13,7 @@
#include "Config/GeneralConfig.h"
#include "InstHelper.h"
#include "MumuExtras.h"
#include "LDExtras.h"
namespace asst
{
@@ -103,6 +104,7 @@ protected:
virtual void clear_info() noexcept;
void callback(AsstMsg msg, const json::value& details);
void init_mumu_extras(const AdbCfg& adb_cfg);
void init_ld_extras(const AdbCfg& adb_cfg);
// 转换 data 中的 CRLF 为 LF有些模拟器自带的 adbexec-out 输出的 \n 会被替换成 \r\n
// 导致解码错误,所以这里转一下回来(点名批评 mumu 和雷电)
@@ -158,6 +160,7 @@ protected:
Encode,
#if ASST_WITH_EMULATOR_EXTRAS
MumuExtras,
LDExtras,
#endif
} screencap_method = ScreencapMethod::UnknownYet;
} m_adb;
@@ -178,6 +181,7 @@ protected:
#if ASST_WITH_EMULATOR_EXTRAS
MumuExtras m_mumu_extras;
LDExtras m_ld_extras;
#endif
};
} // namespace asst

124
src/MaaCore/LDExtras.cpp Normal file
View File

@@ -0,0 +1,124 @@
#include "LDExtras.h"
#if ASST_WITH_EMULATOR_EXTRAS
#include "Utils/Logger.hpp"
#include "Utils/NoWarningCV.h"
#include "Utils/Platform/SafeWindows.h"
#include <string>
#include <sstream>
#include <Controller/AdbController.h>
namespace asst
{
LDExtras::~LDExtras()
{
uninit();
}
bool LDExtras::init(const std::filesystem::path& ld_path, unsigned int ld_inst_index, unsigned int ld_pid, int width, int height)
{
const bool same = ld_inst_index == ld_inst_index_ && ld_pid == ld_pid_;
if (same && inited_) {
return true;
}
ld_inst_index_ = ld_inst_index;
ld_path_ = ld_path;
ld_pid_ = ld_pid;
display_width_ = width;
display_height_ = height;
inited_ = load_ld_library() && connect_ld() && init_screencap();
return inited_;
}
bool LDExtras::reload()
{
inited_ = load_ld_library() && connect_ld() && init_screencap();
LogInfo << "Reload LDExtras: " << VAR(inited_);
return inited_;
}
void LDExtras::uninit()
{
inited_ = false;
disconnect_ld();
}
std::optional<cv::Mat> LDExtras::screencap() const
{
if (!screenshot_instance_) {
LogError << "screenshot_instance_ is null";
return std::nullopt;
}
void* pixels = screenshot_instance_->cap();
if (!pixels) {
LogError << "Failed to capture screen";
return std::nullopt;
}
const cv::Mat raw(display_height_, display_width_, CV_8UC3, pixels);
cv::Mat dst;
cv::flip(raw, dst, 0);
return dst;
}
bool LDExtras::load_ld_library()
{
auto lib_path = ld_path_ / "ldopengl64.dll";
if (!load_library(lib_path)) {
LogError << "Failed to load library" << VAR(lib_path);
return false;
}
create_screenshot_instance_func_ = get_function<decltype(CreateScreenShotInstance)>(kCreateScreenShotInstanceFuncName);
if (!create_screenshot_instance_func_) {
LogError << "Failed to get function" << VAR(kCreateScreenShotInstanceFuncName);
return false;
}
return true;
}
bool LDExtras::connect_ld()
{
LogInfo << VAR(ld_inst_index_) << VAR(ld_pid_);
screenshot_instance_ = create_screenshot_instance_func_(ld_inst_index_, ld_pid_);
if (!screenshot_instance_) {
LogError << "Failed to create screenshot instance" << VAR(ld_inst_index_);
return false;
}
return true;
}
bool LDExtras::init_screencap()
{
if (!screenshot_instance_) {
LogError << "screenshot_instance_ is null";
return false;
}
LogDebug << VAR(display_width_) << VAR(display_height_);
return true;
}
void LDExtras::disconnect_ld()
{
LogInfo << "Disconnecting LD";
if (screenshot_instance_) {
screenshot_instance_->release();
screenshot_instance_ = nullptr;
}
}
}
#endif

58
src/MaaCore/LDExtras.h Normal file
View File

@@ -0,0 +1,58 @@
#pragma once
#include "Common/AsstConf.h"
#if ASST_WITH_EMULATOR_EXTRAS
#include <filesystem>
#include <optional>
#include <string>
#include "Utils/Platform/SafeWindows.h"
#include "LD/dnopengl/dnopengl.h"
#include "Utils/LibraryHolder.hpp"
#include "Utils/NoWarningCVMat.h"
namespace asst
{
class LDExtras : public LibraryHolder<LDExtras>
{
public:
LDExtras() = default;
virtual ~LDExtras();
bool inited() const { return inited_; }
bool init(const std::filesystem::path& ld_path, unsigned int ld_inst_index, unsigned int ld_pid, int width, int height);
bool reload();
void uninit();
std::optional<cv::Mat> screencap() const;
private:
bool load_ld_library();
bool connect_ld();
bool init_screencap();
void disconnect_ld();
private:
unsigned int ld_inst_index_ = 0;
unsigned int ld_pid_ = 0;
IScreenShotClass* screenshot_instance_ = nullptr;
int display_width_ = 0;
int display_height_ = 0;
bool inited_ = false;
private:
std::filesystem::path ld_path_;
inline static const std::string kCreateScreenShotInstanceFuncName = "CreateScreenShotInstance";
private:
std::function<decltype(CreateScreenShotInstance)> create_screenshot_instance_func_;
};
}
#endif

View File

@@ -69,6 +69,7 @@
<ClInclude Include="Controller\Platform\PlatformIO.h" />
<ClInclude Include="Controller\Platform\PlatformFactory.h" />
<ClInclude Include="InstHelper.h" />
<ClInclude Include="LDExtras.h" />
<ClInclude Include="Task\Experiment\CombatRecordRecognitionTask.h" />
<ClInclude Include="Task\Experiment\SingleStepBattleProcessTask.h" />
<ClInclude Include="Task\Fight\FightTimesTaskPlugin.h" />
@@ -260,6 +261,7 @@
<ClCompile Include="Controller\Platform\PosixIO.cpp" />
<ClCompile Include="Controller\Platform\Win32IO.cpp" />
<ClCompile Include="InstHelper.cpp" />
<ClCompile Include="LDExtras.cpp" />
<ClCompile Include="Task\Experiment\CombatRecordRecognitionTask.cpp" />
<ClCompile Include="Task\Experiment\SingleStepBattleProcessTask.cpp" />
<ClCompile Include="Task\Fight\FightTimesTaskPlugin.cpp" />

View File

@@ -728,6 +728,9 @@
<ClInclude Include="Task\Roguelike\Map\RoguelikeRoutingTaskPlugin.h">
<Filter>Source\Task\Roguelike</Filter>
</ClInclude>
<ClInclude Include="LDExtras.h">
<Filter>Source\Controller</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Vision\VisionHelper.cpp">
@@ -1221,5 +1224,8 @@
<ClCompile Include="Config\Roguelike\Sami\RoguelikeFoldartalConfig.cpp">
<Filter>Source\Resource\Roguelike</Filter>
</ClCompile>
<ClCompile Include="LDExtras.cpp">
<Filter>Source\Controller</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@@ -64,6 +64,9 @@ namespace MaaWpfGui.Constants
public const string MuMu12EmulatorPath = "Connect.MuMu12EmulatorPath";
public const string MuMu12Index = "Connect.MuMu12Index";
public const string MuMu12Display = "Connect.MuMu12Display";
public const string LdPlayerExtrasEnabled = "Connect.LdPlayerExtras.Enabled";
public const string LdPlayerEmulatorPath = "Connect.LdPlayerEmulatorPath";
public const string LdPlayerIndex = "Connect.LdPlayerIndex";
public const string RetryOnAdbDisconnected = "Connect.RetryOnDisconnected";
public const string AllowAdbRestart = "Connect.AllowADBRestart";
public const string AllowAdbHardRestart = "Connect.AllowADBHardRestart";

View File

@@ -64,6 +64,11 @@ namespace MaaWpfGui.Constants
/// </summary>
public const string MuMuSpecialScreenshot = "MuMuSpecialScreenshot";
/// <summary>
/// The recommended color for LD special screenshot.
/// </summary>
public const string LdSpecialScreenshot = "LdSpecialScreenshot";
// 颜色在MaaWpfGui\Res\Themes中定义
// Brush are defined in MaaWpfGui\Res\Themes
}

View File

@@ -119,6 +119,11 @@ namespace MaaWpfGui.Main
AsstSetConnectionExtras("MuMuEmulator12", extras);
}
private static void AsstSetConnectionExtrasLdPlayer(string extras)
{
AsstSetConnectionExtras("LDPlayer", extras);
}
private static unsafe AsstTaskId AsstAppendTask(AsstHandle handle, string type, string taskParams)
{
fixed (byte* ptr1 = EncodeNullTerminatedUtf8(type),
@@ -534,6 +539,20 @@ namespace MaaWpfGui.Main
method = "MuMuExtras";
}
break;
case "LDPlayer":
if (Instances.SettingsViewModel.LdPlayerExtras.Enable && method != "LDExtras")
{
Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("LdExtrasNotEnabledMessage"), UiLogColor.Error);
Instances.CopilotViewModel.AddLog(LocalizationHelper.GetString("LdExtrasNotEnabledMessage"), UiLogColor.Error, showTime: false);
needToStop = true;
}
else if (timeCost < 100)
{
color = UiLogColor.LdSpecialScreenshot;
method = "LDExtras";
}
break;
}
@@ -1766,6 +1785,9 @@ namespace MaaWpfGui.Main
case "MuMuEmulator12":
AsstSetConnectionExtrasMuMu12(Instances.SettingsViewModel.MuMuEmulator12Extras.Config);
break;
case "LDPlayer":
AsstSetConnectionExtrasLdPlayer(Instances.SettingsViewModel.LdPlayerExtras.Config);
break;
}
if (Instances.SettingsViewModel.AutoDetectConnection)

View File

@@ -395,6 +395,22 @@ Only supports Official(CN) and Bilibili server. Login account is not supported.<
<system:String x:Key="MuMu12Index">Instance Number</system:String>
<system:String x:Key="MuMu12Display">Display Number</system:String>
<system:String x:Key="MuMuExtrasNotEnabledMessage">Mumu screenshot enhancement not enabled, please check related settings</system:String>
<system:String x:Key="LdExtrasEnabled">Enable LD's screenshot enhancement mode</system:String>
<system:String x:Key="LdExtrasEnabledTip">
Requires LDPlayer 9 V9.0.78 or later.\n
It is strongly recommended to perform a screenshot test before using this feature for the first time.\n\n
Please provide the following information:\n\n
1. Emulator path: (e.g., C:\\leidian\\LDPlayer9)\n
The emulator path should be the folder where the emulator application is located, for example, if the application path is\n
"C:\\leidian\\LDPlayer9\\ldconsole.exe"\n
You should fill in\n
"C:\\leidian\\LDPlayer9".\n\n
2. Instance number: (e.g., 0)\n
The instance number can be found in the LDPlayer multi-instance manager, default is 0. If there are multiple instances, the number can be found in the ID column under Version/ID.\n
</system:String>
<system:String x:Key="LdEmulatorPath">LD Emulator exec path</system:String>
<system:String x:Key="LdIndex">Instance Number</system:String>
<system:String x:Key="LdExtrasNotEnabledMessage">LD screenshot enhancement not enabled, please check related settings</system:String>
<system:String x:Key="RetryOnDisconnected">Retry launching the emulator when ADB connection fails</system:String>
<system:String x:Key="RetryOnDisconnectedTip">Try restarting the emulator after 20 failed reconnect attempts, must be checked when using a background timed task and setting the task to close the emulator when finished</system:String>
<system:String x:Key="RetryOnDisconnectedEmulatorPathEmptyError">Emulator path is empty! Please fill in "Launch Settings - Emulator Path" and reselect.</system:String>

View File

@@ -395,6 +395,21 @@
<system:String x:Key="MuMu12Index">インスタンス番号</system:String>
<system:String x:Key="MuMu12Display">表示番号</system:String>
<system:String x:Key="MuMuExtrasNotEnabledMessage">MuMu スクリーンショットの強化 が有効になっていません。関連する設定を確認してください</system:String>
<system:String x:Key="LdExtrasEnabled">LD のスクリーンショット拡張モードを有効にする</system:String>
<system:String x:Key="LdExtrasEnabledTip">
LDPlayer 9 V9.0.78 以降のバージョンが必要です。\n
この機能を初めて使用する前に、スクリーンショットテストを行うことを強くお勧めします。\n\n
以下の情報を入力してください:\n\n
1. エミュレーターのパスC:\\leidian\\LDPlayer9\n
エミュレーターのパスは、エミュレーターアプリケーションが存在するフォルダのパスを入力してください。例えば、アプリケーションパスが\n
「C:\\leidian\\LDPlayer9\\ldconsole.exe」の場合、\n
「C:\\leidian\\LDPlayer9」と入力してください。\n\n
2. インスタンス番号0\n
インスタンス番号は、LDPlayerのマルチインスタンスマネージャーで確認できます。デフォルトは0です。複数のインスタンスがある場合、バージョン/番号のID列に番号が表示されます。\n
</system:String>
<system:String x:Key="LdEmulatorPath">LD エミュレータパス</system:String>
<system:String x:Key="LdIndex">インスタンス番号</system:String>
<system:String x:Key="LdExtrasNotEnabledMessage">LD スクリーンショットの強化 が有効になっていません。関連する設定を確認してください</system:String>
<system:String x:Key="RetryOnDisconnected">ADB接続が失敗した場合、エミュレータの起動を試みる</system:String>
<system:String x:Key="RetryOnDisconnectedTip">再接続に20回失敗した後、エミュレータを再起動してみてくださいバックグラウンドのタイムドタスクを使用し、終了時にエミュレータを終了させるタスクを設定する場合は、必ずチェックを入れること</system:String>
<system:String x:Key="RetryOnDisconnectedEmulatorPathEmptyError">エミュレータのパスが空です!「起動設定-エミュレータのパス」を入力してから再度選択してください。</system:String>

View File

@@ -396,6 +396,21 @@ OF-1을 해금하지 않았다면 선택하지 말아 주세요.</system:String>
<system:String x:Key="MuMu12Index">인스턴스 번호</system:String>
<system:String x:Key="MuMu12Display">표시 번호</system:String>
<system:String x:Key="MuMuExtrasNotEnabledMessage">MuMu의 스크린샷 향상가 활성화되지 않았습니다. 관련 설정을 확인하세요</system:String>
<system:String x:Key="LdExtrasEnabled">LD 스크린샷 향상 모드 활성화</system:String>
<system:String x:Key="LdExtrasEnabledTip">
LDPlayer 9 V9.0.78 이상 버전이 필요합니다.\n
이 기능을 처음 사용하기 전에 스크린샷 테스트를 수행하는 것이 좋습니다.\n\n
다음 정보를 입력하십시오:\n\n
1. 에뮬레이터 경로: (예: C:\\leidian\\LDPlayer9)\n
에뮬레이터 경로는 에뮬레이터 응용 프로그램이 있는 폴더 경로여야 합니다. 예를 들어, 응용 프로그램 경로가\n
"C:\\leidian\\LDPlayer9\\ldconsole.exe"인 경우,\n
"C:\\leidian\\LDPlayer9"를 입력해야 합니다。\n\n
2. 인스턴스 번호: (예: 0)\n
인스턴스 번호는 LDPlayer 다중 인스턴스 관리자에서 확인할 수 있습니다. 기본값은 0입니다. 여러 인스턴스가 있는 경우, 버전/번호의 ID 열에서 번호를 찾을 수 있습니다。\n
</system:String>
<system:String x:Key="LdEmulatorPath">LD 에뮬레이터 경로</system:String>
<system:String x:Key="LdIndex">인스턴스 번호</system:String>
<system:String x:Key="LdExtrasNotEnabledMessage">LD 의 스크린샷 향상가 활성화되지 않았습니다. 관련 설정을 확인하세요</system:String>
<system:String x:Key="RetryOnDisconnected">ADB 연결이 실패하면 에뮬레이터를 다시 시도합니다</system:String>
<system:String x:Key="RetryOnDisconnectedTip">재연결을 20회 시도해도 실패하면 에뮬레이터를 다시 시작합니다. 백그라운드 예약을 사용하고 작업이 완료되면 에뮬레이터가 종료되도록 설정하려면 반드시 선택해야 합니다</system:String>
<system:String x:Key="RetryOnDisconnectedEmulatorPathEmptyError">에뮬레이터 경로가 비어 있습니다!「연결 설정/에뮬레이터 경로」를 입력한 후 다시 선택하세요.</system:String>

View File

@@ -395,6 +395,22 @@
<system:String x:Key="MuMu12Index">实例编号</system:String>
<system:String x:Key="MuMu12Display">屏幕编号</system:String>
<system:String x:Key="MuMuExtrasNotEnabledMessage">Mumu 截图增强未生效,请检查相关设置</system:String>
<system:String x:Key="LdExtrasEnabled">启用 LD 截图增强模式</system:String>
<system:String x:Key="LdExtrasEnabledTip">
需使用 LDPlayer 9 V9.0.78 及更新版本。\n
强烈建议在开启本功能后的首次使用之前进行截图测试。\n\n
请填写以下信息:\n\n
1. 模拟器路径例如C:\\leidian\\LDPlayer9\n
模拟器路径应填写模拟器应用所在文件夹路径,例如应用路径为\n
「C:\\leidian\\LDPlayer9\\ldconsole.exe」\n
应填写\n
「C:\\leidian\\LDPlayer9」。\n\n
2. 实例编号例如0\n
实例编号可在 LDPlayer 多开器中查看,默认为 0若有多开实例编号见 版本/编号 中的 ID。\n
</system:String>
<system:String x:Key="LdEmulatorPath">LD 安装路径</system:String>
<system:String x:Key="LdIndex">实例编号</system:String>
<system:String x:Key="LdExtrasNotEnabledMessage">LD 截图增强未生效,请检查相关设置</system:String>
<system:String x:Key="RetryOnDisconnected">ADB 连接失败时尝试启动模拟器</system:String>
<system:String x:Key="RetryOnDisconnectedTip">重连失败 20 次后尝试重启模拟器,使用后台定时任务且设置任务完成关闭模拟器时请勾选</system:String>
<system:String x:Key="RetryOnDisconnectedEmulatorPathEmptyError">模拟器路径为空!请填写「启动设置-模拟器路径」后重新勾选。</system:String>

View File

@@ -395,6 +395,22 @@
<system:String x:Key="MuMu12Index">實例號碼</system:String>
<system:String x:Key="MuMu12Display">螢幕號碼</system:String>
<system:String x:Key="MuMuExtrasNotEnabledMessage">Mumu 截圖增強未生效,请檢查相關設定</system:String>
<system:String x:Key="LdExtrasEnabled">啟用 LD 截圖增強模式</system:String>
<system:String x:Key="LdExtrasEnabledTip">
需使用 LDPlayer 9 V9.0.78 及更新版本。\n
強烈建議在開啟本功能後的首次使用之前進行截圖測試。\n\n
請填寫以下信息:\n\n
1. 模擬器路徑例如C:\\leidian\\LDPlayer9\n
模擬器路徑應填寫模擬器應用所在文件夾路徑,例如應用路徑為\n
「C:\\leidian\\LDPlayer9\\ldconsole.exe」\n
應填寫\n
「C:\\leidian\\LDPlayer9」。\n\n
2. 實例編號例如0\n
實例編號可在 LDPlayer 多開器中查看,默認為 0若有多開實例編號見 版本/編號 中的 ID。\n
</system:String>
<system:String x:Key="LdEmulatorPath">LD 模擬器路徑</system:String>
<system:String x:Key="LdIndex">實例號碼</system:String>
<system:String x:Key="LdExtrasNotEnabledMessage">LD 截圖增強未生效,请檢查相關設定</system:String>
<system:String x:Key="RetryOnDisconnected">ADB 連線失敗時嘗試啟動模擬器</system:String>
<system:String x:Key="RetryOnDisconnectedTip">重連失敗 20 次後嘗試重開模擬器,使用後台定時任務且設定任務完成關閉模擬器時必須勾選</system:String>
<system:String x:Key="RetryOnDisconnectedEmulatorPathEmptyError">模擬器路徑為空!請填寫「啟動設定-模擬器路徑」後重新勾選。</system:String>

View File

@@ -44,4 +44,5 @@
<SolidColorBrush x:Key="RobotOperatorLogBrush" Color="Gray" />
<SolidColorBrush x:Key="DownloadLogBrush" Color="Violet" />
<SolidColorBrush x:Key="MuMuSpecialScreenshot" Color="#02BFFF" />
<SolidColorBrush x:Key="LdSpecialScreenshot" Color="#6666FF" />
</ResourceDictionary>

View File

@@ -26,4 +26,5 @@
<SolidColorBrush x:Key="RobotOperatorLogBrush" Color="DarkGray" />
<SolidColorBrush x:Key="DownloadLogBrush" Color="BlueViolet" />
<SolidColorBrush x:Key="MuMuSpecialScreenshot" Color="#02BFFF" />
<SolidColorBrush x:Key="LdSpecialScreenshot" Color="#6666FF" />
</ResourceDictionary>

View File

@@ -4305,6 +4305,144 @@ namespace MaaWpfGui.ViewModels.UI
public MuMuEmulator12ConnectionExtras MuMuEmulator12Extras { get; set; } = new();
public class LdPlayerConnectionExtras : PropertyChangedBase
{
private bool _enable = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.LdPlayerExtrasEnabled, bool.FalseString));
public bool Enable
{
get => _enable;
set
{
if (!SetAndNotify(ref _enable, value))
{
return;
}
if (value)
{
MessageBoxHelper.Show(LocalizationHelper.GetString("LdExtrasEnabledTip"));
}
Instances.AsstProxy.Connected = false;
ConfigurationHelper.SetValue(ConfigurationKeys.LdPlayerExtrasEnabled, value.ToString());
}
}
private static readonly string _configuredPath = ConfigurationHelper.GetValue(ConfigurationKeys.LdPlayerEmulatorPath, @"C:\leidian\LDPlayer9");
private string _emulatorPath = Directory.Exists(_configuredPath) ? _configuredPath : string.Empty;
/// <summary>
/// Gets or sets a value indicating the path of the emulator.
/// </summary>
public string EmulatorPath
{
get => _emulatorPath;
set
{
if (_enable && !string.IsNullOrEmpty(value) && !Directory.Exists(value))
{
MessageBoxHelper.Show("LD Emulator Path Not Found");
value = string.Empty;
}
Instances.AsstProxy.Connected = false;
SetAndNotify(ref _emulatorPath, value);
ConfigurationHelper.SetValue(ConfigurationKeys.LdPlayerEmulatorPath, value);
}
}
private string _index = ConfigurationHelper.GetValue(ConfigurationKeys.LdPlayerIndex, "0");
/// <summary>
/// Gets or sets the index of the emulator.
/// </summary>
public string Index
{
get => _index;
set
{
Instances.AsstProxy.Connected = false;
SetAndNotify(ref _index, value);
ConfigurationHelper.SetValue(ConfigurationKeys.LdPlayerIndex, value);
}
}
private int GetEmulatorPid(int index)
{
string emulatorPath = $@"{EmulatorPath}\ldconsole.exe";
if (!File.Exists(emulatorPath))
{
MessageBoxHelper.Show("LD Emulator Path Not Found");
return 0;
}
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = emulatorPath,
Arguments = "list2",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
};
using Process? process = Process.Start(startInfo);
if (process == null)
{
_logger.Warning("Failed to start ldconsole list2");
return 0;
}
using StreamReader reader = process.StandardOutput;
string result = reader.ReadToEnd();
string[] lines = result.Split([Environment.NewLine], StringSplitOptions.RemoveEmptyEntries);
if (lines.Length <= 0)
{
_logger.Warning("Failed to get emulator PID.");
return 0;
}
foreach (string line in lines)
{
string[] parts = line.Split(',');
if (parts.Length < 6 || !int.TryParse(parts[0], out int currentIndex) || currentIndex != index)
{
continue;
}
if (int.TryParse(parts[5], out int pid))
{
return pid;
}
}
_logger.Warning("Failed to get emulator PID.");
return 0;
}
public string Config
{
get
{
if (!Enable)
{
return JsonConvert.SerializeObject(new JObject());
}
var configObject = new JObject
{
["path"] = EmulatorPath,
["index"] = int.TryParse(Index, out var indexParse) ? indexParse : 0,
["pid"] = GetEmulatorPid(indexParse),
};
return JsonConvert.SerializeObject(configObject);
}
}
}
public LdPlayerConnectionExtras LdPlayerExtras { get; set; } = new();
private bool _retryOnDisconnected = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.RetryOnAdbDisconnected, bool.FalseString));
/// <summary>

View File

@@ -53,8 +53,8 @@
</StackPanel>
<StackPanel
Grid.Row="1"
Orientation="Horizontal"
HorizontalAlignment="Center">
HorizontalAlignment="Center"
Orientation="Horizontal">
<hc:TextBox
Width="420"
Height="30"
@@ -120,43 +120,69 @@
ItemsSource="{Binding ConnectConfigList}"
SelectedValue="{Binding ConnectConfig}"
SelectedValuePath="Value" />
<CheckBox
Margin="10"
HorizontalAlignment="Center"
Content="{DynamicResource MuMu12ExtrasEnabled}"
IsChecked="{Binding MuMuEmulator12Extras.Enable}"
Visibility="{c:Binding 'ConnectConfig==&quot;MuMuEmulator12&quot;'}" />
<hc:TextBox
Width="420"
Height="30"
Margin="10"
hc:InfoElement.Placeholder="Example: C:\Program Files\Netease\MuMuPlayer-12.0"
hc:TitleElement.Title="{DynamicResource MuMu12EmulatorPath}"
hc:TitleElement.TitlePlacement="Left"
Text="{Binding MuMuEmulator12Extras.EmulatorPath}"
Visibility="{c:Binding 'MuMuEmulator12Extras.Enable and ConnectConfig==&quot;MuMuEmulator12&quot;'}" />
<StackPanel
HorizontalAlignment="Center"
Orientation="Horizontal"
Visibility="{c:Binding MuMuEmulator12Extras.Enable}">
<hc:TextBox
Width="130"
Height="30"
Margin="10"
hc:InfoElement.Placeholder="0"
hc:TitleElement.Title="{DynamicResource MuMu12Index}"
hc:TitleElement.TitlePlacement="Left"
Text="{Binding MuMuEmulator12Extras.Index}"
Visibility="{c:Binding 'ConnectConfig==&quot;MuMuEmulator12&quot;'}" />
<hc:TextBox
Width="130"
Height="30"
Margin="10"
hc:InfoElement.Placeholder="0"
hc:TitleElement.Title="{DynamicResource MuMu12Display}"
hc:TitleElement.TitlePlacement="Left"
Text="{Binding MuMuEmulator12Extras.Display}"
Visibility="{c:Binding 'ConnectConfig==&quot;MuMuEmulator12&quot;'}" />
<StackPanel>
<StackPanel Visibility="{c:Binding 'ConnectConfig==&quot;MuMuEmulator12&quot;'}">
<CheckBox
Margin="10"
HorizontalAlignment="Center"
Content="{DynamicResource MuMu12ExtrasEnabled}"
IsChecked="{Binding MuMuEmulator12Extras.Enable}" />
<hc:TextBox
Width="420"
Height="30"
Margin="10"
hc:InfoElement.Placeholder="Example: C:\Program Files\Netease\MuMuPlayer-12.0"
hc:TitleElement.Title="{DynamicResource MuMu12EmulatorPath}"
hc:TitleElement.TitlePlacement="Left"
Text="{Binding MuMuEmulator12Extras.EmulatorPath}"
Visibility="{c:Binding MuMuEmulator12Extras.Enable}" />
<StackPanel
HorizontalAlignment="Center"
Orientation="Horizontal"
Visibility="{c:Binding MuMuEmulator12Extras.Enable}">
<hc:TextBox
Width="130"
Height="30"
Margin="10"
hc:InfoElement.Placeholder="0"
hc:TitleElement.Title="{DynamicResource MuMu12Index}"
hc:TitleElement.TitlePlacement="Left"
Text="{Binding MuMuEmulator12Extras.Index}" />
<hc:TextBox
Width="130"
Height="30"
Margin="10"
hc:InfoElement.Placeholder="0"
hc:TitleElement.Title="{DynamicResource MuMu12Display}"
hc:TitleElement.TitlePlacement="Left"
Text="{Binding MuMuEmulator12Extras.Display}" />
</StackPanel>
</StackPanel>
<StackPanel Visibility="{c:Binding 'ConnectConfig==&quot;LDPlayer&quot;'}">
<CheckBox
Margin="10"
HorizontalAlignment="Center"
Content="{DynamicResource LdExtrasEnabled}"
IsChecked="{Binding LdPlayerExtras.Enable}" />
<hc:TextBox
Width="420"
Height="30"
Margin="10"
hc:InfoElement.Placeholder="Example: C:\leidian\LDPlayer9"
hc:TitleElement.Title="{DynamicResource LdEmulatorPath}"
hc:TitleElement.TitlePlacement="Left"
Text="{Binding LdPlayerExtras.EmulatorPath}"
Visibility="{c:Binding LdPlayerExtras.Enable}" />
<hc:TextBox
Width="130"
Height="30"
Margin="10"
hc:InfoElement.Placeholder="0"
hc:TitleElement.Title="{DynamicResource LdIndex}"
hc:TitleElement.TitlePlacement="Left"
Text="{Binding LdPlayerExtras.Index}"
Visibility="{c:Binding LdPlayerExtras.Enable}" />
</StackPanel>
</StackPanel>
<StackPanel HorizontalAlignment="Center" Orientation="Vertical">
<Button
@@ -172,9 +198,7 @@
Visibility="{c:Binding 'TestLinkInfo!=&quot;&quot;'}" />
</StackPanel>
</StackPanel>
<StackPanel
Grid.Row="4"
HorizontalAlignment="Center">
<StackPanel Grid.Row="4" HorizontalAlignment="Center">
<CheckBox
Margin="20,10"
Content="{DynamicResource RetryOnDisconnected}"
@@ -216,8 +240,7 @@
Content="{DynamicResource ForcedReplaceAdb}" />
</StackPanel>
<StackPanel
Grid.Row="6">
<StackPanel Grid.Row="6">
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<CheckBox
Margin="20,10"