mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-03-30 10:19:51 +08:00
435 lines
15 KiB
C#
435 lines
15 KiB
C#
using BetterGenshinImpact.Core.Config;
|
||
using BetterGenshinImpact.GameTask.GameLoading.Assets;
|
||
using System;
|
||
using System.Diagnostics;
|
||
using BetterGenshinImpact.GameTask.Common;
|
||
using BetterGenshinImpact.GameTask.Common.BgiVision;
|
||
using BetterGenshinImpact.GameTask.Common.Element.Assets;
|
||
using BetterGenshinImpact.GameTask.Model.Area;
|
||
using Microsoft.Extensions.Logging;
|
||
using System.IO;
|
||
using System.Text.RegularExpressions;
|
||
using Microsoft.Win32;
|
||
using System.Linq;
|
||
using System.Threading;
|
||
using System.Text;
|
||
using Vanara.PInvoke;
|
||
|
||
namespace BetterGenshinImpact.GameTask.GameLoading;
|
||
|
||
public class GameLoadingTrigger : ITaskTrigger
|
||
{
|
||
public static bool GlobalEnabled = true;
|
||
|
||
public string Name => "自动开门";
|
||
|
||
public bool IsEnabled { get => GlobalEnabled; set {} }
|
||
|
||
public int Priority => 999;
|
||
|
||
public bool IsExclusive => false;
|
||
|
||
public bool IsBiliJudged = false;
|
||
public bool IsBili = false;
|
||
|
||
public bool IsBackgroundRunning => true;
|
||
|
||
private readonly GameLoadingAssets _assets;
|
||
|
||
private readonly GenshinStartConfig _config = TaskContext.Instance().Config.GenshinStartConfig;
|
||
private static ILogger<GameLoadingTrigger> _logger = App.GetLogger<GameLoadingTrigger>();
|
||
|
||
|
||
// private int _enterGameClickCount = 0;
|
||
// private int _welkinMoonClickCount = 0;
|
||
// private int _noneClickCount, _wmNoneClickCount;
|
||
|
||
private DateTime _prevExecuteTime = DateTime.MinValue;
|
||
|
||
private DateTime _triggerStartTime = DateTime.Now;
|
||
|
||
private string GameServer = "";
|
||
|
||
private string channelValue = "";
|
||
|
||
private string FileName = "";
|
||
|
||
private bool biliLoginClicked = false;
|
||
private (double x1080, double y1080)? lastAgreementClickPos = null;
|
||
|
||
public GameLoadingTrigger()
|
||
{
|
||
GameLoadingAssets.DestroyInstance();
|
||
_assets = GameLoadingAssets.Instance;
|
||
}
|
||
|
||
public void InnerSetEnabled(bool enabled)
|
||
{
|
||
GlobalEnabled = enabled;
|
||
}
|
||
|
||
public void Init()
|
||
{
|
||
if (!_config.AutoEnterGameEnabled)
|
||
{
|
||
InnerSetEnabled(false);
|
||
}
|
||
|
||
// // 前面没有联动启动原神,这个任务也不用启动
|
||
// if ((DateTime.Now - TaskContext.Instance().LinkedStartGenshinTime).TotalMinutes >= 5)
|
||
// {
|
||
// IsEnabled = false;
|
||
// }
|
||
if (_config.RecordGameTimeEnabled)
|
||
{
|
||
FileName = Path.GetFileName(_config.InstallPath);
|
||
if (FileName == "GenshinImpact.exe")
|
||
{
|
||
GameServer = "hk4e_global";
|
||
StartStarward();
|
||
}
|
||
|
||
if (FileName == "YuanShen.exe")
|
||
{
|
||
string iniPath = Path.GetDirectoryName(_config.InstallPath) + "//config.ini";
|
||
string iniContent;
|
||
string pattern = @"
|
||
^\s*\[General\]\s*$
|
||
(?:(?!\[).|\r?\n)*
|
||
^\s*channel=(\S+)
|
||
";
|
||
|
||
try
|
||
{
|
||
iniContent = File.ReadAllText(iniPath);
|
||
Regex regex = new Regex(pattern,
|
||
RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase);
|
||
Match match = regex.Match(iniContent);
|
||
channelValue = match.Success ? match.Groups[1].Value : "";
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
}
|
||
|
||
// channelValue = 1 : 官服
|
||
// channelValue = 14 : B服
|
||
if (channelValue == "1")
|
||
{
|
||
GameServer = "hk4e_cn";
|
||
StartStarward();
|
||
}
|
||
|
||
if (channelValue == "14")
|
||
{
|
||
GameServer = "hk4e_bilibili";
|
||
StartStarward();
|
||
}
|
||
|
||
|
||
Debug.WriteLine($"[GameLoading] 从文件读取到游戏区服:{GameServer}");
|
||
// 这里注册表的优先级要比读取文件低,因为使用starward安装原神不会写入注册表
|
||
if (GameServer == null)
|
||
{
|
||
GameServer = GetGameServerRegistry();
|
||
Debug.WriteLine($"[GameLoading] 从注册表读取到游戏区服:{GameServer}");
|
||
StartStarward();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public bool StartStarward()
|
||
{
|
||
try
|
||
{
|
||
Debug.WriteLine($"[GameLoading] 服务器:{GameServer}");
|
||
if (IsStarwardProtocolRegistered())
|
||
{
|
||
Process.Start(new ProcessStartInfo($"starward://playtime/{GameServer}") { UseShellExecute = true });
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
TaskControl.Logger.LogWarning("没有检测到 Starward 协议注册,请查看帮助文档!");
|
||
return false;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine("[GameLoading] Starward记录时间失败");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public string GetGameServerRegistry()
|
||
{
|
||
try
|
||
{
|
||
var cn =
|
||
Registry.GetValue($@"HKEY_CURRENT_USER\Software\miHoYo\HYP\1_1\hk4e_cn", "GameInstallPath",
|
||
null) as string;
|
||
if (!string.IsNullOrEmpty(cn))
|
||
{
|
||
var filePath = Path.Combine(cn, "YuanShen.exe");
|
||
GameServer = "hk4e_cn";
|
||
return GameServer;
|
||
}
|
||
|
||
var global = Registry.GetValue($@"HKEY_CURRENT_USER\Software\Cognosphere\HYP\1_0\hk4e_global",
|
||
"GameInstallPath", null) as string;
|
||
if (!string.IsNullOrEmpty(global))
|
||
{
|
||
var filePath = Path.Combine(global, "GenshinImpact.exe");
|
||
GameServer = "hk4e_global";
|
||
return GameServer;
|
||
}
|
||
|
||
var bilibili =
|
||
Registry.GetValue($@"HKEY_CURRENT_USER\Software\miHoYo\HYP\standalone\14_0\hk4e_cn\umfgRO5gh5\hk4e_cn",
|
||
"GameInstallPath", null) as string;
|
||
if (!string.IsNullOrEmpty(bilibili))
|
||
{
|
||
var filePath = Path.Combine(bilibili, "YuanShen.exe");
|
||
GameServer = "hk4e_bilibili";
|
||
return GameServer;
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
TaskControl.Logger.LogDebug(e, "获取服务器失败");
|
||
}
|
||
|
||
return "";
|
||
}
|
||
|
||
public bool IsStarwardProtocolRegistered()
|
||
{
|
||
try
|
||
{
|
||
// 打开注册表路径 HKEY_CLASSES_ROOT\starward
|
||
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("starward"))
|
||
{
|
||
// 如果键存在
|
||
if (key != null)
|
||
{
|
||
// 检查是否存在 URL Protocol 值
|
||
object urlProtocol = key.GetValue("URL Protocol");
|
||
// 如果 URL Protocol 存在且值为空字符串(标准配置),认为协议已注册
|
||
if (urlProtocol != null && urlProtocol.ToString() == "")
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 如果访问注册表时发生错误,记录调试信息
|
||
Debug.WriteLine($"[GameLoading] 检查 Starward 协议时发生错误: {ex.Message}");
|
||
}
|
||
|
||
// 如果键不存在或不符合条件,返回 false
|
||
return false;
|
||
}
|
||
|
||
public void OnCapture(CaptureContent content)
|
||
{
|
||
// 2s 一次
|
||
if ((DateTime.Now - _prevExecuteTime).TotalMilliseconds <= 2000)
|
||
{
|
||
return;
|
||
}
|
||
|
||
_prevExecuteTime = DateTime.Now;
|
||
// 5min 后自动停止
|
||
if ((DateTime.Now - _triggerStartTime).TotalMinutes >= 5)
|
||
{
|
||
InnerSetEnabled(false);
|
||
return;
|
||
}
|
||
// 成功进入游戏判断
|
||
if (Bv.IsInMainUi(content.CaptureRectArea) || Bv.IsInAnyClosableUi(content.CaptureRectArea) || Bv.IsInDomain(content.CaptureRectArea))
|
||
{
|
||
// _logger.LogInformation("当前在游戏主界面");
|
||
InnerSetEnabled(false);
|
||
return;
|
||
}
|
||
|
||
// B服判断
|
||
if (!IsBiliJudged)
|
||
{
|
||
try
|
||
{
|
||
var exePath = _config.InstallPath;
|
||
if (!string.IsNullOrEmpty(exePath))
|
||
{
|
||
var configIni = Path.Combine(Path.GetDirectoryName(exePath)!, "config.ini");
|
||
if (File.Exists(configIni))
|
||
{
|
||
var lines = File.ReadAllLines(configIni);
|
||
foreach (var line in lines)
|
||
{
|
||
var kv = line.Trim();
|
||
if (kv.StartsWith("channel=") && kv.EndsWith("14"))
|
||
{
|
||
IsBili = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
TaskControl.Logger.LogWarning("B服判断异常: " + ex.Message);
|
||
}
|
||
IsBiliJudged = true;
|
||
}
|
||
|
||
// 官服流程:先识别并点击顶号或切号的后一次“进入游戏”弹窗按钮
|
||
if (!IsBili)
|
||
{
|
||
var extraEnterGameBtn = content.CaptureRectArea.Find(_assets.ChooseEnterGameRo);
|
||
if (!extraEnterGameBtn.IsEmpty())
|
||
{
|
||
extraEnterGameBtn.Click();
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 点击进入游戏按钮
|
||
var ra = content.CaptureRectArea.Find(_assets.EnterGameRo);
|
||
|
||
if (!ra.IsEmpty())
|
||
{
|
||
TaskContext.Instance().PostMessageSimulator.LeftButtonClickBackground();
|
||
biliLoginClicked = true;
|
||
return;
|
||
}
|
||
|
||
// 只有在"进入游戏"按钮未出现时,才进行B服登录处理
|
||
if (IsBili && !biliLoginClicked)
|
||
{
|
||
// B服流程:处理登录窗口
|
||
var process = Process.GetProcessesByName("YuanShen").FirstOrDefault();
|
||
var (loginWindow, windowType) = GetBiliLoginWindow(process);
|
||
|
||
if (process != null && loginWindow != IntPtr.Zero)
|
||
{
|
||
var dpiScale = TaskContext.Instance().DpiScale;
|
||
if (windowType.Contains("协议"))
|
||
{
|
||
GameCaptureRegion.GameRegion1080PPosClick(960 + 70 * dpiScale, 540 + 75 * dpiScale);
|
||
}
|
||
|
||
if (windowType.Contains("登录"))
|
||
{
|
||
Thread.Sleep(2000);
|
||
GameCaptureRegion.GameRegion1080PPosClick(960, 540 + 90 * dpiScale);
|
||
Thread.Sleep(2000);
|
||
|
||
// 检查窗口是否还存在
|
||
var (remainingWindow, remainingType) = GetBiliLoginWindow(process);
|
||
if (remainingWindow == IntPtr.Zero)
|
||
{
|
||
_logger.LogInformation("B服登录完成,准备进入游戏");
|
||
// 添加延时确保窗口完全消失
|
||
Thread.Sleep(2000);
|
||
// 点击屏幕尝试找回焦点
|
||
TaskContext.Instance().PostMessageSimulator.LeftButtonClickBackground();
|
||
biliLoginClicked = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (Bv.IsInBlessingOfTheWelkinMoon(content.CaptureRectArea))
|
||
{
|
||
GameCaptureRegion.GameRegion1080PPosMove(100, 100);
|
||
TaskContext.Instance().PostMessageSimulator.LeftButtonClickBackground();
|
||
Debug.WriteLine("[GameLoading] Click blessing of the welkin moon");
|
||
// TaskControl.Logger.LogInformation("自动点击月卡");
|
||
return;
|
||
}
|
||
|
||
// 原石
|
||
var ysRa = content.CaptureRectArea.Find(ElementAssets.Instance.PrimogemRo);
|
||
if (!ysRa.IsEmpty())
|
||
{
|
||
GameCaptureRegion.GameRegion1080PPosMove(100, 100);
|
||
TaskContext.Instance().PostMessageSimulator.LeftButtonClickBackground();
|
||
Debug.WriteLine("[GameLoading] 跳过原石");
|
||
return;
|
||
}
|
||
}
|
||
|
||
// B服登录窗口检测
|
||
private static (IntPtr windowHandle, string windowType) GetBiliLoginWindow(Process process)
|
||
{
|
||
IntPtr bHWnd = IntPtr.Zero;
|
||
string windowType = "";
|
||
|
||
User32.EnumWindows((hWnd, lParam) =>
|
||
{
|
||
try
|
||
{
|
||
// 获取窗口标题
|
||
int titleLength = User32.GetWindowTextLength(hWnd);
|
||
if (titleLength > 0)
|
||
{
|
||
StringBuilder title = new StringBuilder(titleLength + 1);
|
||
User32.GetWindowText(hWnd, title, title.Capacity);
|
||
|
||
string titleText = title.ToString();
|
||
|
||
// 检查是否是B服登录窗口(通过标题匹配)
|
||
if (titleText.Contains("bilibili", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
// 检查窗口所有者是否是原神进程
|
||
var owner = User32.GetWindow(hWnd, User32.GetWindowCmd.GW_OWNER);
|
||
if (owner != IntPtr.Zero)
|
||
{
|
||
User32.GetWindowThreadProcessId(owner, out uint ownerPid);
|
||
if (ownerPid == process.Id)
|
||
{
|
||
// 检查窗口是否可见和启用
|
||
bool isVisible = User32.IsWindowVisible(hWnd);
|
||
bool isEnabled = User32.IsWindowEnabled(hWnd);
|
||
|
||
// 检查协议窗口
|
||
if (titleText.Contains("协议", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
if (isEnabled)
|
||
{
|
||
bHWnd = hWnd.DangerousGetHandle();
|
||
windowType = "协议";
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// 检查登录窗口
|
||
if (titleText.Contains("登录", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
if (isEnabled)
|
||
{
|
||
bHWnd = hWnd.DangerousGetHandle();
|
||
windowType = "登录";
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogDebug($"枚举窗口时出错: {ex.Message}");
|
||
}
|
||
|
||
return true;
|
||
}, IntPtr.Zero);
|
||
|
||
return (bHWnd, windowType);
|
||
}
|
||
}; |