Skip to content

Commit bcc5d45

Browse files
committed
重写插件模块以支持热重载
1 parent 576e364 commit bcc5d45

10 files changed

+140
-44
lines changed

MultiSEngine/Config.cs

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public static Config Load()
4141
public string ListenIP { get; set; } = "0.0.0.0";
4242
public int ListenPort { get; set; } = 7778;
4343
public string ServerName { get; set; } = "MultiSEngine";
44+
public int ServerVersion { get; set; } = 243;
4445
public string Token { get; set; } = "114514";
4546
public int SwitchTimeOut { get; set; } = 5000;
4647
public bool EnableChatForward { get; set; } = true;

MultiSEngine/Core/Adapter/FakeWorldAdapter.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ public override bool GetPacket(Packet packet)
8484
if (!Hooks.OnPlayerJoin(Client, Client.IP, Client.Port, hello.Version, out var joinEvent))
8585
{
8686
Client.ReadVersion(joinEvent.Version);
87-
if (Client.Player.VersionNum != Data.TRVersion)
88-
Client.Disconnect(Localization.Instance["Prompt_NoAvailableServer", joinEvent.Version]);
87+
if (Client.Player.VersionNum != Config.Instance.ServerVersion)
88+
Client.Disconnect(Localization.Instance["Prompt_VersionNotAllowed", joinEvent.Version]);
8989
else
9090
InternalSendPacket(new LoadPlayer() { PlayerSlot = 0, ServerWantsToRunCheckBytesInClientLoopThread = true });
9191
}

MultiSEngine/Core/Adapter/VisualPlayerAdapter.cs

