Danro-Jump/Assets/Scripts/Runtime/Bootstrap/WindowsLauncherLicenseChecker.cs
Danil Iarosh 77186697f8 Prepare v0.1 gameplay and hardware foundation
Подробные изменения:

- Расширен основной gameplay loop: джетпак, новые платформы, оборудование, враги, прогрессия сложности и ограничения взаимодействий игрока.

- Добавлены Bootstrap-сервисы запуска приложения, защита, фокус окна, управление курсором и настройки сервисов через DI.

- Добавлена COM-интеграция с технической схемой Input System, обработкой кнопок/ADC, подсветкой и консольной диагностикой.

- Добавлен отдельный debug/logging модуль с каналами, обработчиками консоли и файловым выводом.

- Добавлены asmdef, link.xml, EditMode/PlayMode тесты и обновлена дорожная карта проекта.

Проверки:

- dotnet build .\\DanroJump.Runtime.csproj --no-restore

- dotnet build .\\DanroJump.Tests.EditMode.csproj --no-restore

- dotnet build .\\DanroJump.Tests.PlayMode.csproj --no-restore
2026-06-08 14:22:48 +03:00

166 lines
5.7 KiB
C#

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using Cysharp.Threading.Tasks;
namespace DanroJump.Bootstrap
{
public sealed class WindowsLauncherLicenseChecker : IStartupLicenseChecker
{
private const uint WaitTimeout = 0x00000102;
private readonly IStartupProtectionRuntime runtime;
public WindowsLauncherLicenseChecker(IStartupProtectionRuntime runtime)
{
this.runtime = runtime ?? throw new ArgumentNullException(nameof(runtime));
}
public UniTask<StartupLicenseResult> CheckAsync(
string launcherName,
TimeSpan timeout,
CancellationToken cancellationToken)
{
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
return UniTask.RunOnThreadPool(
() => CheckOnThread(launcherName, timeout),
configureAwait: false,
cancellationToken: cancellationToken);
#else
return UniTask.FromResult(StartupLicenseResult.Failed("Windows launcher check is unavailable on this platform."));
#endif
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
private StartupLicenseResult CheckOnThread(string launcherName, TimeSpan timeout)
{
string launcherPath = FindLauncherPath(launcherName);
if (string.IsNullOrEmpty(launcherPath))
{
return StartupLicenseResult.Failed($"{launcherName} was not found.");
}
var startupInfo = new STARTUPINFO
{
cb = Marshal.SizeOf<STARTUPINFO>()
};
bool success = CreateProcess(
launcherPath,
null,
IntPtr.Zero,
IntPtr.Zero,
false,
0,
IntPtr.Zero,
Path.GetDirectoryName(launcherPath),
ref startupInfo,
out PROCESS_INFORMATION processInfo);
if (!success)
{
return StartupLicenseResult.Failed($"Failed to start {launcherName}. Win32 error: {Marshal.GetLastWin32Error()}.");
}
try
{
uint waitMs = timeout.TotalMilliseconds >= uint.MaxValue
? uint.MaxValue
: (uint)Math.Max(1d, timeout.TotalMilliseconds);
uint waitResult = WaitForSingleObject(processInfo.hProcess, waitMs);
if (waitResult == WaitTimeout)
{
return StartupLicenseResult.Failed($"{launcherName} timed out.");
}
if (!GetExitCodeProcess(processInfo.hProcess, out uint exitCode))
{
return StartupLicenseResult.Failed($"Failed to read {launcherName} exit code. Win32 error: {Marshal.GetLastWin32Error()}.");
}
return exitCode == 0
? StartupLicenseResult.Valid($"{launcherName} returned success.")
: StartupLicenseResult.Failed($"{launcherName} returned exit code {exitCode}.");
}
finally
{
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
}
private string FindLauncherPath(string launcherName)
{
string pathInBase = Path.Combine(runtime.BaseDirectory, launcherName);
if (File.Exists(pathInBase))
{
return pathInBase;
}
string pathInStreamingAssets = Path.Combine(runtime.StreamingAssetsPath, launcherName);
if (File.Exists(pathInStreamingAssets))
{
return pathInStreamingAssets;
}
return null;
}
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct STARTUPINFO
{
public int cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public int dwX;
public int dwY;
public int dwXSize;
public int dwYSize;
public int dwXCountChars;
public int dwYCountChars;
public int dwFillAttribute;
public int dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetExitCodeProcess(IntPtr hProcess, out uint lpExitCode);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
#endif
}
}