using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Threading; #if UNITY_5_3_OR_NEWER && UNITY_WINDOW using AnotherFileBrowser.Windows; #endif using BITKit.Mod; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.UIElements; namespace BITKit.UX { public class UXModService:UIToolKitPanel,IDisposable { protected override string DocumentPath => "ux_mod_Service"; private const string TemplatePath = "ux_mod_service_template"; public override bool AllowCursor => true; public UXModService(IUXService uxService) : base(uxService) { ModService.OnModInstalled+=OnModInstalled; ModService.OnModUnInstalled+=OnModUnInstalled; ModService.OnModLoaded+=OnModLoaded; ModService.OnModUnLoaded+=OnModUnLoaded; ModService.OnLocked+=OnLocked; } private VisualTreeAsset _modTemplate; [UXBindPath("open-mod-button")] private Button _openModButton; [UXBindPath("mods-container")] private VisualElement _modsContainer; [UXBindPath("return-button")] private Button _returnButton; [UXBindPath("mod-name-label")] private Label _modNameLabel; [UXBindPath("mod-description-label")] private Label _modDescriptionLabel; [UXBindPath("reload-mod-button",true)] private Button _reloadModButton; private readonly ConcurrentDictionary _modContainers=new(); private void OnLocked(bool obj) { RootVisualElement?.SetEnabled(!obj); } public override async UniTask EntryAsync() { await base.EntryAsync(); _modTemplate =await ModService.LoadAsset(TemplatePath); UXUtils.Inject(this); if (_openModButton is not null) { _openModButton.clicked += OpenMod; } if (_returnButton is not null) { _returnButton.clicked += UXService.Return; } _modsContainer.Clear(); foreach (var x in ModService.Mods) { OnModInstalled(x); } if (_reloadModButton is not null) _reloadModButton.clicked += async () => { _reloadModButton.SetEnabled(false); await ModService.Reload(); await UniTask.SwitchToMainThread(); _reloadModButton.SetEnabled(true); }; } private async void OnModUnInstalled(IMod obj) { await UniTask.SwitchToMainThread(); _modContainers.TryRemove(obj.Name, out var container); container.RemoveFromHierarchy(); } private async void OnModInstalled(IMod obj) { await UniTask.SwitchToMainThread(); var container = _modContainers.GetOrAdd(obj.Name,_=> Create(obj)); container.RegisterCallback(x => { if (x.button != 1) return; ContextMenuBuilder.Create().BuildAction("卸载", () => { ModService.UnLoad(obj); ModService.UnInstall(obj); }).Build(); }); } private async void OnModUnLoaded(IMod obj) { await UniTask.SwitchToMainThread(); //var container = _modContainers.GetOrAdd(obj.Name,_=> Create(obj)); if(_modContainers.TryGetValue(obj.Name,out var container)) { container.Get().SetValueWithoutNotify(false); } } private async void OnModLoaded(IMod obj) { await UniTask.SwitchToMainThread(); var container = _modContainers.GetOrAdd(obj.Name,_=> Create(obj)); container.Get().SetValueWithoutNotify(true); } private static void OpenMod() { BIT4Log.Log("正在打开选择文件对话框"); #if UNITY_EDITOR new Thread(OpenModInternal).Start(); #else OpenModInternal(); #endif return; void OpenModInternal() { #if UNITY_5_3_OR_NEWER && UNITY_WINDOW BIT4Log.Log("已进入文件对话框线程"); new FileBrowser().OpenFileBrowser(new BrowserProperties() { //filterIndex = 0, //filter = "C Sharp files (*.cs)|*.cs |Dll files (*.dll)|*.dll", }, Filepath); #else throw new NotSupportedException($"{Application.platform} 不支持打开文件对话框"); #endif return; async void Filepath(string path) { BIT4Log.Log("已选择文件:"+path); await BITApp.SwitchToMainThread(); try { var file = new FileInfo(path); switch (file.Extension) { case ".cs": #if UNITY_5_3_OR_NEWER var code = await File.ReadAllTextAsync(path); var assembly = BITSharp.Compile(code); await ModService.Load(assembly,Path.GetDirectoryName(path)); #else throw new NotSupportedException($"{Application.platform} 不支持编译C#代码"); #endif break; case ".dll": var bytes = await File.ReadAllBytesAsync(path); await ModService.Load(Assembly.Load(bytes),Path.GetDirectoryName(path)); break; case ".json" when file.Name is ModPackage.DefaultFileName: await ModService.LoadFromPackage(path); break; default: Alert.Print(new AlertMessage() { title = "加载失败", message = "不支持的文件类型" }); break; } } catch (Exception e) { Alert.Print(new AlertMessage() { title = "加载失败", message = e.Message }); BIT4Log.LogException(e); } } } } private VisualElement Create(IMod mod) { var container =_modsContainer.Create(_modTemplate); container.Get().RegisterValueChangedCallback(evt => { if (evt.newValue) { ModService.Load(mod); } else { ModService.UnLoad(mod); } }); container.tooltip = mod.Name+"\n"+mod.Description; container.Get