+3
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ public override bool GetPacket(Packet packet)
7878
Player.UpdateData(playerInfo, false);
7979
return true;
8080
case WorldData worldData:
81+
#if DEBUG
82+
Client.SendInfoMessage($"SSC: {worldData.EventInfo1[6]}");
83+
#endif
8184
Player.UpdateData(worldData, false);
8285
if (Callback != null)
8386
{

MultiSEngine/Core/Command.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public static bool HandleCommand(ClientData client, string text, out bool contin
8686
}
8787
catch (Exception ex)
8888
{
89-
Logs.Info($"An exception occurred while executing the command: {command.Name}{Environment.NewLine}{ex.Message}");
89+
Logs.Info($"An exception occurred while executing the command: {command.Name}{Environment.NewLine}{ex}");
9090
client.SendErrorMessage(Localization.Get("Prompt_CommandFailed"));
9191
}
9292
return true;

MultiSEngine/Core/PluginSystem.cs

+103-32
Original file line numberDiff line numberDiff line change
@@ -4,59 +4,130 @@
44
using System.IO;
55
using System.Linq;
66
using System.Reflection;
7+
using System.Runtime.CompilerServices;
8+
using System.Runtime.Loader;
79
using TrProtocol;
810

911
namespace MultiSEngine.Core
1012
{
11-
public abstract class MSEPlugin
13+
public interface IMSEPlugin
1214
{
13-
public abstract string Name { get; }
14-
public abstract string Description { get; }
15-
public abstract string Author { get; }
16-
public abstract Version Version { get; }
17-
public abstract void Initialize();
18-
public abstract void Dispose();
19-
public virtual PacketSerializer Serializer => Net.ClientSerializer;
15+
public string Name { get; }
16+
public string Description { get; }
17+
public string Author { get; }
18+
public Version Version { get; }
19+
public void Initialize();
20+
public void Dispose();
21+
public PacketSerializer Serializer => Net.ClientSerializer;
2022
}
2123
public class PluginSystem
2224
{
2325
public static readonly string PluginPath = Path.Combine(Environment.CurrentDirectory, "Plugins");
24-
public static readonly List<MSEPlugin> PluginList = new();
25-
[AutoInit("Loading all plugins.", $"Plugin(s) loaded.")]
26+
public static readonly List<IMSEPlugin> PluginList = new();
27+
private static PluginHost<IMSEPlugin> _pluginHost;
28+
[AutoInit("Loading all plugins.")]
2629
internal static void Load()
2730
{
2831
if (!Directory.Exists(PluginPath))
2932
Directory.CreateDirectory(PluginPath);
30-
Directory.GetFiles(PluginPath, "*.dll").ForEach(p =>
31-
{
32-
Assembly plugin = Assembly.LoadFile(p);
33-
if (plugin.GetTypes().Where(t => t.BaseType == typeof(MSEPlugin))?.ToArray() is { Length: > 0 } instances)
34-
{
35-
instances.ForEach(instance =>
36-
{
37-
try
38-
{
39-
var pluginInstance = Activator.CreateInstance(instance) as MSEPlugin;
40-
pluginInstance.Initialize();
41-
Logs.Success($"- Loaded plugin: {pluginInstance.Name} <{pluginInstance.Author}> V{pluginInstance.Version}");
42-
PluginList.Add(pluginInstance);
43-
}
44-
catch (Exception ex)
45-
{
46-
Logs.Warn($"Failed to load plugin: {p}{Environment.NewLine}{ex}");
47-
}
48-
});
49-
}
50-
});
33+
_pluginHost ??= new PluginHost<IMSEPlugin>();
34+
_pluginHost.LoadPlugins(PluginPath, plugin => Logs.Success($"- Loaded plugin: {plugin.Name} <{plugin.Author}> V{plugin.Version}"));
35+
36+
Logs.Info($"{PluginList.Count} Plugin(s) loaded.");
5137
}
5238
internal static void Unload()
5339
{
5440
PluginList.ForEach(p =>
5541
{
5642
p.Dispose();
57-
Logs.Text($"Plugin: {p.Name} disposed.");
43+
Logs.Info($"- Disposed plugin: {p.Name}");
5844
});
5945
PluginList.Clear();
46+
_pluginHost.Unload();
47+
_pluginHost = null;
48+
}
49+
public static void Reload()
50+
{
51+
Unload();
52+
Load();
53+
}
54+
#region 插件加载类
55+
class PluginHost<TPlugin> where TPlugin : IMSEPlugin
56+
{
57+
private AssemblyLoadContext _pluginAssemblyLoadingContext;
58+
59+
public PluginHost()
60+
{
61+
_pluginAssemblyLoadingContext = new AssemblyLoadContext("PluginAssemblyContext", isCollectible: true);
62+
}
63+
public static void RegisterPlugin(TPlugin plugin, Action<IMSEPlugin> registerCallback = null)
64+
{
65+
if (PluginList.Contains(plugin))
66+
Logs.Warn($"Plugin:{plugin.Name} already loaded.");
67+
else
68+
{
69+
try
70+
{
71+
plugin.Initialize();
72+
PluginList.Add(plugin);
73+
registerCallback?.Invoke(plugin);
74+
}
75+
catch (Exception ex)
76+
{
77+
Logs.Warn($"Failed to initialize plugin: {plugin.Name}{Environment.NewLine}{ex}");
78+
}
79+
}
80+
}
81+
public void LoadPlugins(string pluginPath, Action<IMSEPlugin> registerCallback = null)
82+
{
83+
LoadPlugins(FindAssemliesWithPlugins(pluginPath), registerCallback);
84+
}
85+
public void LoadPlugins(IReadOnlyCollection<string> assembliesWithPlugins, Action<IMSEPlugin> registerCallback = null)
86+
{
87+
foreach (var assemblyPath in assembliesWithPlugins)
88+
{
89+
var assembly = _pluginAssemblyLoadingContext.LoadFromAssemblyPath(assemblyPath);
90+
var validPluginTypes = GetPluginTypes(assembly);
91+
foreach (var pluginType in validPluginTypes)
92+
{
93+
var pluginInstance = (TPlugin)Activator.CreateInstance(pluginType);
94+
RegisterPlugin(pluginInstance, registerCallback);
95+
}
96+
}
97+
}
98+
/// <summary>
99+
/// 寻找存在指定类型的程序集路径
100+
/// </summary>
101+
/// <param name="path">路径</param>
102+
/// <returns></returns>
103+
public static IReadOnlyCollection<string> FindAssemliesWithPlugins(string path)
104+
{
105+
var assemblies = Directory.GetFiles(path, "*.dll");
106+
var assemblyPluginInfos = new List<string>();
107+
var pluginFinderAssemblyContext = new AssemblyLoadContext(name: "PluginFinderAssemblyContext", isCollectible: true);
108+
foreach (var assemblyPath in assemblies)
109+
{
110+
var assembly = pluginFinderAssemblyContext.LoadFromAssemblyPath(assemblyPath);
111+
if (GetPluginTypes(assembly).Any())
112+
assemblyPluginInfos.Add(assembly.Location);
113+
}
114+
pluginFinderAssemblyContext.Unload();
115+
return assemblyPluginInfos;
116+
}
117+
public static IReadOnlyCollection<Type> GetPluginTypes(Assembly assembly)
118+
{
119+
return assembly.GetTypes()
120+
.Where(type =>
121+
!type.IsAbstract &&
122+
typeof(TPlugin).IsAssignableFrom(type))
123+
.ToArray();
124+
}
125+
public void Unload()
126+
{
127+
_pluginAssemblyLoadingContext.Unload();
128+
_pluginAssemblyLoadingContext = null;
129+
}
60130
}
131+
#endregion
61132
}
62133
}

MultiSEngine/Modules/ClientHelper.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ public static void ReadVersion(this ClientData client, string version)
218218
client.Player.VersionNum = version.StartsWith("Terraria") && int.TryParse(version[8..], out var v)
219219
? v
220220
: Config.Instance.DefaultServerInternal.VersionNum;
221-
Logs.Info($"Version num of {client.Name} is {client.Player.VersionNum}.");
221+
Logs.Info($"Version of {client.Name} is {Data.Convert(client.Player.VersionNum)}.");
222222
}
223223
public static bool HandleCommand(this ClientData client, string cmd)
224224
{

MultiSEngine/Modules/Cmds/ConsoleCommand.cs

+7-2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ public override bool Execute(ClientData client, string cmdName, string[] parma)
3939
Localization._instance = null;
4040
Logs.Success("Successfully reloaded.");
4141
break;
42+
case "reloadplugin":
43+
case "rp":
44+
Core.PluginSystem.Reload();
45+
break;
4246
case "broadcast":
4347
case "bc":
4448
if (parma.Length > 1)
@@ -56,10 +60,11 @@ public override bool Execute(ClientData client, string cmdName, string[] parma)
5660
Logs.Info($"Avaliable console commands:{Environment.NewLine}" +
5761
$"- stop(exit){Environment.NewLine}" +
5862
$"- kick <Player name> (reason){Environment.NewLine}" +
59-
$"- bc <message> (target server){Environment.NewLine}" +
63+
$"- broadcase(bc) <message> (target server){Environment.NewLine}" +
6064
$"- list{Environment.NewLine}" +
6165
$"- online{Environment.NewLine}" +
62-
$"- reload{Environment.NewLine}", false);
66+
$"- reload{Environment.NewLine}" +
67+
$"- reloadplugin(rp){Environment.NewLine}", false);
6368
break;
6469
}
6570
Data.Commands.FirstOrDefault(c => c.Name == "mce")?.Execute(client, cmdName, parma);

