添加EntityModuleConfig帮助类;添加AddonDLL的SHA512哈希验证 (#85)

This commit is contained in:
milimoe 2024-08-24 01:17:47 +08:00 committed by GitHub
parent e07d0abb90
commit d6787de920
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 238 additions and 59 deletions

View File

@ -0,0 +1,107 @@
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Core.Api.Utility
{
/// <summary>
/// 简易的实体模组配置文件生成器<para/>
/// 仅支持继承了 <see cref="BaseEntity"/> 的实体类型,每个 <see cref="EntityModuleConfig{T}"/> 仅保存一种实体类型的数据
/// <para/>文件会保存为:程序目录/configs/<see cref="ModuleName"/>/<see cref="FileName"/>.json
/// </summary>
/// <remarks>
/// 新建一个配置文件,文件会保存为:程序目录/configs/<paramref name="module_name"/>/<paramref name="file_name"/>.json
/// </remarks>
/// <param name="module_name"></param>
/// <param name="file_name"></param>
public class EntityModuleConfig<T>(string module_name, string file_name) : Dictionary<string, T> where T : BaseEntity
{
/// <summary>
/// 模组的名称
/// </summary>
public string ModuleName { get; set; } = module_name;
/// <summary>
/// 配置文件的名称(后缀将是.json
/// </summary>
public string FileName { get; set; } = file_name;
/// <summary>
/// 使用索引器给指定key赋值不存在key会新增
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public new T this[string key]
{
get => base[key];
set
{
if (value != null) Add(key, value);
}
}
/// <summary>
/// 获取指定key的value
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public T? Get(string key)
{
if (TryGetValue(key, out T? value) && value != null)
{
return value;
}
return null;
}
/// <summary>
/// 添加一个配置如果已存在key会覆盖
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public new void Add(string key, T value)
{
if (value != null)
{
if (TryGetValue(key, out _)) base[key] = value;
else base.Add(key, value);
}
}
/// <summary>
/// 从配置文件中读取配置。
/// </summary>
public void LoadConfig()
{
string dpath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/{ModuleName}";
string fpath = $@"{dpath}/{FileName}.json";
if (Directory.Exists(dpath) && File.Exists(fpath))
{
string json = File.ReadAllText(fpath, General.DefaultEncoding);
Dictionary<string, T> dict = NetworkUtility.JsonDeserialize<Dictionary<string, T>>(json) ?? [];
Clear();
foreach (string key in dict.Keys)
{
T obj = dict[key];
base.Add(key, obj);
}
}
}
/// <summary>
/// 将配置保存到配置文件。调用此方法会覆盖原有的.json请注意备份
/// </summary>
public void SaveConfig()
{
string json = NetworkUtility.JsonSerialize((Dictionary<string, T>)this);
string dpath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/{ModuleName}";
string fpath = $@"{dpath}/{FileName}.json";
if (!Directory.Exists(dpath))
{
Directory.CreateDirectory(dpath);
}
using StreamWriter writer = new(fpath, false, General.DefaultEncoding);
writer.WriteLine(json);
writer.Flush();
}
}
}

View File

@ -42,6 +42,11 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// </summary>
public Dictionary<GameModule, GameModuleServer?> AssociatedServers { get; } = [];
/// <summary>
/// 已加载的模组DLL名称对应的路径
/// </summary>
public static Dictionary<string, string> ModuleFilePaths => new(AddonManager.ModuleFilePaths);
private GameModuleLoader() { }
/// <summary>

View File

@ -340,50 +340,63 @@ namespace Milimoe.FunGame.Core.Api.Utility
public class Encryption
{
/// <summary>
/// 使用HMACSHA512算法加密
/// 使用 HMAC-SHA512 算法对文本进行加密
/// </summary>
/// <param name="Message">需要加密的值</param>
/// <param name="Key">秘钥</param>
/// <returns></returns>
internal static string HmacSha512(string Message, string Key)
/// <param name="text">需要加密的文本</param>
/// <param name="key">用于加密的秘钥</param>
/// <returns>加密后的 HMAC-SHA512 哈希值</returns>
public static string HmacSha512(string text, string key)
{
byte[] MessageBytes = General.DefaultEncoding.GetBytes(Message);
Key = Convert.ToBase64String(General.DefaultEncoding.GetBytes(Key));
byte[] KeyBytes = General.DefaultEncoding.GetBytes(Key);
HMACSHA512 Hmacsha512 = new(KeyBytes);
byte[] Hash = Hmacsha512.ComputeHash(MessageBytes);
string Hmac = BitConverter.ToString(Hash).Replace("-", "");
return Hmac.ToLower();
byte[] text_bytes = General.DefaultEncoding.GetBytes(text);
key = Convert.ToBase64String(General.DefaultEncoding.GetBytes(key));
byte[] key_bytes = General.DefaultEncoding.GetBytes(key);
HMACSHA512 hmacsha512 = new(key_bytes);
byte[] hash_bytes = hmacsha512.ComputeHash(text_bytes);
string hmac = BitConverter.ToString(hash_bytes).Replace("-", "");
return hmac.ToLower();
}
/// <summary>
/// 计算文件的 SHA-256 哈希值
/// </summary>
/// <param name="file_path">要计算哈希值的文件路径</param>
/// <returns>文件的 SHA-256 哈希值</returns>
public static string FileSha512(string file_path)
{
using SHA256 sha256 = SHA256.Create();
using FileStream stream = File.OpenRead(file_path);
byte[] hash = sha256.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
/// <summary>
/// 使用 RSA 算法加密
/// </summary>
/// <param name="PlainText">明文</param>
/// <param name="PublicKey">公钥</param>
/// <param name="plain_text">明文</param>
/// <param name="plublic_key">公钥</param>
/// <returns></returns>
public static string RSAEncrypt(string PlainText, string PublicKey)
public static string RSAEncrypt(string plain_text, string plublic_key)
{
byte[] Plain = Encoding.UTF8.GetBytes(PlainText);
using RSACryptoServiceProvider RSA = new();
RSA.FromXmlString(PublicKey);
byte[] Encrypted = RSA.Encrypt(Plain, false);
return Convert.ToBase64String(Encrypted);
byte[] plain = Encoding.UTF8.GetBytes(plain_text);
using RSACryptoServiceProvider rsa = new();
rsa.FromXmlString(plublic_key);
byte[] encrypted = rsa.Encrypt(plain, false);
return Convert.ToBase64String(encrypted);
}
/// <summary>
/// 使用 RSA 算法解密
/// </summary>
/// <param name="SecretText">密文</param>
/// <param name="PrivateKey">私钥</param>
/// <param name="secret_text">密文</param>
/// <param name="private_key">私钥</param>
/// <returns></returns>
public static string RSADecrypt(string SecretText, string PrivateKey)
public static string RSADecrypt(string secret_text, string private_key)
{
byte[] Encrypted = Convert.FromBase64String(SecretText);
using RSACryptoServiceProvider RSA = new();
RSA.FromXmlString(PrivateKey);
byte[] Decrypted = RSA.Decrypt(Encrypted, false);
return Encoding.UTF8.GetString(Decrypted);
byte[] secret = Convert.FromBase64String(secret_text);
using RSACryptoServiceProvider rsa = new();
rsa.FromXmlString(private_key);
byte[] decrypted = rsa.Decrypt(secret, false);
return Encoding.UTF8.GetString(decrypted);
}
}
@ -393,14 +406,15 @@ namespace Milimoe.FunGame.Core.Api.Utility
public static class StringExtension
{
/// <summary>
/// 使用HMACSHA512算法加密
/// 使用 HMAC-SHA512 算法对文本进行加密<para/>
/// 注意:此方法会先将 <paramref name="key" /> 转为小写并计算两次哈希。
/// </summary>
/// <param name="msg">需要加密的值</param>
/// <param name="key">秘钥</param>
/// <returns></returns>
public static string Encrypt(this string msg, string key)
/// <param name="text">需要加密的文本</param>
/// <param name="key">用于加密的秘钥</param>
/// <returns>加密后的 HMAC-SHA512 哈希值</returns>
public static string Encrypt(this string text, string key)
{
return Encryption.HmacSha512(msg, Encryption.HmacSha512(msg, key.ToLower()));
return Encryption.HmacSha512(text, Encryption.HmacSha512(text, key.ToLower()));
}
}

View File

@ -9,7 +9,7 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <para/>文件会保存为:程序目录/configs/<see cref="PluginName"/>/<see cref="FileName"/>.json
/// </summary>
/// <remarks>
/// 新建一个配置文件,文件会保存为:程序目录/configs/<see cref="PluginName"/>/<see cref="FileName"/>.json
/// 新建一个配置文件,文件会保存为:程序目录/configs/<paramref name="plugin_name"/>/<paramref name="file_name"/>.json
/// </remarks>
/// <param name="plugin_name"></param>
/// <param name="file_name"></param>

View File

@ -13,6 +13,11 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// </summary>
public Dictionary<string, Plugin> Plugins { get; } = [];
/// <summary>
/// 已加载的插件DLL名称对应的路径
/// </summary>
public static Dictionary<string, string> PluginFilePaths => new(AddonManager.PluginFilePaths);
private PluginLoader()
{

View File

@ -130,7 +130,7 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <summary>
/// 读取TXT文件内容
/// </summary>
/// <param name="filename">文件名</param>
/// <param name="filename">文件名(需要包含扩展名)</param>
/// <param name="path">相对路径</param>
/// <returns>内容</returns>
public static string ReadTXT(string filename, string path = "")
@ -154,13 +154,14 @@ namespace Milimoe.FunGame.Core.Api.Utility
}
/// <summary>
/// 写入TXT文件内容如不存在文件会创建反之新起一行追加
/// 写入TXT文件内容如不存在文件会创建<para/>
/// <paramref name="overwrite" /> 选项用于覆盖或追加文本
/// </summary>
/// <param name="message"></param>
/// <param name="filename">文件名</param>
/// <param name="content"></param>
/// <param name="filename">文件名(需要包含扩展名)</param>
/// <param name="path">相对路径</param>
/// <returns>内容</returns>
public static void WriteTXT(string message, string filename, string path = "")
/// <param name="overwrite">是否覆盖</param>
public static void WriteTXT(string content, string filename, string path = "", bool overwrite = false)
{
if (path.Trim() != "")
{
@ -170,11 +171,19 @@ namespace Milimoe.FunGame.Core.Api.Utility
}
else path = $@"{AppDomain.CurrentDomain.BaseDirectory}{filename}";
// 写入内容
StreamWriter writer = File.Exists(path) ? new(path, true, General.DefaultEncoding) : new(path, false, General.DefaultEncoding);
writer.WriteLine(message);
StreamWriter writer = File.Exists(path) ? new(path, !overwrite, General.DefaultEncoding) : new(path, false, General.DefaultEncoding);
writer.WriteLine(content);
writer.Close();
}
/// <summary>
/// 写入并覆盖TXT文件内容
/// </summary>
/// <param name="content"></param>
/// <param name="filename">文件名(需要包含扩展名)</param>
/// <param name="path">相对路径</param>
public static void OverwriteTXT(string content, string filename, string path = "") => WriteTXT(content, filename, path, true);
/// <summary>
/// 追加错误日志 默认写入logs文件夹下的当日日期.log文件
/// </summary>

View File

@ -8,6 +8,16 @@ namespace Milimoe.FunGame.Core.Service
{
internal class AddonManager
{
/// <summary>
/// 已加载的插件DLL名称对应的路径
/// </summary>
internal static Dictionary<string, string> PluginFilePaths { get; } = [];
/// <summary>
/// 已加载的模组DLL名称对应的路径
/// </summary>
internal static Dictionary<string, string> ModuleFilePaths { get; } = [];
/// <summary>
/// 从plugins目录加载所有插件
/// </summary>
@ -28,7 +38,7 @@ namespace Milimoe.FunGame.Core.Service
foreach (Type type in assembly.GetTypes().AsEnumerable().Where(type => type.IsSubclassOf(typeof(Plugin))))
{
AddAddonInstances(type, plugins, (instance) =>
if (AddAddonInstances(type, plugins, (instance) =>
{
if (instance.Load(otherobjs))
{
@ -36,7 +46,10 @@ namespace Milimoe.FunGame.Core.Service
return true;
}
return false;
});
}))
{
AddDictionary(PluginFilePaths, assembly, dll);
}
}
}
@ -65,9 +78,11 @@ namespace Milimoe.FunGame.Core.Service
foreach (Type type in assembly.GetTypes().AsEnumerable().Where(type => typeof(IAddon).IsAssignableFrom(type)))
{
bool isAdded = false;
if (type.IsSubclassOf(typeof(GameModule)))
{
AddAddonInstances(type, modules, (instance) =>
isAdded = AddAddonInstances(type, modules, (instance) =>
{
if (instance.Load(otherobjs))
{
@ -79,15 +94,20 @@ namespace Milimoe.FunGame.Core.Service
}
else if (type.IsSubclassOf(typeof(CharacterModule)))
{
AddAddonInstances(type, characters, (instance) => instance.Load(otherobjs));
isAdded = AddAddonInstances(type, characters, (instance) => instance.Load(otherobjs));
}
else if (type.IsSubclassOf(typeof(SkillModule)))
{
AddAddonInstances(type, skills, (instance) => instance.Load(otherobjs));
isAdded = AddAddonInstances(type, skills, (instance) => instance.Load(otherobjs));
}
else if (type.IsSubclassOf(typeof(ItemModule)))
{
AddAddonInstances(type, items, (instance) => instance.Load(otherobjs));
isAdded = AddAddonInstances(type, items, (instance) => instance.Load(otherobjs));
}
if (isAdded)
{
AddDictionary(ModuleFilePaths, assembly, dll);
}
}
}
@ -118,9 +138,11 @@ namespace Milimoe.FunGame.Core.Service
foreach (Type type in assembly.GetTypes().AsEnumerable().Where(type => typeof(IAddon).IsAssignableFrom(type)))
{
bool isAdded = false;
if (type.IsSubclassOf(typeof(GameModule)))
{
AddAddonInstances(type, modules, (instance) =>
isAdded = AddAddonInstances(type, modules, (instance) =>
{
if (instance.Load(otherobjs))
{
@ -132,7 +154,7 @@ namespace Milimoe.FunGame.Core.Service
}
else if (type.IsSubclassOf(typeof(GameModuleServer)))
{
AddAddonInstances(type, servers, (instance) =>
isAdded = AddAddonInstances(type, servers, (instance) =>
{
if (instance.Load(otherobjs))
{
@ -144,15 +166,20 @@ namespace Milimoe.FunGame.Core.Service
}
else if (type.IsSubclassOf(typeof(CharacterModule)))
{
AddAddonInstances(type, characters, (instance) => instance.Load(otherobjs));
isAdded = AddAddonInstances(type, characters, (instance) => instance.Load(otherobjs));
}
else if (type.IsSubclassOf(typeof(SkillModule)))
{
AddAddonInstances(type, skills, (instance) => instance.Load(otherobjs));
isAdded = AddAddonInstances(type, skills, (instance) => instance.Load(otherobjs));
}
else if (type.IsSubclassOf(typeof(ItemModule)))
{
AddAddonInstances(type, items, (instance) => instance.Load(otherobjs));
isAdded = AddAddonInstances(type, items, (instance) => instance.Load(otherobjs));
}
if (isAdded)
{
AddDictionary(ModuleFilePaths, assembly, dll);
}
}
}
@ -178,7 +205,10 @@ namespace Milimoe.FunGame.Core.Service
foreach (Type type in assembly.GetTypes().AsEnumerable().Where(type => type.IsSubclassOf(typeof(GameMap))))
{
AddAddonInstances(type, maps, (instance) => instance.Load(objs));
if (AddAddonInstances(type, maps, (instance) => instance.Load(objs)))
{
AddDictionary(ModuleFilePaths, assembly, dll);
}
}
}
@ -192,8 +222,9 @@ namespace Milimoe.FunGame.Core.Service
/// <param name="type">循环程序集的类型</param>
/// <param name="dictionary">实例的字典</param>
/// <param name="isadd">加载时触发的检查方法返回false不添加</param>
private static void AddAddonInstances<T>(Type type, Dictionary<string, T> dictionary, Func<T, bool>? isadd = null) where T : IAddon
private static bool AddAddonInstances<T>(Type type, Dictionary<string, T> dictionary, Func<T, bool>? isadd = null) where T : IAddon
{
bool isAdded = false;
T? instance = (T?)Activator.CreateInstance(type);
if (instance != null)
{
@ -201,8 +232,16 @@ namespace Milimoe.FunGame.Core.Service
if (!string.IsNullOrWhiteSpace(name) && (isadd == null || isadd(instance)))
{
dictionary.TryAdd(name.Trim(), instance);
}
}
isAdded = true;
}
}
return isAdded;
}
private static void AddDictionary(Dictionary<string, string> dict, Assembly assembly, string dllpath)
{
string filename = assembly.GetName().Name?.Trim() ?? "";
if (filename != "") dict.TryAdd(filename, dllpath);
}
}
}