123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588 |
- using Microsoft.Extensions.Logging.Console;
- using MySql.Data.MySqlClient;
- using SinMaiLauncher.ChildProcessHolder;
- using SinMaiLauncher.Interops;
- using SinMaiLauncher.Models;
- namespace SinMaiLauncher;
- public partial class MainForm : Form
- {
- private ILogger logger;
- private readonly IEnumerator<string> txtAni = TextAnimation.LoadingTextSeq(8).GetEnumerator();
- private SettingReader setting;
- private Dictionary<ChildProcessKind, ChildProcessControlGroup> childProcessControlGroups;
- private string mainControlStatusText = "摸鱼中";
- private bool mainControlStatusTextAnimation = false;
- public MainForm()
- {
- InitializeComponent();
- }
- private async void MainForm_Shown(object sender, EventArgs e)
- {
- TopMost = false;
- TopMost = true;
- tcMain.SelectedTab = tpConsoles;
- ConsoleInterops.CurrentProcess.ConsoleAllocate();
- ConsoleInterops.CurrentProcess.ConsoleDisableQuickEdit();
- ConsoleInterops.CurrentProcess.ConsoleWindowSetParent(tpConsoleLauncher);
- ConsoleInterops.CurrentProcess.ConsoleWindowFill(tpConsoleLauncher);
- tpConsoleLauncher.Resize += delegate { ConsoleInterops.CurrentProcess.ConsoleWindowFill(tpConsoleLauncher); };
- //start host application
- var builder = Host.CreateDefaultBuilder();
- builder.ConfigureServices((ctx, services) =>
- {
- //控制台日志格式
- services.AddLogging(opt =>
- {
- opt.AddSimpleConsole(p =>
- {
- p.TimestampFormat = "[dd HH:mm:ss] ";
- p.SingleLine = true;
- p.ColorBehavior = LoggerColorBehavior.Enabled;
- })
- .AddDebug()
- //.AddProvider(loggingIntoEventBus)
- ;
- services.AddSingleton(opt);
- });
- services.AddSingleton(this);
- });
- var host = builder.Build();
- var services = host.Services;
- host.Start();
- logger = services.GetRequiredService<ILogger<MainForm>>();
- var conf = services.GetRequiredService<IConfiguration>();
- var settingModel = new SinMaiLauncherSettingModel();
- conf.GetSection("SinMaiLauncher").Bind(settingModel);
- setting = new(settingModel);
- childProcessControlGroups = new Dictionary<ChildProcessKind, ChildProcessControlGroup>
- {
- {
- ChildProcessKind.MariaDb, new ChildProcessControlGroup
- {
- StateBag = new ProcessStateBagMariaDb(services.GetRequiredService<ILogger<ProcessStateBagMariaDb>>(), setting),
- ConsoleTab = tpConsoleMaria, PidLabel = lblPidMaria, StatusLabel = lblSubControlMaria,
- StartButton = btnSubControlMariaStart, StopButton = btnSubControlMariaStop
- }
- },
- {
- ChildProcessKind.AquaDx, new ChildProcessControlGroup
- {
- StateBag = new ProcessStateBagAquaDx(services.GetRequiredService<ILogger<ProcessStateBagAquaDx>>(), setting),
- Dep = ChildProcessKind.MariaDb,
- ConsoleTab = tpConsoleAquaDx, PidLabel = lblPidAquaDx, StatusLabel = lblSubControlAquaDx,
- StartButton = btnSubControlAquaDxStart, StopButton = btnSubControlAquaDxStop
- }
- },
- {
- ChildProcessKind.Injector, new ChildProcessControlGroup
- {
- StateBag = new ChildProcessStateBagForConsole(ChildProcessKind.Injector, setting.Injector),
- Dep = ChildProcessKind.AquaDx,
- ConsoleTab = tpConsoleInjector, PidLabel = lblPidInjector, StatusLabel = lblSubControlInjector,
- StartButton = btnSubControlInjectorStart, StopButton = btnSubControlInjectorStop
- }
- },
- {
- ChildProcessKind.SinMai, new ChildProcessControlGroup
- {
- StateBag = new ChildProcessStateBagForWin(ChildProcessKind.SinMai, setting.SinMai),
- Dep = ChildProcessKind.Injector,
- ConsoleTab = null, PidLabel = lblPidSinMai, StatusLabel = lblSubControlSinMai,
- StartButton = btnSubControlSinMaiStart, StopButton = btnSubControlSinMaiStop
- }
- }
- };
- foreach (var gr in childProcessControlGroups.Values)
- {
- var tab = gr.ConsoleTab;
- var cpb = gr.StateBag;
- var btnStart = gr.StartButton;
- var btnStop = gr.StopButton;
- var lblPid = gr.PidLabel;
- var lblStatus = gr.StatusLabel;
- //绑定tab控制台窗口填充
- if (cpb is ChildProcessStateBagForConsole con)
- {
- tab.Resize += delegate
- {
- if (con.HWndConsole.HasValue) WindowInterops.FillWindow(con.HWndConsole.Value, tab);
- };
- }
- btnStart.Click += async delegate
- {
- if (cpb.IsAlive)
- {
- MessageBox.Show("它在运行了,别重复点启动啊");
- return;
- }
- if (gr.Dep.HasValue && childProcessControlGroups[gr.Dep.Value].StateBag.Status != ChildProcessStatus.Ready)
- {
- MessageBox.Show("依赖的进程未就绪,启动不了的,等就绪了再启动。要按顺序启动。");
- return;
- }
- await cpb.StartAsync();
- };
- btnStop.Click += async delegate
- {
- if (cpb.IsAlive == false)
- {
- MessageBox.Show("都没启动,咋停止啊?");
- return;
- }
- await cpb.StopAsync(TimeSpan.FromSeconds(5));
- };
- cpb.StatusUpdated += async delegate
- {
- void UpdateUi()
- {
- lblPid.Text = cpb.Pid.HasValue ? cpb.Pid.Value.ToString() : "-";
- lblStatus.Text = cpb.Status switch
- {
- ChildProcessStatus.NoLaunched => "未启动",
- ChildProcessStatus.PreLaunching => "准备启动",
- ChildProcessStatus.MissingWorkingDir => "丢失工作目录",
- ChildProcessStatus.MissingExeFile => "丢失exe文件",
- ChildProcessStatus.Launching => "正在启动",
- ChildProcessStatus.WaitingReady => "等待就绪...",
- ChildProcessStatus.Ready => "就绪",
- ChildProcessStatus.Stopping => "正在停止",
- ChildProcessStatus.Stopped => "已停止",
- _ => "?"
- };
- if (cpb.Status == ChildProcessStatus.WaitingReady && cpb is ChildProcessStateBagForConsole con && cpb.IsAlive && cpb.Pid.HasValue && con.HWndConsole.HasValue)
- {
- if (tab != null)
- {
- //console 收纳
- Program.DisableQuickEdit(cpb.Pid.Value);
- WindowInterops.RemoveBorderVisible(con.HWndConsole.Value);
- WindowInterops.SetParent(con.HWndConsole.Value, tab.Handle);
- WindowInterops.FillWindow(con.HWndConsole.Value, tab);
- Height++;
- Height--;
- }
- }
- if (cpb.Status == ChildProcessStatus.Ready && cpb is ChildProcessStateBagForWin win)
- {
- //chkExplorerAuto
- }
- }
- if (InvokeRequired) await InvokeAsync(UpdateUi);
- else UpdateUi();
- };
- }
- logger.LogInformation("Started");
- Application.DoEvents();
- await Task.Delay(1000);
- tcMain.Enabled = true;
- //#if DEBUG
- // tcMain.SelectedTab = tpSubControls;
- //#else
- tcMain.SelectedTab = tpMainControl;
- //#endif
- }
- private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
- {
- if (
- childProcessControlGroups[ChildProcessKind.MariaDb].StateBag.Status == ChildProcessStatus.Ready
- || childProcessControlGroups[ChildProcessKind.AquaDx].StateBag.Status == ChildProcessStatus.Ready
- )
- {
- e.Cancel = MessageBox.Show($"基础设施进程仍在运行。{Environment.NewLine}为了避免进度数据裂开,请确保安全停止再关闭。{Environment.NewLine}要强制关闭吗?", "关闭启动器", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.No;
- }
- }
- private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
- {
- //Kill all child process
- childProcessControlGroups[ChildProcessKind.SinMai].StateBag.StopAsync(TimeSpan.FromSeconds(10)).Wait(TimeSpan.FromSeconds(10));
- childProcessControlGroups[ChildProcessKind.Injector].StateBag.StopAsync(TimeSpan.FromSeconds(10)).Wait(TimeSpan.FromSeconds(10));
- childProcessControlGroups[ChildProcessKind.AquaDx].StateBag.StopAsync(TimeSpan.FromSeconds(10)).Wait(TimeSpan.FromSeconds(10));
- childProcessControlGroups[ChildProcessKind.MariaDb].StateBag.StopAsync(TimeSpan.FromSeconds(10)).Wait(TimeSpan.FromSeconds(10));
- }
- private void tcConsoles_SelectedIndexChanged(object sender, EventArgs e)
- {
- Height++;
- Height--;
- }
- private class ChildProcessControlGroup
- {
- public ChildProcessStateBag StateBag { get; set; }
- public ChildProcessKind? Dep { get; set; }
- public TabPage? ConsoleTab { get; set; } // 可空,窗口进程为 null
- public Label PidLabel { get; set; }
- public Label StatusLabel { get; set; }
- public Button StartButton { get; set; }
- public Button StopButton { get; set; }
- }
- private async void btnMainControlStart_Click(object sender, EventArgs e)
- {
- btnMainControlStart.Enabled = false;
- mainControlStatusTextAnimation = true;
- var orderedProcesses = new List<(ChildProcessKind kind, TimeSpan timeout)>
- {
- (ChildProcessKind.MariaDb, TimeSpan.FromSeconds(30)),
- (ChildProcessKind.AquaDx,TimeSpan.FromSeconds(90)),
- (ChildProcessKind.Injector,TimeSpan.FromSeconds(10)),
- (ChildProcessKind.SinMai,TimeSpan.FromSeconds(10)),
- };
- var startedProcesses = new List<ChildProcessKind>();
- try
- {
- foreach (var (kind, timeout) in orderedProcesses)
- {
- var gr = childProcessControlGroups[kind];
- var cpb = gr.StateBag;
- if (cpb.IsAlive)
- {
- logger.LogWarning("{Kind} is already running, skipping.", kind);
- startedProcesses.Add(kind); // 已运行的也记录,便于回滚
- continue;
- }
- mainControlStatusText = $"正在启动 {kind}";
- await cpb.StartAsync();
- await WaitForReadyAsync(cpb, timeout);
- if (cpb.Status != ChildProcessStatus.Ready)
- {
- logger.LogError($"{kind} failed to reach Ready state (current: {cpb.Status}), rolling back.");
- await RollbackAsync(startedProcesses);
- return;
- }
- startedProcesses.Add(kind);
- }
- mainControlStatusText = $"已启动!";
- btnMainControlStop.Enabled = true;
- mainControlStatusTextAnimation = false;
- }
- catch (Exception exception)
- {
- logger.LogError(exception, "Error during startup, rolling back.");
- await RollbackAsync(startedProcesses);
- }
- async Task WaitForReadyAsync(ChildProcessStateBag cpb, TimeSpan timeout)
- {
- if (cpb.Status == ChildProcessStatus.Ready
- || cpb.Status == ChildProcessStatus.MissingWorkingDir
- || cpb.Status == ChildProcessStatus.MissingExeFile
- || cpb.Status == ChildProcessStatus.Stopped) return;
- var tcs = new TaskCompletionSource<bool>();
- EventHandler<EventArgs> handler = null;
- handler = delegate
- {
- if (cpb.Status == ChildProcessStatus.Ready
- || cpb.Status == ChildProcessStatus.MissingWorkingDir
- || cpb.Status == ChildProcessStatus.MissingExeFile
- || cpb.Status == ChildProcessStatus.Stopped
- )
- {
- cpb.StatusUpdated -= handler;
- tcs.TrySetResult(true);
- }
- };
- cpb.StatusUpdated += handler;
- try
- {
- // 等待 Ready 或超时
- await Task.WhenAny(tcs.Task, Task.Delay(timeout));
- if (!tcs.Task.IsCompleted)
- {
- cpb.StatusUpdated -= handler;
- throw new TimeoutException($"{cpb.Kind} did not reach Ready state within {timeout.TotalSeconds} seconds.");
- }
- }
- finally
- {
- cpb.StatusUpdated -= handler; // 确保清理
- }
- }
- async Task RollbackAsync(List<ChildProcessKind> startedProcesses)
- {
- // 按相反顺序停止
- foreach (var kind in startedProcesses.AsEnumerable().Reverse())
- {
- mainControlStatusText = $"正在停止 {kind}";
- var group = childProcessControlGroups[kind];
- var cpb = group.StateBag;
- if (cpb.IsAlive)
- {
- logger.LogInformation("Stopping {Kind} during rollback...", kind);
- await InvokeAsync(() => group.StatusLabel.Text = "正在停止...");
- await cpb.StopAsync(TimeSpan.FromSeconds(5));
- }
- }
- mainControlStatusText = "启动失败,已回滚到启动之前";
- btnMainControlStart.Enabled = true;
- mainControlStatusTextAnimation = false;
- }
- }
- private async void btnMainControlStop_Click(object sender, EventArgs e)
- {
- var orderedProcesses = new List<(ChildProcessKind kind, TimeSpan timeout)>
- {
- (ChildProcessKind.SinMai,TimeSpan.FromSeconds(5)),
- (ChildProcessKind.Injector,TimeSpan.FromSeconds(10)),
- (ChildProcessKind.AquaDx,TimeSpan.FromSeconds(90)),
- (ChildProcessKind.MariaDb, TimeSpan.FromSeconds(30)),
- };
- foreach (var (kind, timeout) in orderedProcesses)
- {
- mainControlStatusText = $"正在停止 {kind}";
- var gr = childProcessControlGroups[kind];
- if (gr.StateBag.IsAlive)
- {
- await gr.StateBag.StopAsync(timeout);
- }
- }
- btnMainControlStart.Enabled = true;
- btnMainControlStop.Enabled = false;
- mainControlStatusText = $"已停止";
- mainControlStatusTextAnimation = false;
- }
- private DateTime lastCheckSinMaiMainWinPos = DateTime.Now;
- private DateTime lastCheckSysVolume = DateTime.Now;
- private void tmrMain_Tick(object sender, EventArgs e)
- {
- if (mainControlStatusTextAnimation)
- {
- txtAni.MoveNext();
- lblMainControlStatus.Text = $"{mainControlStatusText} {txtAni.Current}";
- }
- else
- {
- lblMainControlStatus.Text = $"{mainControlStatusText}";
- }
- //窗口调整
- if (chkWinTrickAuto.Checked && lastCheckSinMaiMainWinPos < DateTime.Now.AddSeconds(-5))
- {
- lastCheckSinMaiMainWinPos = DateTime.Now;
- var pb = (ChildProcessStateBagForWin)childProcessControlGroups[ChildProcessKind.SinMai].StateBag;
- if (pb.Status == ChildProcessStatus.Ready && pb.HWndMainWindow.HasValue)
- {
- MoveSinMaiMainWindow();
- }
- }
- //显示系统音量
- if (lastCheckSysVolume < DateTime.Now.AddSeconds(-1))
- {
- lastCheckSinMaiMainWinPos = DateTime.Now;
- var sv = SystemAudioVolumeInterop.GetVolume();
- lblSysVolume.Text = sv.HasValue ? $"{sv:N2}%" : "-";
- }
- }
- private void btnAimeWrite_Click(object sender, EventArgs e)
- {
- var input = cboAimeCard.Text;
- if (string.IsNullOrEmpty(input))
- {
- MessageBox.Show("倒是输入aime卡号啊?");
- return;
- }
- if (Directory.Exists(setting.AimeFleSinMaiDir) == false)
- {
- MessageBox.Show("找不到写aime卡文件的目录");
- return;
- }
- if (Directory.Exists(setting.AimeFleSinMaiPath))
- {
- MessageBox.Show("找不到写aime卡的文件");
- return;
- }
- var aime = input.Replace("-", "");
- File.WriteAllText(setting.AimeFleSinMaiPath, aime);
- MessageBox.Show($"已将选定的卡号已写入 aime.txt !{Environment.NewLine}在游戏登录时按回车键读卡{Environment.NewLine}(按一次回车不行就多按几次回车键直到读卡成功🙄)");
- }
- private void btnWinTrick_Click(object sender, EventArgs e)
- {
- var pb = (ChildProcessStateBagForWin)childProcessControlGroups[ChildProcessKind.SinMai].StateBag;
- if (pb.Status != ChildProcessStatus.Ready || !pb.HWndMainWindow.HasValue)
- {
- MessageBox.Show("游戏启动之后再点这个按钮");
- return;
- }
- var moveWindowResult = MoveSinMaiMainWindow();
- switch (moveWindowResult)
- {
- case WindowInterops.MoveWindowResult.FailGet:
- MessageBox.Show("操作不成功:读取失败");
- break;
- case WindowInterops.MoveWindowResult.FailSet:
- MessageBox.Show("操作不成功:设置失败");
- break;
- case WindowInterops.MoveWindowResult.Success:
- MessageBox.Show("操作成功");
- break;
- case WindowInterops.MoveWindowResult.NoChange:
- MessageBox.Show("没有改变");
- break;
- }
- }
- private WindowInterops.MoveWindowResult MoveSinMaiMainWindow()
- {
- var pb = (ChildProcessStateBagForWin)childProcessControlGroups[ChildProcessKind.SinMai].StateBag;
- //WS_CLIPSIBLINGS WS_POPUPWINDOW WS_VISIBLE
- var rc = setting.Settings.AutoRect;
- return WindowInterops.MoveWindow(pb.HWndMainWindow.Value, rc.Left, rc.Top, rc.Width, rc.Height);
- }
- private void btnAimeRefresh_Click(object sender, EventArgs e)
- {
- if (childProcessControlGroups[ChildProcessKind.MariaDb].StateBag.Status != ChildProcessStatus.Ready)
- {
- MessageBox.Show("启动数据库先啊!");
- return;
- }
- var conf = setting.Settings.Infra.MariaDb;
- var connectionString = $"Server={conf.Host};Port={conf.Port};Uid={conf.Usr};Pwd={conf.Pwd};";
- using var conn = new MySqlConnection(connectionString);
- conn.Open();
- using var cmd = conn.CreateCommand();
- cmd.CommandText = "SELECT `luid` FROM `aqua`.`sega_card`";
- using var dr = cmd.ExecuteReader();
- cboAimeCard.Items.Clear();
- while (dr.Read())
- {
- var aimeR = dr["luid"].ToString();
- var aimeH = string.Join("-",
- aimeR.Substring(0, 4),
- aimeR.Substring(4, 4),
- aimeR.Substring(8, 4),
- aimeR.Substring(12, 4),
- aimeR.Substring(16, 4));
- cboAimeCard.Items.Add(aimeH);
- }
- MessageBox.Show($"从数据库中读取到 {cboAimeCard.Items.Count} 个卡号已载入下拉列表");
- }
- private void btnExplorerKill_Click(object sender, EventArgs e)
- {
- MessageBox.Show("还没做!");
- }
- private void btnExplorerRestore_Click(object sender, EventArgs e)
- {
- MessageBox.Show("还没做!");
- }
- private void btnProcessLoopBackVirtualNic_Click(object sender, EventArgs e)
- {
- MessageBox.Show("还没做!");
- }
- private void btnAppDataOverwrite_Click(object sender, EventArgs e)
- {
- MessageBox.Show("还没做!");
- }
- private void btnSysVolumeDec_Click(object sender, EventArgs e)
- {
- var sv = SystemAudioVolumeInterop.GetVolume();
- if (!sv.HasValue)
- {
- MessageBox.Show("无法获取当前音量");
- return;
- }
- sv -= 5;
- if (sv < 0) sv = 0;
- if (SystemAudioVolumeInterop.SetVolume(sv.Value) == false) MessageBox.Show("音量调整失败");
- }
- private void btnSysVolumeInc_Click(object sender, EventArgs e)
- {
- var sv = SystemAudioVolumeInterop.GetVolume();
- if (!sv.HasValue)
- {
- MessageBox.Show("无法获取当前音量");
- return;
- }
- sv += 5;
- if (sv > 100) sv = 100;
- if (SystemAudioVolumeInterop.SetVolume(sv.Value) == false) MessageBox.Show("音量调整失败");
- }
- }
|