MultiSEngine/Modules/Cmds/InternalCommand.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public override bool Execute(ClientData client, string cmdName, string[] parma)
5656
break;
5757
#if DEBUG
5858
case "let":
59-
if (parma.Count < 3)
59+
if (parma.Length < 3)
6060
Console.Write("error /mse let name server");
6161
else
6262
Data.Clients.FirstOrDefault(c => c.Name.ToLower().StartsWith(parma[1].ToLower()))?.Join(Utils.GetServerInfoByName(parma[2]).FirstOrDefault());

MultiSEngine/Modules/Data.cs

+19-3
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77

88
namespace MultiSEngine.Modules
99
{
10-
internal class Data
10+
public class Data
1111
{
12-
public const int TRVersion = 242;
13-
public static readonly List<DataStruct.ClientData> Clients = new();
12+
public static readonly List<ClientData> Clients = new();
1413
public static readonly List<CmdBase> Commands = new();
1514
internal static byte[] StaticSpawnSquareData { get; set; }
1615
private static string _motd = string.Empty;
@@ -20,6 +19,23 @@ internal class Data
2019
.Replace("{players}", string.Join(", ", Clients.Select(c => c.Name)))
2120
.Replace("{servers}", string.Join(", ", Config.Instance.Servers.Select(s => s.Name)));
2221
public static string MotdPath => Path.Combine(Environment.CurrentDirectory, "MOTD.txt");
22+
public static string Convert(int version)
23+
{
24+
string protocol = $"Terraria{version}";
25+
return protocol switch
26+
{
27+
"Terraria230" => "v1.4.0.5",
28+
"Terraria233" => "v1.4.1.1",
29+
"Terraria234" => "v1.4.1.2",
30+
"Terraria235" => "v1.4.2",
31+
"Terraria236" => "v1.4.2.1",
32+
"Terraria237" => "v1.4.2.2",
33+
"Terraria238" => "v1.4.2.3",
34+
"Terraria242" => "v1.4.3",
35+
"Terraria243" => "v1.4.3.1",
36+
_ => "Unknown",
37+
};
38+
}
2339
[AutoInit]
2440
public static void Init()
2541
{

MultiSEngine/MultiSEngine.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
<LangVersion>preview</LangVersion>
55
<OutputType>Exe</OutputType>
66
<TargetFramework>net6.0</TargetFramework>
7-
<AssemblyVersion>1.0.5</AssemblyVersion>
8-
<FileVersion>1.0.5</FileVersion>
7+
<AssemblyVersion>1.0.5.2</AssemblyVersion>
8+
<FileVersion>1.0.5.2</FileVersion>
99
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
1010
<AnalysisLevel>latest</AnalysisLevel>
1111
</PropertyGroup>

0 commit comments

Comments
 (0)