2024-01-23 02:56:26 +08:00
|
|
|
using System;
|
|
|
|
using System.Collections;
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.IO;
|
2024-03-04 18:45:21 +08:00
|
|
|
using System.Reflection;
|
2024-01-23 02:56:26 +08:00
|
|
|
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;
|
2024-03-04 18:45:21 +08:00
|
|
|
[UXBindPath("reload-mod-button",true)]
|
|
|
|
private Button reloadModButton;
|
2024-01-23 02:56:26 +08:00
|
|
|
|
|
|
|
private readonly ConcurrentDictionary<string,UXContainer> _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);
|
|
|
|
}
|
2024-03-04 18:45:21 +08:00
|
|
|
|
2024-01-23 02:56:26 +08:00
|
|
|
private void Start()
|
|
|
|
{
|
|
|
|
UXUtils.Inject(this);
|
|
|
|
if (_openModButton is not null)
|
|
|
|
{
|
|
|
|
_openModButton.clicked += OpenMod;
|
|
|
|
}
|
2024-03-04 18:45:21 +08:00
|
|
|
|
2024-01-23 02:56:26 +08:00
|
|
|
if (_returnButton is not null)
|
|
|
|
{
|
|
|
|
_returnButton.clicked += UXService.Return;
|
|
|
|
}
|
2024-03-04 18:45:21 +08:00
|
|
|
|
2024-01-23 02:56:26 +08:00
|
|
|
_modsContainer.Clear();
|
|
|
|
foreach (var x in ModService.Mods)
|
|
|
|
{
|
|
|
|
OnModInstalled(x);
|
|
|
|
}
|
2024-03-04 18:45:21 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
};
|
2024-01-23 02:56:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private async void OnModUnInstalled(IMod obj)
|
|
|
|
{
|
|
|
|
await UniTask.SwitchToMainThread();
|
|
|
|
if (destroyCancellationToken.IsCancellationRequested) return;
|
|
|
|
_modContainers.TryRemove(obj.Name, out var container);
|
2024-03-04 18:45:21 +08:00
|
|
|
container.visualElement.RemoveFromHierarchy();
|
2024-01-23 02:56:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
2024-03-04 18:45:21 +08:00
|
|
|
BIT4Log.Log("正在打开选择文件对话框");
|
|
|
|
#if UNITY_EDITOR
|
2024-01-23 02:56:26 +08:00
|
|
|
new Thread(OpenModInternal).Start();
|
2024-03-04 18:45:21 +08:00
|
|
|
#else
|
|
|
|
OpenModInternal();
|
|
|
|
#endif
|
|
|
|
|
2024-01-23 02:56:26 +08:00
|
|
|
return;
|
|
|
|
void OpenModInternal()
|
|
|
|
{
|
2024-03-04 18:45:21 +08:00
|
|
|
BIT4Log.Log<UXModService>("已进入文件对话框线程");
|
2024-01-23 02:56:26 +08:00
|
|
|
new FileBrowser().OpenFileBrowser(new BrowserProperties()
|
|
|
|
{
|
2024-03-04 18:45:21 +08:00
|
|
|
//filterIndex = 0,
|
|
|
|
//filter = "C Sharp files (*.cs)|*.cs |Dll files (*.dll)|*.dll",
|
2024-01-23 02:56:26 +08:00
|
|
|
}, Filepath);
|
|
|
|
return;
|
|
|
|
async void Filepath(string path)
|
|
|
|
{
|
2024-03-04 18:45:21 +08:00
|
|
|
BIT4Log.Log<UXModService>("已选择文件:"+path);
|
2024-01-23 02:56:26 +08:00
|
|
|
await BITApp.SwitchToMainThread();
|
2024-03-04 18:45:21 +08:00
|
|
|
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)
|
2024-01-23 02:56:26 +08:00
|
|
|
{
|
2024-03-04 18:45:21 +08:00
|
|
|
Alert.Print(new AlertMessage()
|
|
|
|
{
|
|
|
|
title = "加载失败",
|
|
|
|
message = e.Message
|
|
|
|
});
|
|
|
|
BIT4Log.LogException(e);
|
2024-01-23 02:56:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|