using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Threading; using AnotherFileBrowser.Windows; using BITKit.Mod; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.UIElements; namespace BITKit.UX { public class UXModService : MonoBehaviour { [SerializeField] private UIDocument document; [SerializeField] 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 OnEnable() { ModService.OnModInstalled+=OnModInstalled; ModService.OnModUnInstalled+=OnModUnInstalled; ModService.OnModLoaded+=OnModLoaded; ModService.OnModUnLoaded+=OnModUnLoaded; ModService.OnLocked+=OnLocked; } private void OnDisable() { ModService.OnModInstalled-=OnModInstalled; ModService.OnModUnInstalled-=OnModUnInstalled; ModService.OnModLoaded-=OnModLoaded; ModService.OnModUnLoaded-=OnModUnLoaded; ModService.OnLocked-=OnLocked; } private void OnLocked(bool obj) { document.rootVisualElement.SetEnabled(!obj); } private void Start() { 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(); if (destroyCancellationToken.IsCancellationRequested) return; await UniTask.SwitchToMainThread(); if (destroyCancellationToken.IsCancellationRequested) return; reloadModButton.SetEnabled(true); }; } private async void OnModUnInstalled(IMod obj) { await UniTask.SwitchToMainThread(); if (destroyCancellationToken.IsCancellationRequested) return; _modContainers.TryRemove(obj.Name, out var container); container.visualElement.RemoveFromHierarchy(); } private async void OnModInstalled(IMod obj) { await UniTask.SwitchToMainThread(); if (destroyCancellationToken.IsCancellationRequested) return; _modContainers.GetOrAdd(obj.Name,_=> Create(obj)); } private async void OnModUnLoaded(IMod obj) { await UniTask.SwitchToMainThread(); if (destroyCancellationToken.IsCancellationRequested) return; var container = _modContainers.GetOrAdd(obj.Name,_=> Create(obj)); container.toggle.SetValueWithoutNotify(false); } private async void OnModLoaded(IMod obj) { await UniTask.SwitchToMainThread(); if (destroyCancellationToken.IsCancellationRequested) return; var container = _modContainers.GetOrAdd(obj.Name,_=> Create(obj)); container.toggle.SetValueWithoutNotify(true); } private static void OpenMod() { BIT4Log.Log("正在打开选择文件对话框"); #if UNITY_EDITOR new Thread(OpenModInternal).Start(); #else OpenModInternal(); #endif return; void OpenModInternal() { BIT4Log.Log("已进入文件对话框线程"); new FileBrowser().OpenFileBrowser(new BrowserProperties() { //filterIndex = 0, //filter = "C Sharp files (*.cs)|*.cs |Dll files (*.dll)|*.dll", }, Filepath); return; async void Filepath(string path) { BIT4Log.Log("已选择文件:"+path); await BITApp.SwitchToMainThread(); try { var file = new FileInfo(path); switch (file.Extension) { case ".cs": var code = await File.ReadAllTextAsync(path); var assembly = BITSharp.Compile(code); await ModService.Load(assembly,Path.GetDirectoryName(path)); 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 UXContainer Create(IMod mod) { var container = new UXContainer(_modsContainer.Create(modTemplate)) { titleLabel = { text = mod.Name }, descriptionLabel = { text = mod.Description } }; container.toggle.RegisterValueChangedCallback(evt => { if (evt.newValue) { ModService.Load(mod); } else { ModService.UnLoad(mod); } }); container.visualElement.tooltip = mod.Name+"\n"+mod.Description; container.button.clicked += () => { _modNameLabel.text = mod.Name; _modDescriptionLabel.text = mod.Description; }; return container; } } }