BITKit/Src/Core/Applation/BITApp.cs

351 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using Cysharp.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
// ReSharper disable StringLiteralTypo
#if NET5_0_OR_GREATER
using Microsoft.Extensions.DependencyInjection;
#endif
namespace BITKit
{
public class AppIsNotPlayingException : Exception
{
public override string Message => "Application Is Not Playing";
}
// ReSharper disable once InconsistentNaming
public class BITApp
{
public static async UniTask SwitchToMainThread()
{
#if UNITY_5_3_OR_NEWER
await UniTask.SwitchToMainThread();
#else
if (SynchronizationContext is null)
{
return;
}
await UniTask.SwitchToSynchronizationContext(SynchronizationContext);
#endif
}
public static class Time
{
public static float ElapsedTime { get; set; }
public static float DeltaTime { get; set; }
public static double TimeAsDouble { get; set; }
}
[System.Serializable]
public record AppSettings
{
public bool AllowInitialize = true;
public List<string> whiteList = new();
public List<string> blackList = new()
{
"System",
"Unity",
"UnityEngine",
"Microsoft",
"Godot",
"Internal",
"GodotPlugins",
// ReSharper disable once StringLiteralTypo
"Cysharp",
"kcp2k",
"Swashbuckle",
"Newtonsoft",
"Lightbug",
"NiceIO",
"AndroidPlayerBuildProgram",
"Steamworks",
"AYellowpaper",
"Utilities",
"mattatz",
"AmazingAssets",
"MeshCombineStudio",
"kcp2k",
"AYellowpaper",
"MudBlazor",
"TextCopy",
"Blazored",
"TextCopy",
"mattatz",
"TrailsFX",
"Knife",
"Needle",
"NiceIO",
"AndroidPlayerBuildProgram",
"DocCodeExamples",
"System",
"UnityEngine",
"Unity",
"Microsoft",
"UnityEditor",
"Google",
"Mono",
"ZXing",
"ImmersiveVRTools",
"MonKey",
"FLib",
"Kcp",
"Udx",
"Sirenix",
"TMPro",
"RotaryHeart",
"Cinemachine",
"ParadoxNotion",
"Net",
"VSCodeEditor",
"AOT",
"UnityEditorInternal",
"UnityEngineInternal",
"JetBrains",
"Bee",
"NotInvited",
"HighlightPlus",
"DG",
"Hierarchy2",
"Cysharp",
"JetBrains",
"Packages",
"Newtonsoft_X",
"Binding",
"NodeCanvas",
"SaveDuringPlay",
"LimWorks",
"MagicaCloth2",
"FastScriptReload",
"ParrelSync",
"KinematicCharacterController",
"LimWorksEditor",
"BuildComponent",
"dnlib",
"BigIntegerLibrary",
"Ionic",
"log4net",
"ImmersiveVrToolsCommon",
"NUnit",
"HarmonyLib",
"MonoMod",
"WebDav",
"PlasticGui",
"Codice",
"GluonGui",
"PlasticPipe",
"XDiffGui",
"MacFsWatcher",
"MacUI",
"PlayerBuildProgramLibrary",
"ExCSS",
"ScriptCompilationBuildProgram",
"BeeBuildProgramCommon",
"Accessibility",
"CodiceApp",
"Newtonsoft",
"MergetoolGui",
"TreeEditor",
"MackySoft",
"FullscreenEditor",
"mattatz",
"AYellowpaper",
"kcp2k",
"MeshCombineStudio",
"AmazingAssets",
"Utilities",
"Pinwheel",
"TinyScript",
"YooAsset",
"Timeline.Samples",
"FAE",
"MK",
"Lzf",
"MySql",
};
}
/// <summary>
/// 依赖服务集合
/// </summary>
public static ServiceCollection ServiceCollection { get; set; } = new();
/// <summary>
/// 依赖服务提供接口
/// </summary>
public static ServiceProvider ServiceProvider
{
get=>_serviceProvider??=ServiceCollection.BuildServiceProvider();
set => _serviceProvider = value;
}
private static ServiceProvider _serviceProvider;
/// <summary>
/// 主线程
/// </summary>
public static SynchronizationContext SynchronizationContext { get; set; } =
SynchronizationContext.Current;
[System.Serializable]
public class OpenPath : IDisposable
{
public string path;
public void Dispose()
{
path = null;
}
public void Execute()
{
//path = string.IsNullOrEmpty(path) ? Environment.CurrentDirectory : path.Replace(@"/", @"\");
var path = Environment.ExpandEnvironmentVariables(this.path);
path = string.IsNullOrEmpty(path) ? Environment.CurrentDirectory : System.IO.Path.Combine(Environment.CurrentDirectory, path);
BIT4Log.Log<OpenPath>($"正在打开文件夹:{path}");
try
{
var process = Process.Start("explorer.exe", path);
SwitchToThisWindow(process.MainWindowHandle, true);
}
catch (System.Exception e)
{
BIT4Log.LogException(e);
}
}
}
[System.Serializable]
public class OpenAPP : IDisposable
{
public string path;
public string WorkingDirectory;
public void Dispose()
{
path = WorkingDirectory = null;
}
public void Execute()
{
if (File.Exists(path))
{
ProcessStartInfo startInfo = new(path)
{
UseShellExecute = true
};
if (string.IsNullOrEmpty(WorkingDirectory) is false)
{
startInfo.WorkingDirectory = WorkingDirectory;
}
var process = Process.Start(startInfo);
SwitchToThisWindow(process.MainWindowHandle, true);
}
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
public static string AppName { get; private set; } = nameof(BITApp);
private static CancellationTokenSource CancellationTokenSource = new();
public static CancellationToken CancellationToken => CancellationTokenSource.Token;
public static InitializationState State;
public static Assembly[] Assemblies;
public static AppSettings Settings { get; protected set; }
private static DateTime InitialTime { get; set; }=DateTime.Now;
public static async UniTask Start(string appName = nameof(BITApp),AppSettings settings=default)
{
Time.TimeAsDouble = 0;
Time.DeltaTime = 1 / 60f;
SynchronizationContext = (SynchronizationContext, SynchronizationContext.Current) switch
{
(null, not null) => SynchronizationContext.Current,
_ => SynchronizationContext
};
if (SynchronizationContext is null)
{
BIT4Log.Warning<BITApp>("未找到主线程上下文,等待主线程将不可用");
}
Settings = settings??new AppSettings();
CancellationTokenSource = new CancellationTokenSource();
AppName = appName;
InitialTime = DateTime.Now;
await Init();
}
private static async Task Init()
{
try
{
if (State is not InitializationState.None)
{
BIT4Log.Warning("警告:上次启动可能未正确退出");
return;
}
//加载app-settings.json
var appSettingsPath = Path.Combine(Environment.CurrentDirectory, "appsettings.json");
if (PathHelper.TryGetText(appSettingsPath,out var json))
{
DataParser.Set(json);
BIT4Log.Log<BITApp>("已加载全局appsettings");
}
//内部反射初始化
await UniTask.SwitchToThreadPool();
Stopwatch stopwatch = new();
stopwatch.Start();
Stopwatch reflectionHelperWatch = new();
reflectionHelperWatch.Start();
await ReflectionHelper.Init();
reflectionHelperWatch.Stop();
Stopwatch commandWatch = new();
await BITCommands.InitializeAsync();
commandWatch.Stop();
Stopwatch binaryWatch = new();
await BITBinary.Start();
binaryWatch.Stop();
stopwatch.Stop();
State = InitializationState.Initialized;
BIT4Log.Log<BITApp>($"已完成初始化,耗时:{stopwatch.ElapsedMilliseconds}ms");
BIT4Log.Log<BITApp>($"反射初始化耗时:{reflectionHelperWatch.ElapsedMilliseconds}ms");
BIT4Log.Log<BITApp>($"初始化二进制序列化耗时:{binaryWatch.ElapsedMilliseconds}ms");
}
catch (System.Exception e)
{
BIT4Log.Warning<BITApp>($"{nameof(BITApp)}初始化错误:");
BIT4Log.LogException(e);
}
#if NET5_0_OR_GREATER
ServiceProvider = ServiceCollection.BuildServiceProvider();
#endif
}
public static void Stop()
{
var runTime = DateTime.Now - InitialTime;
BIT4Log.Log<BITApp>($"正在停止{nameof(BITApp)}");
CancellationTokenSource.Cancel();
State = InitializationState.None;
BITCommands.Dispose();
BIT4Log.Log<BITApp>($"已停止{nameof(BITApp)}");
BIT4Log.Log<BITApp>($"已运行时间:{runTime.ToString("hh\\mm\\ss")}");
BIT4Log.Log<BITApp>("Exit Code:0");
}
public static void Run(string path, string WorkingDirectory = "")
{
using var open = new OpenAPP() { path = path, WorkingDirectory = WorkingDirectory };
open.Execute();
}
public static void Open(string path)
{
using var open = new OpenPath() { path = path };
open.Execute();
}
}
}