Skip to content

Commit 31a3798

Browse files
committed
refactor(managed): Change export method
1 parent ff9b839 commit 31a3798

File tree

3 files changed

+241
-10
lines changed

3 files changed

+241
-10
lines changed

managed/managed.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
4747
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
4848
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
49+
<PackageReference Include="Mono.Cecil" Version="0.11.6" />
4950
<PackageReference Include="MySqlConnector" Version="2.4.0" />
5051
<PackageReference Include="Npgsql" Version="9.0.3" />
5152
<PackageReference Include="Spectre.Console" Version="0.51.1" />
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
using Mono.Cecil;
2+
using Microsoft.Extensions.Logging;
3+
4+
namespace SwiftlyS2.Core.Modules.Plugins;
5+
6+
internal class DependencyResolver
7+
{
8+
private readonly ILogger _logger;
9+
private readonly Dictionary<string, List<string>> _dependencyGraph = new();
10+
private readonly Dictionary<string, string> _pluginPaths = new();
11+
12+
public DependencyResolver(ILogger logger)
13+
{
14+
_logger = logger;
15+
}
16+
17+
public void AnalyzeDependencies(IEnumerable<string> pluginDirectories)
18+
{
19+
_dependencyGraph.Clear();
20+
_pluginPaths.Clear();
21+
22+
var exportAssemblies = new Dictionary<string, string>(); // assemblyName -> path
23+
24+
foreach (var pluginDir in pluginDirectories)
25+
{
26+
var exportDir = Path.Combine(pluginDir, "resources", "exports");
27+
if (Directory.Exists(exportDir))
28+
{
29+
var exportFiles = Directory.GetFiles(exportDir, "*.dll");
30+
foreach (var exportFile in exportFiles)
31+
{
32+
try
33+
{
34+
using var assembly = AssemblyDefinition.ReadAssembly(exportFile);
35+
var assemblyName = assembly.Name.Name;
36+
exportAssemblies[assemblyName] = exportFile;
37+
_pluginPaths[assemblyName] = exportFile;
38+
39+
if (!_dependencyGraph.ContainsKey(assemblyName))
40+
{
41+
_dependencyGraph[assemblyName] = new List<string>();
42+
}
43+
44+
_logger.LogDebug($"Found export assembly: {assemblyName} at {exportFile}");
45+
}
46+
catch (Exception ex)
47+
{
48+
_logger.LogWarning(ex, $"Failed to read assembly {exportFile}");
49+
}
50+
}
51+
}
52+
}
53+
54+
foreach (var (assemblyName, assemblyPath) in exportAssemblies)
55+
{
56+
try
57+
{
58+
using var assembly = AssemblyDefinition.ReadAssembly(assemblyPath);
59+
var dependencies = new List<string>();
60+
61+
foreach (var reference in assembly.MainModule.AssemblyReferences)
62+
{
63+
var refName = reference.Name;
64+
65+
if (exportAssemblies.ContainsKey(refName))
66+
{
67+
dependencies.Add(refName);
68+
_logger.LogDebug($"{assemblyName} depends on {refName}");
69+
}
70+
}
71+
72+
_dependencyGraph[assemblyName] = dependencies;
73+
}
74+
catch (Exception ex)
75+
{
76+
_logger.LogWarning(ex, $"Failed to analyze dependencies for {assemblyName}");
77+
}
78+
}
79+
}
80+
81+
public List<string> GetLoadOrder()
82+
{
83+
var result = new List<string>();
84+
var visited = new HashSet<string>();
85+
var visiting = new HashSet<string>();
86+
87+
foreach (var assembly in _dependencyGraph.Keys)
88+
{
89+
if (!visited.Contains(assembly))
90+
{
91+
TopologicalSort(assembly, visited, visiting, result);
92+
}
93+
}
94+
95+
return result.Select(name => _pluginPaths[name]).ToList();
96+
}
97+
98+
private void TopologicalSort(
99+
string assembly,
100+
HashSet<string> visited,
101+
HashSet<string> visiting,
102+
List<string> result)
103+
{
104+
if (visiting.Contains(assembly))
105+
{
106+
var cycle = BuildCyclePath(assembly, visiting);
107+
throw new InvalidOperationException(
108+
$"Circular dependency detected: {cycle}");
109+
}
110+
111+
if (visited.Contains(assembly))
112+
{
113+
return;
114+
}
115+
116+
visiting.Add(assembly);
117+
118+
if (_dependencyGraph.TryGetValue(assembly, out var dependencies))
119+
{
120+
foreach (var dependency in dependencies)
121+
{
122+
TopologicalSort(dependency, visited, visiting, result);
123+
}
124+
}
125+
126+
visiting.Remove(assembly);
127+
visited.Add(assembly);
128+
result.Add(assembly);
129+
}
130+
private string BuildCyclePath(string start, HashSet<string> visiting)
131+
{
132+
var path = new List<string> { start };
133+
var current = start;
134+
135+
if (_dependencyGraph.TryGetValue(current, out var deps))
136+
{
137+
foreach (var dep in deps)
138+
{
139+
if (visiting.Contains(dep))
140+
{
141+
path.Add(dep);
142+
if (dep == start)
143+
{
144+
break;
145+
}
146+
current = dep;
147+
}
148+
}
149+
}
150+
151+
return string.Join(" -> ", path);
152+
}
153+
154+
public string GetDependencyGraphVisualization()
155+
{
156+
var lines = new List<string> { "Dependency Graph:" };
157+
158+
foreach (var (assembly, dependencies) in _dependencyGraph.OrderBy(x => x.Key))
159+
{
160+
if (dependencies.Any())
161+
{
162+
lines.Add($" {assembly} -> [{string.Join(", ", dependencies)}]");
163+
}
164+
else
165+
{
166+
lines.Add($" {assembly} (no dependencies)");
167+
}
168+
}
169+
170+
return string.Join(Environment.NewLine, lines);
171+
}
172+
}
173+

