diff --git a/Next.Api/Attributes/IsUnmanagedAttribute.cs b/Next.Api/Attributes/IsUnmanagedAttribute.cs new file mode 100644 index 0000000..e6c01e3 --- /dev/null +++ b/Next.Api/Attributes/IsUnmanagedAttribute.cs @@ -0,0 +1,13 @@ +// ReSharper disable CheckNamespace + +namespace System.Runtime.CompilerServices; + +/// +/// This attribute is required by the compiler for the "unmanaged" generic type constraint. +/// It should be generated automatically, but for whatever reason its not. +/// +[Obsolete("Do not use directly.", true)] +[AttributeUsage(AttributeTargets.All)] +internal sealed class IsUnmanagedAttribute : Attribute +{ +} \ No newline at end of file diff --git a/Next.Api/Bases/LanguageLoaderBase.cs b/Next.Api/Bases/LanguageLoaderBase.cs new file mode 100644 index 0000000..1c2c52e --- /dev/null +++ b/Next.Api/Bases/LanguageLoaderBase.cs @@ -0,0 +1,10 @@ +using Next.Api.Interfaces; + +namespace Next.Api.Bases; + +public abstract class LanguageLoaderBase(string[] filter) +{ + public string[] Filter { get; protected set; } = filter; + + public abstract void Load(ILanguageManager _manager, Stream stream, string FileName); +} \ No newline at end of file diff --git a/Next.Api/Interfaces/ILanguageManager.cs b/Next.Api/Interfaces/ILanguageManager.cs new file mode 100644 index 0000000..fd4bb00 --- /dev/null +++ b/Next.Api/Interfaces/ILanguageManager.cs @@ -0,0 +1,12 @@ +using Next.Api.Bases; + +namespace Next.Api.Interfaces; + +public interface ILanguageManager +{ + public void AddLoader(LanguageLoaderBase _loader); + + public LanguageLoaderBase? GetLoader(string extensionName); + + public void AddToMap(SupportedLangs lang, string key, string value, string loaderName); +} \ No newline at end of file diff --git a/Next.Api/Logs/LocalWriter.cs b/Next.Api/Logs/LocalWriter.cs new file mode 100644 index 0000000..72f41e8 --- /dev/null +++ b/Next.Api/Logs/LocalWriter.cs @@ -0,0 +1,30 @@ +using BepInEx; +using BepInEx.Core; + +namespace Next.Api.Logs; + +public class LocalWriter +{ + public LocalWriter(NextLog log) + { + if (!Directory.Exists(LogDir)) + Directory.CreateDirectory(LogDir); + + LogFileWriter = new StreamWriter(File.Open(Path.Combine(LogDir, log.LogSource.SourceName), FileMode.OpenOrCreate, FileAccess.Write)) + { + AutoFlush = true, + }; + + LogFileWriter.WriteLine("NextLog Listener Start"); + LogFileWriter.WriteLine($"CurrentTime: {DateTime.Now:g}"); + } + + public static readonly string LogDir = Path.Combine(Paths.GameRootPath, "NextLogs"); + private readonly StreamWriter LogFileWriter; + + public LocalWriter Write(string str) + { + LogFileWriter.WriteLine(str); + return this; + } +} \ No newline at end of file diff --git a/Next.Api/Logs/LogConfigInfo.cs b/Next.Api/Logs/LogConfigInfo.cs new file mode 100644 index 0000000..50e38f6 --- /dev/null +++ b/Next.Api/Logs/LogConfigInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; + +namespace Next.Api.Logs; + +public class LogConfigInfo(Assembly assembly, NextLog log) +{ + public readonly Assembly Assembly = assembly; + public string LogName { get; set; } = string.Empty; + public NextLog _Log { get; set; } = log; + public readonly List UseAssembly = [assembly]; +} \ No newline at end of file diff --git a/Next.Api/Logs/LogHelper.cs b/Next.Api/Logs/LogHelper.cs new file mode 100644 index 0000000..a64d867 --- /dev/null +++ b/Next.Api/Logs/LogHelper.cs @@ -0,0 +1,95 @@ +using System.Reflection; +using BepInEx.Logging; + + +namespace Next.Api.Logs; + +public static class LogHelper +{ + /* + 各消息作用: + + 发生了致命错误,无法从中恢复 : A fatal error has occurred, which cannot be recovered from + Fatal + + 发生错误,但可以从中恢复 : An error has occured, but can be recovered from + Error + + 已发出警告,但并不一定意味着发生了错误 : A warning has been produced, but does not necessarily mean that something wrong has happened + Warning + + 应向用户显示的重要消息 : An important message that should be displayed to the user + Message + + 重要性较低的消息 : A message of low importance + Info + + 可能只有开发人员感兴趣的消息 : A message that would likely only interest a developer + Debug, + */ + + + /// + /// 一般信息 + /// + /// + public static void Info(string Message) + { + Log(Message, LogLevel.Info, Assembly.GetCallingAssembly()); + } + + /// + /// 报错 + /// + /// + public static void Error(string Message) + { + Log(Message, LogLevel.Error, Assembly.GetCallingAssembly()); + } + + /// + /// 测试 + /// + /// + public static void Debug(string Message) + { + Log(Message, LogLevel.Debug, Assembly.GetCallingAssembly()); + } + + public static void Fatal(string Message) + { + Log(Message, LogLevel.Fatal, Assembly.GetCallingAssembly()); + } + + /// + /// 警告 + /// + /// + public static void Warn(string Message) + { + Log(Message, LogLevel.Warning, Assembly.GetCallingAssembly()); + } + + + public static void Message(string Message) + { + Log(Message, LogLevel.Message, Assembly.GetCallingAssembly()); + } + + public static void Exception(Exception exception) + { + Error(exception.ToString()); + } + + public static void Log(object @object, LogLevel errorLevel = LogLevel.None, Assembly? assembly = null) + { + var _assembly = assembly ?? Assembly.GetCallingAssembly(); + var log = NextLog.GetUseLog(_assembly); + log.WriteToFile(@object, errorLevel); + } + + public static void LogObject(object @object) + { + Log(@object, LogLevel.Error, Assembly.GetCallingAssembly()); + } +} \ No newline at end of file diff --git a/Next.Api/Logs/NextLog.cs b/Next.Api/Logs/NextLog.cs new file mode 100644 index 0000000..0cedaf8 --- /dev/null +++ b/Next.Api/Logs/NextLog.cs @@ -0,0 +1,110 @@ +using System.Reflection; +using System.Text; +using BepInEx; +using BepInEx.Logging; + +namespace Next.Api.Logs; + +public class NextLog(Assembly _assembly, ManualLogSource logSource) +{ + public Assembly Assembly = _assembly; + public ManualLogSource LogSource = logSource; + public bool UseLocalWrite { get; set; } = false; + private LocalWriter? _writer; + public LocalWriter? Writer + { + get + { + if (UseLocalWrite) + { + return _writer ??= new LocalWriter(this); + } + + return null; + } + } + + public static List LogConfigInfos = []; + public static NextLog? MainLog { get; private set; } + + public static void RemoveLog(Assembly? assembly = null) + { + var _assembly = assembly ?? Assembly.GetCallingAssembly(); + foreach (var info in LogConfigInfos.Where(n => n.UseAssembly.Contains(_assembly))) + info.UseAssembly.Remove(_assembly); + } + + public static NextLog? SetLog(Assembly assembly) + { + if (LogConfigInfos.All(n => n.Assembly != assembly)) + return null; + + var info = LogConfigInfos.First(n => n.Assembly == assembly); + info.UseAssembly.Add(assembly); + + return info._Log; + } + + public static NextLog CreateMainLog(ManualLogSource logSource) + { + if (ConsoleManager.ConsoleEnabled) + System.Console.OutputEncoding = Encoding.UTF8; + + var assembly = Assembly.GetCallingAssembly(); + MainLog = new NextLog(assembly, logSource); + RemoveLog(assembly); + + LogConfigInfos.Add(new LogConfigInfo(assembly, MainLog) + { + LogName = logSource.SourceName + }); + return MainLog; + } + + public static NextLog GetUseLog(Assembly? assembly = null) + { + var _assembly = assembly ?? Assembly.GetCallingAssembly(); + var log = LogConfigInfos.FirstOrDefault(n => n.UseAssembly.Contains(_assembly))?._Log; + if (log != null) + return log; + + if (MainLog == null) return CreateMainLog(_assembly.GetLogSource()); + + LogConfigInfos.First(n => n._Log == MainLog).UseAssembly.Add(_assembly); + return MainLog; + } + + public NextLog WriteToFile(object @object, LogLevel errorLevel = LogLevel.None) + { + var Message = @object as string; + switch (errorLevel) + { + case LogLevel.Message: + LogSource.LogMessage(Message); + break; + case LogLevel.Error: + LogSource.LogError(Message); + break; + case LogLevel.Warning: + LogSource.LogWarning(Message); + break; + case LogLevel.Fatal: + LogSource.LogFatal(Message); + break; + case LogLevel.Info: + LogSource.LogInfo(Message); + break; + case LogLevel.Debug: + LogSource.LogDebug(Message); + break; + case LogLevel.None: + case LogLevel.All: + default: + LogSource.LogInfo(Message); + goto Writer; + } + Writer: + Writer?.Write($"[FastLog, Level: {errorLevel}] {Message}"); + return this; + } +} \ No newline at end of file diff --git a/Next.Api/Utils/BepInExUtils.cs b/Next.Api/Utils/BepInExUtils.cs new file mode 100644 index 0000000..851305f --- /dev/null +++ b/Next.Api/Utils/BepInExUtils.cs @@ -0,0 +1,25 @@ +using System.Reflection; +using BepInEx; +using BepInEx.Configuration; +using BepInEx.Logging; +using BepInEx.Unity.IL2CPP; + +namespace Next.Api.Utils; + +public static class BepInExUtils +{ + public static PluginInfo GetPlugin(this Assembly assembly) + { + return IL2CPPChainloader.Instance.Plugins.FirstOrDefault(n => n.Value.Location == assembly.Location).Value; + } + + public static ConfigFile GetConfig(this Assembly assembly) => assembly.GetBaseInstance().Config; + + public static ManualLogSource GetLogSource(this Assembly assembly) => assembly.GetBaseInstance().Log; + + public static BasePlugin GetBaseInstance(this Assembly assembly) => (BasePlugin)assembly.GetPlugin().Instance; + + public static T? GetInstance(this PluginInfo info) where T : BasePlugin => info.Instance as T; + + public static BepInPlugin GetMetadata(this Assembly assembly) => assembly.GetPlugin().Metadata; +} \ No newline at end of file