Files
2026-08-04 00:10:33 +08:00

377 lines
15 KiB
C#

using System;
using System.IO;
using System.Linq;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using BarrageGrab.Modles.JsonEntity;
using Newtonsoft.Json.Linq;
using Jint.Runtime;
namespace BarrageGrab
{
public class Program
{
static FormView mainForm = null;
static bool exited = false;
static bool formExited = false;
static WinApi.ControlCtrlDelegate controlCtr = ControlCtrlHandle;
static Mutex mutex = new Mutex(false, "DyBarrageGrab");
// 插件所在目录(用于解析相对路径)
public static string BaseDir => AppDomain.CurrentDomain.BaseDirectory;
// === 启动项 (支持相对路径,相对于插件所在目录) ===
public static bool AutoLaunchGame = true;
public static string GameExePath = @"..\Fantasy.Map.Simulator\FantasyMapSimulator.exe";
public static string GameWorkDir = @"..\Fantasy.Map.Simulator";
// === 配置表路径 (支持相对路径) ===
public static string ConfigDir = @"..\Fantasy.Map.Simulator\BepInEx\config";
public static string EffectFile = "effect_config.txt";
public static string MacroFile = "macro_config.txt";
public static string GiftFile = "gift_config.txt";
public static string CommandFile = "command_config.txt";
public static string FkeyFile = "fkey_config.txt";
/// <summary>将相对路径解析为绝对路径(基于插件所在目录)。已是绝对路径则原样返回。</summary>
public static string ResolveFullPath(string path)
{
if (string.IsNullOrWhiteSpace(path)) return path;
return Path.IsPathRooted(path) ? path : Path.GetFullPath(Path.Combine(BaseDir, path));
}
// 解析后的绝对路径(只读)
public static string GameExeFullPath => ResolveFullPath(GameExePath);
public static string GameWorkFullPath => ResolveFullPath(GameWorkDir);
public static string ConfigDirFullPath => ResolveFullPath(ConfigDir);
// 配置表完整路径(只读,基于 ConfigDirFullPath)
public static string EffectPath => Path.Combine(ConfigDirFullPath, EffectFile);
public static string MacroPath => Path.Combine(ConfigDirFullPath, MacroFile);
public static string GiftPath => Path.Combine(ConfigDirFullPath, GiftFile);
public static string CommandPath => Path.Combine(ConfigDirFullPath, CommandFile);
public static string FkeyPath => Path.Combine(ConfigDirFullPath, FkeyFile);
public static void SetGamePaths(string exePath, string workDir)
{
if (!string.IsNullOrWhiteSpace(exePath)) GameExePath = exePath;
if (!string.IsNullOrWhiteSpace(workDir)) GameWorkDir = workDir;
}
public static void SetConfigPaths(string configDir,
string effectFile, string macroFile, string giftFile, string cmdFile, string fkeyFile)
{
if (!string.IsNullOrWhiteSpace(configDir)) ConfigDir = configDir;
if (!string.IsNullOrWhiteSpace(effectFile)) EffectFile = effectFile;
if (!string.IsNullOrWhiteSpace(macroFile)) MacroFile = macroFile;
if (!string.IsNullOrWhiteSpace(giftFile)) GiftFile = giftFile;
if (!string.IsNullOrWhiteSpace(cmdFile)) CommandFile = cmdFile;
if (!string.IsNullOrWhiteSpace(fkeyFile)) FkeyFile = fkeyFile;
}
/// <summary>从 GameExeFullPath 提取进程名(不带扩展名)</summary>
static string GameProcessName
{
get
{
try { return Path.GetFileNameWithoutExtension(GameExeFullPath); }
catch { return "FantasyMapSimulator"; }
}
}
/// <summary>关闭所有同名游戏进程,返回是否已无残留</summary>
static bool CloseGameProcesses()
{
var procName = GameProcessName;
var procs = Process.GetProcessesByName(procName);
if (procs.Length == 0)
{
Logger.PrintColor("未发现运行中的游戏进程 (" + procName + ")", ConsoleColor.DarkGray);
return true;
}
Logger.PrintColor("发现 " + procs.Length + " 个游戏进程 (" + procName + "),正在关闭...", ConsoleColor.Yellow);
int killed = 0, failed = 0;
foreach (var p in procs)
{
try
{
if (!p.HasExited)
{
try { p.CloseMainWindow(); } catch { }
if (!p.WaitForExit(2000)) { p.Kill(); p.WaitForExit(3000); }
}
killed++;
}
catch { failed++; }
finally { try { p.Close(); } catch { } }
}
Thread.Sleep(500);
var remain = Process.GetProcessesByName(procName);
if (remain.Length > 0)
{
Logger.PrintColor("仍有 " + remain.Length + " 个进程未退出,强制结束...", ConsoleColor.Yellow);
foreach (var r in remain)
{
try { if (!r.HasExited) r.Kill(); } catch { }
finally { try { r.Close(); } catch { } }
}
Thread.Sleep(1000);
}
bool allClosed = Process.GetProcessesByName(procName).Length == 0;
Logger.PrintColor("关闭完成 (成功" + killed + ", 失败" + failed + ")", allClosed ? ConsoleColor.Green : ConsoleColor.Yellow);
return allClosed;
}
public static void LaunchGame()
{
try
{
if (!File.Exists(GameExeFullPath))
{
MessageBox.Show("找不到游戏文件:\n" + GameExeFullPath + "\n(配置路径: " + GameExePath + ")", "启动失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
var existing = Process.GetProcessesByName(GameProcessName);
if (existing.Length > 0)
{
Logger.PrintColor("游戏已在运行中,跳过启动", ConsoleColor.Yellow);
foreach (var p in existing) try { p.Close(); } catch { }
return;
}
StartGameProcess();
}
catch (Exception ex)
{
Logger.LogError(ex, "启动游戏失败: " + ex.Message);
}
}
public static void RestartGame()
{
try
{
if (!File.Exists(GameExeFullPath))
{
MessageBox.Show("找不到游戏文件:\n" + GameExeFullPath + "\n(配置路径: " + GameExePath + ")", "启动失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
Logger.PrintColor("── 重启游戏:关闭进程 ──", ConsoleColor.Cyan);
bool closed = CloseGameProcesses();
if (!closed)
{
Logger.PrintColor("警告: 部分游戏进程未能完全关闭,仍尝试重新启动", ConsoleColor.Yellow);
}
Thread.Sleep(1500);
Logger.PrintColor("── 重启游戏:重新启动 ──", ConsoleColor.Cyan);
StartGameProcess();
}
catch (Exception ex)
{
Logger.LogError(ex, "重启游戏失败: " + ex.Message);
}
}
private static void StartGameProcess()
{
var psi = new ProcessStartInfo(GameExeFullPath)
{
WorkingDirectory = GameWorkFullPath,
UseShellExecute = true
};
Process.Start(psi);
Logger.PrintColor("游戏已启动", ConsoleColor.Green);
}
/// <summary>加载 game_settings.json (启动项 + 配置表路径)</summary>
private static void LoadGameSettings()
{
try
{
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "game_settings.json");
if (!File.Exists(path)) return;
var json = JObject.Parse(File.ReadAllText(path));
// 启动项
string exe = json["gameExePath"]?.Value<string>();
string dir = json["gameWorkDir"]?.Value<string>();
bool auto = json["autoLaunchGame"]?.Value<bool>() ?? true;
if (!string.IsNullOrWhiteSpace(exe)) GameExePath = exe;
if (!string.IsNullOrWhiteSpace(dir)) GameWorkDir = dir;
AutoLaunchGame = auto;
// 配置表路径
string configDir = json["configDir"]?.Value<string>();
if (!string.IsNullOrWhiteSpace(configDir)) ConfigDir = configDir;
var cfg = json["configFiles"];
if (cfg != null)
{
string eff = cfg["effect"]?.Value<string>();
string mac = cfg["macro"]?.Value<string>();
string gift = cfg["gift"]?.Value<string>();
string cmd = cfg["command"]?.Value<string>();
string fkey = cfg["fkey"]?.Value<string>();
if (!string.IsNullOrWhiteSpace(eff)) EffectFile = eff;
if (!string.IsNullOrWhiteSpace(mac)) MacroFile = mac;
if (!string.IsNullOrWhiteSpace(gift)) GiftFile = gift;
if (!string.IsNullOrWhiteSpace(cmd)) CommandFile = cmd;
if (!string.IsNullOrWhiteSpace(fkey)) FkeyFile = fkey;
}
Logger.PrintColor($"已加载游戏设置: 自动启动={AutoLaunchGame}, 配置目录={ConfigDir}", ConsoleColor.DarkGray);
}
catch (Exception ex)
{
Logger.PrintColor($"加载游戏设置失败: {ex.Message}", ConsoleColor.Yellow);
}
}
private const string WatchdogArg = "--watchdog";
private const string WatchdogPidArg = "--pid";
static void Main(string[] args)
{
if (IsWatchdogMode(args)) { RunWatchdog(args); return; }
if (!mutex.WaitOne(TimeSpan.Zero, true))
{
Console.WriteLine("另一个实例已在运行。");
Console.ReadKey();
return;
}
SetTitle("弹幕监听推送");
StartWatchdog();
LoadGameSettings();
try { Init(); }
catch (Exception ex)
{
Logger.LogError(ex, $"程序初始化错误,{ex.Message}");
MessageBox.Show(ex.Message, "程序初始化错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
exited = true;
}
while (!exited) Thread.Sleep(500);
if (!AppRuntime.WsServer.IsDisposed) AppRuntime.WsServer.Dispose();
Logger.PrintColor("服务器已关闭...");
WinApi.SetConsoleCtrlHandler(controlCtr, false);
}
private static void StartWatchdog()
{
try
{
var exePath = Application.ExecutablePath;
if (string.IsNullOrWhiteSpace(exePath) || !File.Exists(exePath)) return;
var pid = Process.GetCurrentProcess().Id;
var args = string.Format("{0} {1} {2}", WatchdogArg, WatchdogPidArg, pid);
var psi = new ProcessStartInfo(exePath, args) { UseShellExecute = false, CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden };
Process.Start(psi);
}
catch { }
}
private static bool IsWatchdogMode(string[] args)
{
return args != null && args.Any(a => string.Equals(a, WatchdogArg, StringComparison.OrdinalIgnoreCase));
}
private static int? TryGetWatchdogPid(string[] args)
{
if (args == null) return null;
for (int i = 0; i < args.Length; i++)
{
if (!string.Equals(args[i], WatchdogPidArg, StringComparison.OrdinalIgnoreCase)) continue;
if (i + 1 >= args.Length) return null;
int pid;
if (int.TryParse(args[i + 1], out pid)) return pid;
return null;
}
return null;
}
private static void RunWatchdog(string[] args)
{
try
{
var pid = TryGetWatchdogPid(args);
if (pid == null || pid <= 0) return;
Process target;
try { target = Process.GetProcessById(pid.Value); }
catch { SafeCloseSystemProxy(); return; }
for (; ; ) { if (target.HasExited) break; Thread.Sleep(1000); }
SafeCloseSystemProxy();
}
catch { try { SafeCloseSystemProxy(); } catch { } }
}
private static void SafeCloseSystemProxy()
{
try { if (!AppSetting.Current.UsedProxy) return; WinApi.CloseSystemProxy(); }
catch { }
}
private static void Init()
{
AppRuntime.Init();
LiveCompanHelper.SwitchSetup();
WinApi.SetConsoleCtrlHandler(controlCtr, true);
WinApi.DisableQuickEditMode();
AppRuntime.DisplayConsole(!AppSetting.Current.HideConsole);
AppRuntime.WsServer.Grab.Proxy.SetUpstreamProxy(AppSetting.Current.UpstreamProxy);
AppRuntime.WsServer.OnClose += (s, e) => { exited = true; };
if (!AppSetting.Current.ComPort.IsNullOrWhiteSpace())
AppRuntime.ComPortServer.OpenStart();
if (AppSetting.Current.ShowWindow)
{
var uiThread = new Thread(new ThreadStart(() =>
{
mainForm = new FormView();
System.Windows.Forms.Application.Run(mainForm);
formExited = true;
AppRuntime.WsServer.Dispose();
}));
uiThread.SetApartmentState(ApartmentState.STA);
uiThread.IsBackground = true;
uiThread.Start();
}
AppRuntime.WsServer.StartListen();
Logger.PrintColor($"{AppRuntime.WsServer.ServerLocation} 弹幕服务已启动", ConsoleColor.Green);
if (AutoLaunchGame) LaunchGame();
Version version = System.Reflection.Assembly.GetAssembly(typeof(Program)).GetName().Version;
SetTitle($"弹幕监听推送 v{version} [{AppRuntime.WsServer.ServerLocation}]");
}
private static void SetTitle(string title)
{
if (WinApi.GetConsoleWindow() != IntPtr.Zero) Console.Title = title;
}
private static bool ControlCtrlHandle(int CtrlType)
{
switch (CtrlType)
{
case 0: break;
case 2:
Logger.PrintColor("2工具被强制关闭");
AppRuntime.WsServer.Dispose();
break;
}
return false;
}
}
}