managed/src/SwiftlyS2.Core/Modules/Plugins/PluginManager.cs

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ RootDirService rootDirService
4747
}
4848

4949
public void Initialize() {
50-
LoadContracts();
50+
LoadExports();
5151
LoadPlugins();
5252
}
5353

@@ -83,20 +83,77 @@ public void HandlePluginChange(object sender, FileSystemEventArgs e)
8383
}
8484
}
8585

86-
private void LoadContracts()
86+
private void LoadExports()
8787
{
8888
var pluginDirs = Directory.GetDirectories(_RootDirService.GetPluginsRoot());
8989

90-
foreach (var pluginDir in pluginDirs) {
91-
var pluginName = Path.GetFileName(pluginDir);
92-
var contractPath = Path.Combine(pluginDir, pluginName + ".Contracts.dll");
93-
if (File.Exists(contractPath)) {
94-
var assembly = Assembly.LoadFrom(Path.Combine(pluginDir, pluginName + ".Contracts.dll"));
95-
var contracts = assembly.GetTypes();
96-
foreach (var contract in contracts) {
97-
_SharedTypes.Add(contract);
90+
// 使用依赖解析器确定正确的加载顺序
91+
var resolver = new DependencyResolver(_Logger);
92+
93+
try
94+
{
95+
resolver.AnalyzeDependencies(pluginDirs);
96+
97+
_Logger.LogInformation(resolver.GetDependencyGraphVisualization());
98+
99+
var loadOrder = resolver.GetLoadOrder();
100+
101+
_Logger.LogInformation($"Loading {loadOrder.Count} export assemblies in dependency order.");
102+
103+
foreach (var exportFile in loadOrder)
104+
{
105+
try
106+
{
107+
var assembly = Assembly.LoadFrom(exportFile);
108+
var exports = assembly.GetTypes();
109+
110+
_Logger.LogDebug($"Loaded {exports.Length} types from {Path.GetFileName(exportFile)}.");
111+
112+
113+
foreach (var export in exports)
114+
{
115+
_SharedTypes.Add(export);
116+
}
117+
}
118+
catch (Exception ex)
119+
{
120+
_Logger.LogWarning(ex, $"Failed to load export assembly: {exportFile}");
98121
}
99122
}
123+
124+
_Logger.LogInformation($"Successfully loaded {_SharedTypes.Count} shared types.");
125+
}
126+
catch (InvalidOperationException ex) when (ex.Message.Contains("Circular dependency"))
127+
{
128+
_Logger.LogError(ex, "Circular dependency detected in plugin exports. Loading exports without dependency resolution.");
129+
130+
foreach (var pluginDir in pluginDirs)
131+
{
132+
if (Directory.Exists(Path.Combine(pluginDir, "resources", "exports")))
133+
{
134+
var exportFiles = Directory.GetFiles(Path.Combine(pluginDir, "resources", "exports"), "*.dll");
135+
foreach (var exportFile in exportFiles)
136+
{
137+
try
138+
{
139+
var assembly = Assembly.LoadFrom(exportFile);
140+
var exports = assembly.GetTypes();
141+
foreach (var export in exports)
142+
{
143+
_SharedTypes.Add(export);
144+
}
145+
}
146+
catch (Exception innerEx)
147+
{
148+
_Logger.LogWarning(innerEx, $"Failed to load export assembly: {exportFile}");
149+
}
150+
}
151+
}
152+
}
153+
}
154+
catch (Exception ex)
155+
{
156+
_Logger.LogError(ex, "Unexpected error during export loading");
100157
}
101158
}
102159

0 commit comments

Comments
 (0)