using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; using RainOpsMini.Helpers; using RainOpsMini.Models; using System.Xml.Linq; using System.Text.Encodings.Web; using System.Text.Unicode; using System.Reflection; namespace RainOpsMini.Plugins { /// /// 插件管理器,负责加载、保存和管理插件配置 /// public class PluginManager { private readonly IEnumerable _plugins; private readonly string _configDirectory; /// /// 构造函数,初始化插件管理器 /// /// 注入的插件集合 public PluginManager(IEnumerable plugins) { _plugins = plugins; _configDirectory = Path.Combine(Environment.CurrentDirectory, "PluginConfig"); // 确保配置目录存在 if (!Directory.Exists(_configDirectory)) { Directory.CreateDirectory(_configDirectory); } // 启动时加载所有插件配置 LoadConfigs(); } /// /// 获取所有插件的信息,包括名称、描述、启用状态和配置 /// /// 包含插件信息的匿名对象集合 public IEnumerable GetPlugins() { return _plugins.Select(p => new { p.Name, p.Description, p.Category, CategoryName = GetCategoryName(p.Category), p.IsEnabled, Config = p.GetConfig(), // 获取配置属性的中文描述 Descriptions = GetConfigDescriptions(p.GetConfig().GetType()) }); } private static string GetCategoryName(PluginCategory category) { return category switch { PluginCategory.BasicFunction => "基础功能", PluginCategory.PointsFunction => "积分功能", PluginCategory.JointBan => "联合封禁", PluginCategory.CombatBuff => "战斗BUFF", PluginCategory.AdvancedFunction => "高级功能", _ => "未知分类" }; } /// /// 根据名称获取特定插件实例 /// /// 插件名称(不区分大小写) /// 插件实例,如果未找到则返回 null public IPlugin? GetPlugin(string name) { return _plugins.FirstOrDefault(p => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); } /// /// 更新指定插件的配置并保存到文件 /// /// 插件名称 /// 新的配置 JSON 对象 public void UpdateConfig(string name, JsonElement config) { var plugin = GetPlugin(name); if (plugin != null) { plugin.UpdateConfig(config); SaveConfig(plugin); } } /// /// 重置指定插件的配置为默认值并保存 /// /// 插件名称 public void ResetConfig(string name) { var plugin = GetPlugin(name); if (plugin != null) { plugin.ResetConfig(); SaveConfig(plugin); } } /// /// 加载所有插件的配置文件 /// private void LoadConfigs() { foreach (var plugin in _plugins) { try { string filePath = Path.Combine(_configDirectory, $"{plugin.GetType().Name}.json"); if (File.Exists(filePath)) { // 如果配置文件存在,读取并应用 string json = File.ReadAllText(filePath); var doc = JsonDocument.Parse(json); plugin.UpdateConfig(doc.RootElement); } else { // 如果配置文件不存在,保存默认配置 SaveConfig(plugin); } } catch (Exception ex) { RainOpsLog.Log($"[PluginManager] 加载配置失败 {plugin.Name}: {ex.Message}"); } } } /// /// 将插件配置保存到 JSON 文件 /// /// 要保存配置的插件实例 private void SaveConfig(IPlugin plugin) { try { var options = new JsonSerializerOptions { WriteIndented = true, // 确保中文不被转义 Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) }; string json = JsonSerializer.Serialize(plugin.GetConfig(), options); string filePath = Path.Combine(_configDirectory, $"{plugin.GetType().Name}.json"); File.WriteAllText(filePath, json, System.Text.Encoding.UTF8); } catch (Exception ex) { RainOpsLog.Log($"[PluginManager] 保存配置失败 {plugin.Name}: {ex.Message}"); } } /// /// 从 XML 文档文件中获取配置属性的描述信息 /// /// 配置类的类型 /// 属性名与描述的键值对字典 private Dictionary GetConfigDescriptions(Type configType) { var descriptions = new Dictionary(); // 尝试多个可能的路径寻找 XML 文档 string[] possiblePaths = new[] { Path.Combine(Environment.CurrentDirectory, "RainOpsMini.xml"), Path.Combine(Directory.GetCurrentDirectory(), "RainOpsMini.xml"), Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "net6.0", "win-x64", "RainOpsMini.xml"), Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "net6.0", "RainOpsMini.xml") }; string xmlPath = possiblePaths.FirstOrDefault(File.Exists); if (xmlPath == null) { RainOpsLog.Log($"[PluginManager] Warning: RainOpsMini.xml not found in any expected location. BaseDir: {Environment.CurrentDirectory}, CurrentDir: {Directory.GetCurrentDirectory()}"); return descriptions; } try { var doc = XDocument.Load(xmlPath); // 获取配置类及其引用的类型的描述 var typesToProcess = new HashSet { configType }; var processedTypes = new HashSet(); while (typesToProcess.Count > 0) { var currentType = typesToProcess.First(); typesToProcess.Remove(currentType); if (processedTypes.Contains(currentType)) continue; processedTypes.Add(currentType); foreach (var prop in currentType.GetProperties()) { // 构建 Member ID: P:Namespace.Class.Property string typeFullName = currentType.FullName?.Replace("+", ".") ?? currentType.Name; string memberId = $"P:{typeFullName}.{prop.Name}"; var summary = doc.Descendants("member") .FirstOrDefault(m => m.Attribute("name")?.Value == memberId) ?.Element("summary") ?.Value .Trim(); if (!string.IsNullOrEmpty(summary)) { // 同时存储原始名称和 CamelCase 名称以最大化兼容性 descriptions[prop.Name] = summary; string camelKey = JsonNamingPolicy.CamelCase.ConvertName(prop.Name); if (camelKey != prop.Name) { descriptions[camelKey] = summary; } } // 检查是否需要处理属性的类型(如果是自定义类或 List 中的类) var propType = prop.PropertyType; if (propType.IsGenericType && (propType.GetGenericTypeDefinition() == typeof(List<>) || propType.GetGenericTypeDefinition() == typeof(IEnumerable<>))) { var itemType = propType.GetGenericArguments()[0]; // 尝试获取 List 元素类型的类注释 string itemTypeFullName = itemType.FullName?.Replace("+", ".") ?? itemType.Name; string itemTypeId = $"T:{itemTypeFullName}"; var itemTypeSummary = doc.Descendants("member") .FirstOrDefault(m => m.Attribute("name")?.Value == itemTypeId) ?.Element("summary") ?.Value .Trim(); if (!string.IsNullOrEmpty(itemTypeSummary)) { descriptions[$"{prop.Name}$Item"] = itemTypeSummary; string camelKey = JsonNamingPolicy.CamelCase.ConvertName(prop.Name); if (camelKey != prop.Name) { descriptions[$"{camelKey}$Item"] = itemTypeSummary; } } propType = itemType; } if (propType.IsClass && propType != typeof(string) && !processedTypes.Contains(propType) && propType.Namespace != null && propType.Namespace.StartsWith("RainOpsMini")) { typesToProcess.Add(propType); } } } } catch (Exception ex) { RainOpsLog.Log($"[PluginManager] 读取 XML 文档失败: {ex.Message}"); } return descriptions; } } }