diff --git a/.gitignore b/.gitignore index 834be6d..8f0184d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +# Plugin-specific files +[Pp]lugin.json + # User-specific files *.rsuser *.suo diff --git a/Pandora Behaviour Engine/Models/Patch/IOManagers/Interface/IDataExporter.cs b/Pandora API/IOManagers/IDataExporter.cs similarity index 72% rename from Pandora Behaviour Engine/Models/Patch/IOManagers/Interface/IDataExporter.cs rename to Pandora API/IOManagers/IDataExporter.cs index f357d50..b28a44b 100644 --- a/Pandora Behaviour Engine/Models/Patch/IOManagers/Interface/IDataExporter.cs +++ b/Pandora API/IOManagers/IDataExporter.cs @@ -5,7 +5,7 @@ using System.Text; using System.Threading.Tasks; -namespace Pandora.Patch.IOManagers; +namespace Pandora.API.Patch.IOManagers; public interface IDataExporter { public DirectoryInfo ExportDirectory { get; set; } @@ -19,9 +19,4 @@ public bool ExportParallel(IEnumerable objs) return success; } } -public interface IMetaDataExporter : IDataExporter -{ - public void LoadMetaData(); - public void SaveMetaData(IEnumerable collection); -} diff --git a/Pandora API/IOManagers/IMetaDataExporter.cs b/Pandora API/IOManagers/IMetaDataExporter.cs new file mode 100644 index 0000000..484f7ac --- /dev/null +++ b/Pandora API/IOManagers/IMetaDataExporter.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Pandora.API.Patch.IOManagers; + +public interface IMetaDataExporter : IDataExporter +{ + public void LoadMetaData(); + public void SaveMetaData(IEnumerable collection); +} + diff --git a/Pandora API/Pandora API.csproj b/Pandora API/Pandora API.csproj new file mode 100644 index 0000000..d8e16b8 --- /dev/null +++ b/Pandora API/Pandora API.csproj @@ -0,0 +1,10 @@ + + + + net8.0 + Pandora.API + enable + enable + + + diff --git a/Pandora API/Patch.Engine.Config/IEngineConfiguration.cs b/Pandora API/Patch.Engine.Config/IEngineConfiguration.cs new file mode 100644 index 0000000..e08a476 --- /dev/null +++ b/Pandora API/Patch.Engine.Config/IEngineConfiguration.cs @@ -0,0 +1,11 @@ +namespace Pandora.API.Patch.Engine.Config; + +public interface IEngineConfiguration +{ + string Name { get; } + + string Description { get; } + + public IPatcher Patcher { get; } + +} diff --git a/Pandora Behaviour Engine/Models/Patch/Engine/Configs/IEngineConfigurationFactory.cs b/Pandora API/Patch.Engine.Config/IEngineConfigurationFactory.cs similarity index 52% rename from Pandora Behaviour Engine/Models/Patch/Engine/Configs/IEngineConfigurationFactory.cs rename to Pandora API/Patch.Engine.Config/IEngineConfigurationFactory.cs index 511c539..ae88c5e 100644 --- a/Pandora Behaviour Engine/Models/Patch/Engine/Configs/IEngineConfigurationFactory.cs +++ b/Pandora API/Patch.Engine.Config/IEngineConfigurationFactory.cs @@ -1,8 +1,10 @@ -using Pandora.Core; -using System.ComponentModel; - -namespace Pandora.Core.Engine.Configs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +namespace Pandora.API.Patch.Engine.Config; public interface IEngineConfigurationFactory { public string Name { get; } diff --git a/Pandora API/Patch.Engine.Config/IEngineConfigurationPlugin.cs b/Pandora API/Patch.Engine.Config/IEngineConfigurationPlugin.cs new file mode 100644 index 0000000..1fd032d --- /dev/null +++ b/Pandora API/Patch.Engine.Config/IEngineConfigurationPlugin.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pandora.API.Patch.Engine.Config; +public interface IEngineConfigurationPlugin +{ + public enum OptionFlags + { + None = 0, + HidePatches = 1, + } + public string MenuPath { get; } + public IEngineConfigurationFactory Factory { get; } +} diff --git a/Pandora API/Patch.Engine.Plugins/IPluginInfo.cs b/Pandora API/Patch.Engine.Plugins/IPluginInfo.cs new file mode 100644 index 0000000..ee0c004 --- /dev/null +++ b/Pandora API/Patch.Engine.Plugins/IPluginInfo.cs @@ -0,0 +1,10 @@ + +namespace Pandora.API.Patch.Engine.Plugins; + +public interface IPluginInfo +{ + public const string FILE_HEADER = "plugin"; + string Name { get; set; } + string Author { get; set; } + string Path { get; set; } +} \ No newline at end of file diff --git a/Pandora API/Patch.Engine.Plugins/IPluginLoader.cs b/Pandora API/Patch.Engine.Plugins/IPluginLoader.cs new file mode 100644 index 0000000..65c0dcc --- /dev/null +++ b/Pandora API/Patch.Engine.Plugins/IPluginLoader.cs @@ -0,0 +1,10 @@ +using Pandora.API.Patch.Engine.Plugins; +using System.IO; +using System.Reflection; + +namespace Pandora.Models.Patch.Engine.Plugins; +public interface IPluginLoader +{ + Assembly LoadPlugin(DirectoryInfo directory, IPluginInfo pluginInfo); + bool TryLoadMetadata(DirectoryInfo directory, out IPluginInfo? pluginInfo); +} \ No newline at end of file diff --git a/Pandora API/Patch.Engine.Plugins/PluginInfo.cs b/Pandora API/Patch.Engine.Plugins/PluginInfo.cs new file mode 100644 index 0000000..ba906a1 --- /dev/null +++ b/Pandora API/Patch.Engine.Plugins/PluginInfo.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pandora.API.Patch.Engine.Plugins; +public class PluginInfo : IPluginInfo +{ + public string Name { get; set; } + public string Author { get; set; } + public string Path { get; set; } +} diff --git a/Pandora API/Patch/IModInfo.cs b/Pandora API/Patch/IModInfo.cs new file mode 100644 index 0000000..50b189f --- /dev/null +++ b/Pandora API/Patch/IModInfo.cs @@ -0,0 +1,32 @@ +namespace Pandora.API.Patch; + +/// +/// Strongly recommended to implement GetHashCode() in addition to IEquatable. +/// +public interface IModInfo : IEquatable +{ + public enum ModFormat + { + FNIS, + Nemesis, + Pandora + } + + public string Name { get; } + + public string Author { get; } + + public string URL { get; } + + public string Code { get; } + + public Version Version { get; } + + public DirectoryInfo Folder { get; } + + public ModFormat Format { get; } + + public bool Active { get; set; } + + public uint Priority { get; set; } +} diff --git a/Pandora API/Patch/IPatcher.cs b/Pandora API/Patch/IPatcher.cs new file mode 100644 index 0000000..3c8cff3 --- /dev/null +++ b/Pandora API/Patch/IPatcher.cs @@ -0,0 +1,36 @@ +namespace Pandora.API.Patch; + +public interface IPatcher +{ + [Flags] + public enum PatcherFlags + { + None = 0, + PreloadFailed = 1 << 1, + UpdateFailed = 1 << 2, + LaunchFailed = 1 << 3, + Success = ~(PreloadFailed | UpdateFailed | LaunchFailed) + } + public PatcherFlags Flags { get; } + public string GetVersionString(); + public Version GetVersion(); + public void SetTarget(List mods); + + public Task PreloadAsync(); + + public void Update(); + + public void Run(); + + public string GetPostRunMessages(); + + public string GetFailureMessages(); + + public Task UpdateAsync(); + + public Task RunAsync(); + + public void SetOutputPath(DirectoryInfo directoryInfo); + + public void SetOutputPath(string outputPath) => SetOutputPath(new DirectoryInfo(outputPath)); +} diff --git a/Pandora Behaviour Engine/Data/IModInfoProvider.cs b/Pandora Behaviour Engine/Data/IModInfoProvider.cs index f014464..24e5c90 100644 --- a/Pandora Behaviour Engine/Data/IModInfoProvider.cs +++ b/Pandora Behaviour Engine/Data/IModInfoProvider.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Pandora.Core; - +using Pandora.API.Patch; namespace Pandora.MVVM.Data; public interface IModInfoProvider diff --git a/Pandora Behaviour Engine/Data/NemesisModInfoProvider.cs b/Pandora Behaviour Engine/Data/NemesisModInfoProvider.cs index 4be0b2f..8ffbaa7 100644 --- a/Pandora Behaviour Engine/Data/NemesisModInfoProvider.cs +++ b/Pandora Behaviour Engine/Data/NemesisModInfoProvider.cs @@ -5,7 +5,7 @@ using System.Text; using System.Threading.Tasks; using Pandora.Core; - +using Pandora.API.Patch; namespace Pandora.MVVM.Data; public class NemesisModInfoProvider : IModInfoProvider diff --git a/Pandora Behaviour Engine/Data/PandoraModInfoProvider.cs b/Pandora Behaviour Engine/Data/PandoraModInfoProvider.cs index bf882b6..8421029 100644 --- a/Pandora Behaviour Engine/Data/PandoraModInfoProvider.cs +++ b/Pandora Behaviour Engine/Data/PandoraModInfoProvider.cs @@ -1,4 +1,5 @@ -using Pandora.Core; +using Pandora.API.Patch; +using Pandora.Core; using System; using System.Collections.Generic; diff --git a/Pandora Behaviour Engine/Models/FNISModInfo.cs b/Pandora Behaviour Engine/Models/FNISModInfo.cs index 1c56ddd..82d75cb 100644 --- a/Pandora Behaviour Engine/Models/FNISModInfo.cs +++ b/Pandora Behaviour Engine/Models/FNISModInfo.cs @@ -1,3 +1,4 @@ +using Pandora.API.Patch; using System; using System.Collections.Generic; using System.IO; diff --git a/Pandora Behaviour Engine/Models/IModInfo.cs b/Pandora Behaviour Engine/Models/IModInfo.cs deleted file mode 100644 index 1867bee..0000000 --- a/Pandora Behaviour Engine/Models/IModInfo.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.Contracts; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Pandora.Core -{ - /// - /// Strongly recommended to implement GetHashCode() in addition to IEquatable. - /// - public interface IModInfo : IEquatable - { - public enum ModFormat - { - FNIS, - Nemesis, - Pandora - } - - public string Name { get; } - - public string Author { get; } - - public string URL { get; } - - public string Code { get; } - - public Version Version { get; } - - public DirectoryInfo Folder { get; } - - public ModFormat Format { get; } - - public bool Active { get; set; } - - public uint Priority { get; set; } - } -} diff --git a/Pandora Behaviour Engine/Models/NemesisModInfo.cs b/Pandora Behaviour Engine/Models/NemesisModInfo.cs index f1ef413..3a4ed26 100644 --- a/Pandora Behaviour Engine/Models/NemesisModInfo.cs +++ b/Pandora Behaviour Engine/Models/NemesisModInfo.cs @@ -5,6 +5,7 @@ using System.Security.Policy; using System.Text; using System.Threading.Tasks; +using Pandora.API.Patch; using Pandora.Core; namespace Pandora.Core; diff --git a/Pandora Behaviour Engine/Models/PandoraModInfo.cs b/Pandora Behaviour Engine/Models/PandoraModInfo.cs index e9b0dd1..b6656cb 100644 --- a/Pandora Behaviour Engine/Models/PandoraModInfo.cs +++ b/Pandora Behaviour Engine/Models/PandoraModInfo.cs @@ -1,4 +1,5 @@ -using Pandora.Core; +using Pandora.API.Patch; +using Pandora.Core; using System; using System.Collections.Generic; using System.IO; diff --git a/Pandora Behaviour Engine/Models/Patch/Engine/BehaviourEngine.cs b/Pandora Behaviour Engine/Models/Patch/Engine/BehaviourEngine.cs index 07870c6..43ab8db 100644 --- a/Pandora Behaviour Engine/Models/Patch/Engine/BehaviourEngine.cs +++ b/Pandora Behaviour Engine/Models/Patch/Engine/BehaviourEngine.cs @@ -1,4 +1,6 @@ using Microsoft.Win32; +using Pandora.API.Patch; +using Pandora.API.Patch.Engine.Config; using Pandora.Core.Engine.Configs; using System; using System.Collections.Generic; @@ -7,17 +9,66 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; - +using System.Reflection; +using Pandora.Models.Patch.Engine; +using Pandora.Models.Patch.Engine.Plugins; +using System.Diagnostics; namespace Pandora.Core { public class BehaviourEngine { + private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); public static readonly DirectoryInfo AssemblyDirectory = new FileInfo(System.Reflection.Assembly.GetEntryAssembly()!.Location).Directory!; + public static readonly List EngineConfigurations = new List(); + public readonly static DirectoryInfo? SkyrimGameDirectory; + private static IEnumerable CreateConfigurations(Assembly assembly) + { + foreach(Type type in assembly.GetTypes()) + { + Debug.WriteLine(type.Module.FullyQualifiedName); + if (typeof(IEngineConfigurationPlugin).IsAssignableFrom(type)) + { + IEngineConfigurationPlugin? result = Activator.CreateInstance(type) as IEngineConfigurationPlugin; + if (result != null) + { + yield return result; + } + } + } + yield break; + } + private static void LoadPlugins() + { + var pluginLoader = new JsonPluginLoader(); + var pluginsDirectory = AssemblyDirectory.CreateSubdirectory("Plugins"); + Assembly assembly; + foreach (DirectoryInfo pluginDirectory in pluginsDirectory.EnumerateDirectories()) + { + if (!pluginLoader.TryLoadMetadata(pluginDirectory, out var pluginInfo)) + { + continue; + } + + try + { + assembly = pluginLoader.LoadPlugin(pluginDirectory, pluginInfo); + EngineConfigurations.AddRange(CreateConfigurations(assembly)); + } + catch + { + + } + } + } + private void ReadSkyrimPath() + { + } static BehaviourEngine() { + LoadPlugins(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { var subKey = "SOFTWARE\\Wow6432Node\\Bethesda Softworks\\Skyrim Special Edition"; @@ -64,7 +115,8 @@ public BehaviourEngine(IEngineConfiguration configuration) } public void Launch(List mods) { - + logger.Info($"Launching with configuration {Configuration.Name}"); + logger.Info($"Launching with patcher {Configuration.Patcher.GetVersionString()}"); Configuration.Patcher.SetTarget(mods); Configuration.Patcher.Update(); Configuration.Patcher.Run(); @@ -72,6 +124,8 @@ public void Launch(List mods) public async Task LaunchAsync(List mods) { + logger.Info($"Launching with configuration {Configuration.Name}"); + logger.Info($"Launching with patcher version {Configuration.Patcher.GetVersionString()}"); Configuration.Patcher.SetTarget(mods); if (!OutputPath.Exists) OutputPath.Create(); diff --git a/Pandora Behaviour Engine/Models/Patch/Engine/Configs/SkyrimConfiguration.cs b/Pandora Behaviour Engine/Models/Patch/Engine/Configs/SkyrimConfiguration.cs index b8ca40f..ad38f89 100644 --- a/Pandora Behaviour Engine/Models/Patch/Engine/Configs/SkyrimConfiguration.cs +++ b/Pandora Behaviour Engine/Models/Patch/Engine/Configs/SkyrimConfiguration.cs @@ -1,4 +1,6 @@ -using Pandora.Core; +using Pandora.API.Patch; +using Pandora.API.Patch.Engine.Config; +using Pandora.Core; using Pandora.Core.IOManagers; using Pandora.Core.Patchers; using Pandora.Core.Patchers.Skyrim; diff --git a/Pandora Behaviour Engine/Models/Patch/Engine/Configs/SkyrimDebugConfiguration.cs b/Pandora Behaviour Engine/Models/Patch/Engine/Configs/SkyrimDebugConfiguration.cs index 2b852a4..f18923a 100644 --- a/Pandora Behaviour Engine/Models/Patch/Engine/Configs/SkyrimDebugConfiguration.cs +++ b/Pandora Behaviour Engine/Models/Patch/Engine/Configs/SkyrimDebugConfiguration.cs @@ -1,4 +1,6 @@ -using Pandora.Core.Patchers; +using Pandora.API.Patch; +using Pandora.API.Patch.Engine.Config; +using Pandora.Core.Patchers; using Pandora.Patch.IOManagers.Skyrim; using Pandora.Patch.Patchers.Skyrim; using System; diff --git a/Pandora Behaviour Engine/Models/Patch/Engine/Interface/IEngineConfiguration.cs b/Pandora Behaviour Engine/Models/Patch/Engine/Interface/IEngineConfiguration.cs deleted file mode 100644 index 9aab3e4..0000000 --- a/Pandora Behaviour Engine/Models/Patch/Engine/Interface/IEngineConfiguration.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Pandora.Core; -using Pandora.Core.IOManagers; -using Pandora.Core.Patchers; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.Intrinsics.X86; -using System.Text; -using System.Threading.Tasks; - -namespace Pandora.Core -{ - public interface IEngineConfiguration - { - string Name { get; } - - string Description { get; } - - public IPatcher Patcher { get; } - - } -} diff --git a/Pandora Behaviour Engine/Models/Patch/Engine/Plugins/JsonPluginLoader.cs b/Pandora Behaviour Engine/Models/Patch/Engine/Plugins/JsonPluginLoader.cs new file mode 100644 index 0000000..155bd70 --- /dev/null +++ b/Pandora Behaviour Engine/Models/Patch/Engine/Plugins/JsonPluginLoader.cs @@ -0,0 +1,41 @@ +using Pandora.API.Patch.Engine.Plugins; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Pandora.Models.Patch.Engine.Plugins; +public class JsonPluginLoader : IPluginLoader +{ + private static JsonSerializerOptions jsonOptions = new() + { + AllowTrailingCommas = true, + PropertyNameCaseInsensitive = true + }; + + public bool TryLoadMetadata(DirectoryInfo directory, [NotNullWhen(true)] out IPluginInfo? pluginInfo) + { + pluginInfo = null; + FileInfo infoFile = new(Path.Join(directory.FullName, string.Concat(IPluginInfo.FILE_HEADER, ".json"))); + if (!infoFile.Exists) { return false; } + using (var readStream = infoFile.OpenRead()) + { + pluginInfo = JsonSerializer.Deserialize(readStream, jsonOptions); + } + return pluginInfo != null; + } + public Assembly LoadPlugin(DirectoryInfo directory, IPluginInfo pluginInfo) + { + string pluginPath = Path.Join(directory.FullName, pluginInfo.Path); + + PluginLoadContext loadContext = new(pluginPath); + + return loadContext.LoadFromAssemblyName(AssemblyName.GetAssemblyName(pluginPath)); + } + +} diff --git a/Pandora Behaviour Engine/Models/Patch/Engine/Plugins/PluginLoadContext.cs b/Pandora Behaviour Engine/Models/Patch/Engine/Plugins/PluginLoadContext.cs new file mode 100644 index 0000000..5955d20 --- /dev/null +++ b/Pandora Behaviour Engine/Models/Patch/Engine/Plugins/PluginLoadContext.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; +using System.Text; +using System.Threading.Tasks; + +namespace Pandora.Models.Patch.Engine.Plugins; +internal class PluginLoadContext : AssemblyLoadContext +{ + private readonly AssemblyDependencyResolver dependancyResolver; + + public PluginLoadContext(string pluginPath) + { + dependancyResolver = new AssemblyDependencyResolver(pluginPath); + } + public PluginLoadContext(FileInfo fileInfo) + { + dependancyResolver = new(fileInfo.FullName); + } + + protected override Assembly? Load(AssemblyName assemblyName) + { + string? assemblyPath = dependancyResolver.ResolveAssemblyToPath(assemblyName); + if (assemblyPath == null) { return null; } + return LoadFromAssemblyPath(assemblyPath); + } + + protected override nint LoadUnmanagedDll(string unmanagedDllName) + { + string? libraryPath = dependancyResolver.ResolveUnmanagedDllToPath(unmanagedDllName); + if (libraryPath == null) { return nint.Zero; } + return LoadUnmanagedDllFromPath(libraryPath); + } + +} diff --git a/Pandora Behaviour Engine/Models/Patch/IOManagers/Skyrim/DebugPackFileExporter.cs b/Pandora Behaviour Engine/Models/Patch/IOManagers/Skyrim/DebugPackFileExporter.cs index 29b5479..9d355d1 100644 --- a/Pandora Behaviour Engine/Models/Patch/IOManagers/Skyrim/DebugPackFileExporter.cs +++ b/Pandora Behaviour Engine/Models/Patch/IOManagers/Skyrim/DebugPackFileExporter.cs @@ -1,4 +1,5 @@ using HKX2E; +using Pandora.API.Patch.IOManagers; using Pandora.Core; using Pandora.Core.IOManagers; using Pandora.Patch.Patchers.Skyrim.Hkx; diff --git a/Pandora Behaviour Engine/Models/Patch/IOManagers/Skyrim/PackFileExporter.cs b/Pandora Behaviour Engine/Models/Patch/IOManagers/Skyrim/PackFileExporter.cs index 5d8ebaa..e951a3d 100644 --- a/Pandora Behaviour Engine/Models/Patch/IOManagers/Skyrim/PackFileExporter.cs +++ b/Pandora Behaviour Engine/Models/Patch/IOManagers/Skyrim/PackFileExporter.cs @@ -1,5 +1,6 @@ using HKX2E; using NLog; +using Pandora.API.Patch.IOManagers; using Pandora.Core; using Pandora.Patch.Patchers.Skyrim.Hkx; using System; diff --git a/Pandora Behaviour Engine/Models/Patch/Patchers/Interface/IAssembler.cs b/Pandora Behaviour Engine/Models/Patch/Patchers/Interface/IAssembler.cs index bd7af85..17aeae7 100644 --- a/Pandora Behaviour Engine/Models/Patch/Patchers/Interface/IAssembler.cs +++ b/Pandora Behaviour Engine/Models/Patch/Patchers/Interface/IAssembler.cs @@ -1,4 +1,5 @@ -using Pandora.Core; +using Pandora.API.Patch; +using Pandora.Core; using System; using System.Collections.Generic; using System.IO; diff --git a/Pandora Behaviour Engine/Models/Patch/Patchers/Interface/IPatcher.cs b/Pandora Behaviour Engine/Models/Patch/Patchers/Interface/IPatcher.cs deleted file mode 100644 index 4640ec5..0000000 --- a/Pandora Behaviour Engine/Models/Patch/Patchers/Interface/IPatcher.cs +++ /dev/null @@ -1,52 +0,0 @@ - -using Pandora.Core.IOManagers; -using Pandora.Patch.Patchers; -using Pandora.Patch.Patchers.Skyrim.Hkx; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Pandora.Core.Patchers -{ - public interface IPatcher - { - [Flags] - public enum PatcherFlags - { - None = 0, - PreloadFailed = 1 << 1, - UpdateFailed = 1 << 2, - LaunchFailed = 1 << 3, - Success = ~PreloadFailed & ~UpdateFailed & ~LaunchFailed - } - public PatcherFlags Flags { get; } - - public string GetVersionString(); - - public Version GetVersion(); - public void SetTarget(List mods); - - public Task PreloadAsync(); - - public void Update(); - - public string GetPostUpdateMessages() => string.Empty; - - public void Run(); - - public string GetPostRunMessages() => string.Empty; - - public string GetFailureMessages(); - - public Task UpdateAsync(); - - public Task RunAsync(); - - public void SetOutputPath(DirectoryInfo directoryInfo); - - public void SetOutputPath(string outputPath) => SetOutputPath(new DirectoryInfo(outputPath)); - } -} diff --git a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/FNIS/FNISAnimationList.cs b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/FNIS/FNISAnimationList.cs index ef54b1e..89a4a0e 100644 --- a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/FNIS/FNISAnimationList.cs +++ b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/FNIS/FNISAnimationList.cs @@ -1,5 +1,6 @@ using HKX2E; using NLog; +using Pandora.API.Patch; using Pandora.Core; using Pandora.Core.Patchers.Skyrim; using Pandora.Patch.Patchers.Skyrim.Hkx; diff --git a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/FNIS/FNISParser.cs b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/FNIS/FNISParser.cs index 7014b17..041f466 100644 --- a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/FNIS/FNISParser.cs +++ b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/FNIS/FNISParser.cs @@ -1,4 +1,5 @@ using HKX2E; +using Pandora.API.Patch; using Pandora.Core; using Pandora.Core.Patchers.Skyrim; using Pandora.Patch.Patchers.Skyrim.Hkx; diff --git a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/IPackFileChangeOwner.cs b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/IPackFileChangeOwner.cs index 4698e64..2a3f452 100644 --- a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/IPackFileChangeOwner.cs +++ b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/IPackFileChangeOwner.cs @@ -1,4 +1,5 @@ -using Pandora.Core; +using Pandora.API.Patch; +using Pandora.Core; using System.Xml.Linq; namespace Pandora.Patch.Patchers.Skyrim.Hkx; diff --git a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/PackFile.cs b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/PackFile.cs index 23f0fec..cff1664 100644 --- a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/PackFile.cs +++ b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/PackFile.cs @@ -13,29 +13,12 @@ namespace Pandora.Patch.Patchers.Skyrim.Hkx; public class PackFile : IEquatable { - public struct IntermediateNode where T : IHavokObject - { - public string Name; - public T NewObject; - - public IntermediateNode(string name, T newObject) - { - Name = name; - NewObject = newObject; - } - - public void Build(PackFile packFile) - { - //packFile.Deserializer.UpdateObjectByName(Name, NewObject); - packFile.Deserializer.UpdatePropertyReferences(Name, NewObject); - } - } public XMap Map { get; private set; } public HavokReferenceXmlDeserializer Deserializer { get; private set; } = new(); - public HavokXmlPartialDeserializer PartialDeserializer { get; private set; } = new(); + public HavokXmlPartialDeserializer PartialDeserializer { get; private set; } = new(); public HavokXmlPartialSerializer PartialSerializer { get; private set; } = new(); - public HavokXmlSerializer Serializer { get; private set; } = new(); + public HavokXmlSerializer Serializer { get; private set; } = new(); public hkRootLevelContainer Container { get; private set; } public static readonly string ROOT_CONTAINER_NAME = "__data__"; @@ -53,7 +36,7 @@ public void Build(PackFile packFile) public FileInfo RebaseOutput(DirectoryInfo exportDirectory) { - OutputHandle = new FileInfo(Path.Join(exportDirectory.FullName, relativeOutputFilePath)); + OutputHandle = new FileInfo(Path.Join(exportDirectory.FullName, relativeOutputFilePath)); return OutputHandle; } @@ -75,11 +58,15 @@ public FileInfo RebaseOutput(DirectoryInfo exportDirectory) public void ApplyChanges() => Dispatcher.ApplyChanges(this); public IEnumerable IndexedElements => objectElementMap.Values; - public Project? ParentProject { get => parentProject; - set { + public Project? ParentProject + { + get => parentProject; + set + { parentProject = value; UniqueName = $"{ParentProject?.Identifier}~{Name}"; - } } + } + } protected ILookup? classLookup = null; @@ -117,7 +104,7 @@ public PackFile(FileInfo file) : this(file, null) } public virtual void Load() - { + { } [MemberNotNull(nameof(classLookup))] @@ -134,14 +121,14 @@ public void BuildClassLookup() public virtual void Activate() { if (!CanActivate()) return; - active = true; + active = true; } public virtual void PopPriorityXmlAsObjects() { } - + public PackFile(string filePath) : this(new FileInfo(filePath)) { } public string UniqueName { get; private set; } @@ -153,7 +140,7 @@ public bool PathExists(string nodeName, string path) { return true; } - return false; + return false; } public bool TargetExists(string nodeName) { @@ -170,7 +157,7 @@ public bool TryGetXMap(string nodeName, [NotNullWhen(true)] out XMapElement? xma } [MemberNotNull(nameof(classLookup))] - public void TryBuildClassLookup() { if (classLookup == null) { BuildClassLookup(); } } + public void TryBuildClassLookup() { if (classLookup == null) { BuildClassLookup(); } } public HashSet UsedNodeNames => Map.NavigateTo(PackFile.ROOT_CONTAINER_NAME).Elements().Select(e => e.Attribute("name")!.Value).ToHashSet(); public XElement GetFirstNodeOfClass(string className) { @@ -183,12 +170,12 @@ public bool PopObjectAsXml(T node) where T : IHavokObject { if (objectElementMap.ContainsKey(node)) { - return false; + return false; } XMapElement mappedElement = new XMapElement(PartialSerializer.SerializeObject(node)); mappedElement.MapSlice(mappedElement); objectElementMap.Add(node, mappedElement); - return true; + return true; } public bool PopObjectAsXml(string nodeName) { @@ -254,9 +241,9 @@ public T GetPushXmlAsObject(T targetObject) where T : class, IHavokObject } if (!Deserializer.TryGetObjectAs(name, out var newObj)) { - return targetObject; + return targetObject; } - return newObj; + return newObj; } public T GetPushedObjectAs(string name) where T : class, IHavokObject { @@ -272,7 +259,7 @@ public virtual void PushPriorityObjects() } public virtual void PushXmlAsObjects() { - foreach(var kvp in objectElementMap.OrderBy(kvp => Deserializer.GetOrder(Serializer.GetName(kvp.Key)))) + foreach (var kvp in objectElementMap.OrderBy(kvp => Deserializer.GetOrder(Serializer.GetName(kvp.Key)))) { IHavokObject? obj = null; try @@ -282,7 +269,7 @@ public virtual void PushXmlAsObjects() catch (Exception ex) { Logger.Error($"Packfile > Active Nodes > Push > FAILED > {ex.ToString()}"); - continue; + continue; } string name = Serializer.GetName(kvp.Key); Deserializer.UpdateDirectReference(kvp.Key, obj); @@ -290,7 +277,7 @@ public virtual void PushXmlAsObjects() Deserializer.UpdateMapping(name, obj); } - objectElementMap.Clear(); + objectElementMap.Clear(); } public bool Equals(PackFile? other) { @@ -303,7 +290,7 @@ public override int GetHashCode() public override bool Equals(object? obj) { - if (obj == null || ! (obj is PackFile)) return false; + if (obj == null || !(obj is PackFile)) return false; return Equals((PackFile)obj); } diff --git a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/PackFileChangeSet.cs b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/PackFileChangeSet.cs index ad50e5b..951fb1e 100644 --- a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/PackFileChangeSet.cs +++ b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/PackFileChangeSet.cs @@ -1,4 +1,5 @@ using NLog; +using Pandora.API.Patch; using Pandora.Core; using System; using System.Collections.Generic; diff --git a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/PackFileTargetCache.cs b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/PackFileTargetCache.cs index e187af7..3cbb8ba 100644 --- a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/PackFileTargetCache.cs +++ b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/PackFileTargetCache.cs @@ -1,4 +1,5 @@ using HKX2E; +using Pandora.API.Patch; using Pandora.Core; using Pandora.Core.Patchers.Skyrim; using System; diff --git a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/PatchNodeCreator.cs b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/PatchNodeCreator.cs index 1a0912c..a3fd2b3 100644 --- a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/PatchNodeCreator.cs +++ b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Hkx/PatchNodeCreator.cs @@ -1,4 +1,5 @@ using HKX2E; +using Pandora.API.Patch; using Pandora.Core; using System; using System.Collections.Generic; diff --git a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Nemesis/NemesisAssembler.cs b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Nemesis/NemesisAssembler.cs index 5c2766a..03cfc56 100644 --- a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Nemesis/NemesisAssembler.cs +++ b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Nemesis/NemesisAssembler.cs @@ -1,8 +1,8 @@ using NLog; +using Pandora.API.Patch; +using Pandora.API.Patch.IOManagers; using Pandora.Core; -using Pandora.Core.IOManagers; using Pandora.Core.Patchers.Skyrim; -using Pandora.Patch.IOManagers; using Pandora.Patch.IOManagers.Skyrim; using Pandora.Patch.Patchers.Skyrim.AnimData; using Pandora.Patch.Patchers.Skyrim.AnimSetData; diff --git a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Pandora/PandoraFragmentAssembler.cs b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Pandora/PandoraFragmentAssembler.cs index 5de5ef9..6cd0848 100644 --- a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Pandora/PandoraFragmentAssembler.cs +++ b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/Pandora/PandoraFragmentAssembler.cs @@ -1,4 +1,5 @@ using HKX2E; +using Pandora.API.Patch; using Pandora.Core; using Pandora.Core.Patchers.Skyrim; using Pandora.Patch.Patchers.Skyrim.AnimData; diff --git a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/ProjectManager.cs b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/ProjectManager.cs index 4d08ec2..a4e07ac 100644 --- a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/ProjectManager.cs +++ b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/ProjectManager.cs @@ -1,5 +1,6 @@ using HKX2E; using NLog; +using Pandora.API.Patch; using Pandora.Patch.Patchers.Skyrim.AnimData; using Pandora.Patch.Patchers.Skyrim.AnimSetData; using Pandora.Patch.Patchers.Skyrim.FNIS; diff --git a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/SkyrimPatcher.cs b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/SkyrimPatcher.cs index 4f8f4c2..e9cb0f7 100644 --- a/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/SkyrimPatcher.cs +++ b/Pandora Behaviour Engine/Models/Patch/Patchers/Skyrim/SkyrimPatcher.cs @@ -18,6 +18,8 @@ using Pandora.Patch.Patchers.Skyrim.Hkx; using Pandora.Patch.IOManagers; using Pandora.Patch.IOManagers.Skyrim; +using Pandora.API.Patch; +using Pandora.API.Patch.IOManagers; namespace Pandora.Patch.Patchers.Skyrim; using PatcherFlags = IPatcher.PatcherFlags; @@ -168,4 +170,9 @@ public void SetOutputPath(DirectoryInfo directoryInfo) nemesisAssembler.SetOutputPath(directoryInfo); pandoraAssembler.SetOutputPath(directoryInfo); } + + public string GetPostUpdateMessages() + { + return string.Empty; + } } diff --git a/Pandora Behaviour Engine/Pandora Behaviour Engine.csproj b/Pandora Behaviour Engine/Pandora Behaviour Engine.csproj index 38b1cb4..7761f10 100644 --- a/Pandora Behaviour Engine/Pandora Behaviour Engine.csproj +++ b/Pandora Behaviour Engine/Pandora Behaviour Engine.csproj @@ -14,6 +14,7 @@ + @@ -34,7 +35,8 @@ - + + @@ -1079,6 +1081,9 @@ PreserveNewest + + Always + SettingsSingleFileGenerator GUISettings.Designer.cs diff --git a/Pandora Behaviour Engine/Plugins/.gitkeep b/Pandora Behaviour Engine/Plugins/.gitkeep new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/Pandora Behaviour Engine/Plugins/.gitkeep @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Pandora Behaviour Engine/View/Controls/EngineMenu.axaml b/Pandora Behaviour Engine/View/Controls/EngineMenu.axaml index fe2dc2f..0f9eeee 100644 --- a/Pandora Behaviour Engine/View/Controls/EngineMenu.axaml +++ b/Pandora Behaviour Engine/View/Controls/EngineMenu.axaml @@ -9,7 +9,7 @@ + BorderThickness="0" Height="20" FontSize="12" FontFamily="Bahnschrift" IsVisible="{Binding MenuEnabled}"> : IEngineConfigurationFactory where T : class,IEngineConfiguration,new() +{ + public ConstEngineConfigurationFactory(string name) + { + Name = name; + } + + public string Name { get; set; } + + public IEngineConfiguration? Config => new T(); + +} + diff --git a/Pandora Behaviour Engine/ViewModels/EngineConfigurationViewModel.cs b/Pandora Behaviour Engine/ViewModels/EngineConfigurationViewModel.cs index d6fe6ac..4b43587 100644 --- a/Pandora Behaviour Engine/ViewModels/EngineConfigurationViewModel.cs +++ b/Pandora Behaviour Engine/ViewModels/EngineConfigurationViewModel.cs @@ -1,4 +1,5 @@ using NLog.Filters; +using Pandora.API.Patch.Engine.Config; using Pandora.Command; using Pandora.Core; using Pandora.Core.Engine.Configs; @@ -12,49 +13,26 @@ using System.Threading.Tasks; namespace Pandora.ViewModels; -public interface IEngineConfigurationViewModel : INotifyPropertyChanged -{ - public string Name { get; } - public RelayCommand? SetCommand { get; } - public ObservableCollection NestedViewModels { get; } -} -public class EngineConfigurationViewModel : IEngineConfigurationFactory,IEngineConfigurationViewModel where T : class, IEngineConfiguration, new() +public class EngineConfigurationViewModel : IEngineConfigurationFactory, IEngineConfigurationViewModel { + private IEngineConfigurationFactory engineConfigurationFactory; + public event PropertyChangedEventHandler? PropertyChanged; private void RaisePropertyChanged([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - public string Name { get; private set; } - public IEngineConfiguration? Config => new T(); + public string Name => engineConfigurationFactory.Name; + public IEngineConfiguration? Config => engineConfigurationFactory.Config; public ObservableCollection NestedViewModels { get; private set; } = new ObservableCollection(); public RelayCommand? SetCommand { get; } = null; - public EngineConfigurationViewModel(string name, RelayCommand setCommand) + public EngineConfigurationViewModel(IEngineConfigurationFactory factory,RelayCommand setCommand) { - Name = name; + engineConfigurationFactory = factory; SetCommand = setCommand; } } -public class EngineConfigurationViewModelContainer : IEngineConfigurationViewModel -{ - public event PropertyChangedEventHandler? PropertyChanged; - private void RaisePropertyChanged([CallerMemberName] string? propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - public string Name { get; private set; } - - public RelayCommand? SetCommand { get; } = null; - - public ObservableCollection NestedViewModels { get; private set; } = new ObservableCollection(); - public EngineConfigurationViewModelContainer(string name, params IEngineConfigurationViewModel[] viewModels) - { - Name = name; - foreach (var viewModel in viewModels) { NestedViewModels.Add(viewModel); } - } -} - diff --git a/Pandora Behaviour Engine/ViewModels/EngineConfigurationViewModelContainer.cs b/Pandora Behaviour Engine/ViewModels/EngineConfigurationViewModelContainer.cs new file mode 100644 index 0000000..970fe80 --- /dev/null +++ b/Pandora Behaviour Engine/ViewModels/EngineConfigurationViewModelContainer.cs @@ -0,0 +1,31 @@ +using Pandora.Command; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace Pandora.ViewModels; + +public class EngineConfigurationViewModelContainer : IEngineConfigurationViewModel +{ + public event PropertyChangedEventHandler? PropertyChanged; + private void RaisePropertyChanged([CallerMemberName] string? propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + public string Name { get; private set; } + + public RelayCommand? SetCommand { get; } = null; + + public ObservableCollection NestedViewModels { get; private set; } = new ObservableCollection(); + public EngineConfigurationViewModelContainer(string name, params IEngineConfigurationViewModel[] viewModels) + { + Name = name; + foreach (var viewModel in viewModels) { NestedViewModels.Add(viewModel); } + } + public EngineConfigurationViewModelContainer(string name) + { + Name = name; + NestedViewModels = new(); + } +} + diff --git a/Pandora Behaviour Engine/ViewModels/EngineViewModel.cs b/Pandora Behaviour Engine/ViewModels/EngineViewModel.cs index 2d63647..b07d315 100644 --- a/Pandora Behaviour Engine/ViewModels/EngineViewModel.cs +++ b/Pandora Behaviour Engine/ViewModels/EngineViewModel.cs @@ -18,6 +18,10 @@ using System.Security.Policy; using Avalonia.Controls.ApplicationLifetimes; using Avalonia; +using Pandora.API.Patch; +using Pandora.API.Patch.Engine.Config; +using System.Reflection; +using System.Xml.Linq; namespace Pandora.ViewModels @@ -72,7 +76,14 @@ public ObservableCollection ModViewModels } private bool engineRunning = false; - + public bool MenuEnabled + { + get => menuEnabled; + private set + { + SetProperty(ref menuEnabled, value); + } + } public bool EngineRunning { get => engineRunning; @@ -115,7 +126,7 @@ public string LogText { private string cachedSearchText = string.Empty; private string searchText = ""; private string searchBGText = "Search"; - + private bool menuEnabled = false; public string SearchText { get => searchText; set @@ -168,7 +179,9 @@ public string LogText { } } - public void SortMods() + private static readonly char[] menuPathSeparators = new char[] { '/', '\\' }; + + public void SortMods() { Mods = Mods.OrderBy(m => m.Code == "pandora").ThenBy(m => m.Priority == 0).ThenBy(m => m.Priority).ToList(); } @@ -191,7 +204,6 @@ public EngineViewModel() CultureInfo.DefaultThreadCurrentCulture = culture; CultureInfo.DefaultThreadCurrentUICulture = culture; CultureInfo.CurrentCulture = culture; - SetupConfigurationOptions(); ReadStartupArguments(); activeModConfig = new FileInfo($"{currentDirectory}\\Pandora_Engine\\ActiveMods.txt"); preloadTask = Task.Run(Engine.PreloadAsync); @@ -200,17 +212,51 @@ public EngineViewModel() } - private void SetupConfigurationOptions() + private void SetupExternalConfigurationPlugin(IEngineConfigurationPlugin injection) + { + if (string.IsNullOrEmpty(injection.MenuPath)) + { + EngineConfigurationViewModels.Add(new EngineConfigurationViewModel(injection.Factory, SetEngineConfigCommand)); + return; + } + + string[] pathSegments = injection.MenuPath.Split(menuPathSeparators); + EngineConfigurationViewModelContainer? container = null; + int index = 0; + container = EngineConfigurationViewModels + .Where(vm => vm.Name.Equals(pathSegments[index], StringComparison.OrdinalIgnoreCase)).FirstOrDefault() + as EngineConfigurationViewModelContainer; + if (container == null) + { + container = new(pathSegments[index]); + EngineConfigurationViewModels.Add(container); + } + index++; + while (pathSegments.Length > index) + { + var tempContainer = container.NestedViewModels + .Where(vm => vm.Name.Equals(pathSegments[index], StringComparison.OrdinalIgnoreCase)).FirstOrDefault() +as EngineConfigurationViewModelContainer; + if (tempContainer == null) + { + tempContainer = new EngineConfigurationViewModelContainer(pathSegments[index]); + container.NestedViewModels.Add(tempContainer); + } + container = tempContainer; + index++; + } + container.NestedViewModels.Add(new EngineConfigurationViewModel(injection.Factory, SetEngineConfigCommand)); + } + private async Task SetupConfigurationOptions() { - engineConfigurationFactory = new EngineConfigurationViewModel("Normal", SetEngineConfigCommand); + engineConfigurationFactory = new EngineConfigurationViewModel(new ConstEngineConfigurationFactory("Normal"), SetEngineConfigCommand); EngineConfigurationViewModels.Add( - new EngineConfigurationViewModelContainer("Skyrim SE/AE", + new EngineConfigurationViewModelContainer("Skyrim 64", new EngineConfigurationViewModelContainer("Behavior", - new EngineConfigurationViewModelContainer("Patch", - (EngineConfigurationViewModel)engineConfigurationFactory, - new EngineConfigurationViewModel("Debug", SetEngineConfigCommand) + (EngineConfigurationViewModel)engineConfigurationFactory, + new EngineConfigurationViewModel(new ConstEngineConfigurationFactory("Debug"), SetEngineConfigCommand) ) //, //new EngineConfigurationViewModelContainer("Convert" @@ -221,7 +267,16 @@ private void SetupConfigurationOptions() ) ) ); - + + foreach (var configPlugin in BehaviourEngine.EngineConfigurations) + { + SetupExternalConfigurationPlugin(configPlugin); + } + if (BehaviourEngine.EngineConfigurations.Count > 0) + { + await WriteLogBoxLine("Plugins loaded."); + } + MenuEnabled = true; //EngineConfigs.Add(new EngineConfigurationViewModel("Skyrim SE/AE", SetEngineConfigCommand)); //EngineConfigs.Add(new EngineConfigurationViewModel("Skyrim SE/AE Debug", SetEngineConfigCommand)); } @@ -230,7 +285,7 @@ public async Task LoadAsync() var launchDirectory = BehaviourEngine.AssemblyDirectory.FullName; ModViewModels.Clear(); Mods.Clear(); - + var pluginsTask = SetupConfigurationOptions(); List modInfoList; { HashSet modInfos = new(); @@ -258,6 +313,7 @@ public async Task LoadAsync() Mods.Add(modInfo); ModViewModels.Add(new(modInfo)); } + await pluginsTask; await WriteLogBoxLine("Mods loaded."); } @@ -295,7 +351,7 @@ private void ReadStartupArguments() { if (startupArguments.Remove("-skyrimDebug64")) { - engineConfigurationFactory = new EngineConfigurationViewModel("Debug", SetEngineConfigCommand); + engineConfigurationFactory = new EngineConfigurationViewModel(new ConstEngineConfigurationFactory("Debug"), SetEngineConfigCommand); Engine = new BehaviourEngine(engineConfigurationFactory.Config); } if (startupArguments.Remove("-autoClose")) diff --git a/Pandora Behaviour Engine/ViewModels/IEngineConfigurationViewModel.cs b/Pandora Behaviour Engine/ViewModels/IEngineConfigurationViewModel.cs new file mode 100644 index 0000000..bca69a0 --- /dev/null +++ b/Pandora Behaviour Engine/ViewModels/IEngineConfigurationViewModel.cs @@ -0,0 +1,12 @@ +using Pandora.Command; +using System.Collections.ObjectModel; +using System.ComponentModel; + +namespace Pandora.ViewModels; + +public interface IEngineConfigurationViewModel : INotifyPropertyChanged +{ + public string Name { get; } + public RelayCommand? SetCommand { get; } + public ObservableCollection NestedViewModels { get; } +} diff --git a/Pandora Behaviour Engine/ViewModels/ModInfoViewModel.cs b/Pandora Behaviour Engine/ViewModels/ModInfoViewModel.cs index d290468..92b830e 100644 --- a/Pandora Behaviour Engine/ViewModels/ModInfoViewModel.cs +++ b/Pandora Behaviour Engine/ViewModels/ModInfoViewModel.cs @@ -1,4 +1,5 @@  +using Pandora.API.Patch; using Pandora.Core; using System; using System.Collections.Generic; @@ -8,7 +9,7 @@ using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; -using static Pandora.Core.IModInfo; + namespace Pandora.ViewModels; public class ModInfoViewModel : INotifyPropertyChanged, IEquatable @@ -47,7 +48,7 @@ public ModInfoViewModel(IModInfo modInfo) public DirectoryInfo Folder => ModInfo.Folder; - public ModFormat Format => ModInfo.Format; + public IModInfo.ModFormat Format => ModInfo.Format; public bool Active { diff --git a/Pandora Example Plugin/EngineConfigurationFactory.cs b/Pandora Example Plugin/EngineConfigurationFactory.cs new file mode 100644 index 0000000..89eba03 --- /dev/null +++ b/Pandora Example Plugin/EngineConfigurationFactory.cs @@ -0,0 +1,10 @@ +using Pandora.API.Patch.Engine.Config; + +namespace ExamplePlugin; + +public class EngineConfigurationFactory : IEngineConfigurationFactory +{ + public string Name { get; } = "Example"; + + public IEngineConfiguration? Config => new ExampleEngineConfiguration(); +} diff --git a/Pandora Example Plugin/ExampleConfigurationInjection.cs b/Pandora Example Plugin/ExampleConfigurationInjection.cs new file mode 100644 index 0000000..094090d --- /dev/null +++ b/Pandora Example Plugin/ExampleConfigurationInjection.cs @@ -0,0 +1,10 @@ +using Pandora.API.Patch.Engine.Config; + +namespace ExamplePlugin; + +public class ExampleConfigurationInjection : IEngineConfigurationPlugin +{ + public string MenuPath { get; } = "Skyrim 64/Behavior/Patch"; + + public IEngineConfigurationFactory Factory { get; } = new EngineConfigurationFactory(); +} diff --git a/Pandora Example Plugin/ExampleEngineConfiguration.cs b/Pandora Example Plugin/ExampleEngineConfiguration.cs new file mode 100644 index 0000000..bd27402 --- /dev/null +++ b/Pandora Example Plugin/ExampleEngineConfiguration.cs @@ -0,0 +1,13 @@ +using Pandora.API.Patch; +using Pandora.API.Patch.Engine.Config; + +namespace ExamplePlugin; + +public class ExampleEngineConfiguration : IEngineConfiguration +{ + public string Name { get; } = "Stuff"; + + public string Description { get; } = "Does some stuff."; + + public IPatcher Patcher { get; } = new ExamplePatcher(); +} diff --git a/Pandora Example Plugin/ExamplePatcher.cs b/Pandora Example Plugin/ExamplePatcher.cs new file mode 100644 index 0000000..7291e0b --- /dev/null +++ b/Pandora Example Plugin/ExamplePatcher.cs @@ -0,0 +1,63 @@ +using Pandora.API.Patch; +using System.Diagnostics; + +namespace ExamplePlugin; + +public class ExamplePatcher : IPatcher +{ + public IPatcher.PatcherFlags Flags { get; } = IPatcher.PatcherFlags.None; + + public string GetFailureMessages() + { + return "\nFailed. Uh oh!"; + } + + public string GetPostRunMessages() + { + return "\nDone!"; + } + public Version GetVersion() + { + return new Version(0, 6, 9); + } + + public string GetVersionString() + { + return $"{GetVersion()}-sigma"; + } + + public async Task PreloadAsync() + { + await Task.Run(() => { Debug.WriteLine("Testing preload."); }); + } + + public void Run() + { + return; + } + + public async Task RunAsync() + { + return await Task.Run(() => { return true; }); + } + + public void SetOutputPath(DirectoryInfo directoryInfo) + { + return; + } + + public void SetTarget(List mods) + { + return; + } + + public void Update() + { + return; + } + + public async Task UpdateAsync() + { + return await Task.Run(() => { return true; }); + } +} diff --git a/Pandora Example Plugin/Pandora Example Plugin.csproj b/Pandora Example Plugin/Pandora Example Plugin.csproj new file mode 100644 index 0000000..12ad1d3 --- /dev/null +++ b/Pandora Example Plugin/Pandora Example Plugin.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + true + ExamplePlugin + enable + enable + true + + + + + + False + runtime + + + + + + Always + + + + diff --git a/Pandora Example Plugin/Properties/launchSettings.json b/Pandora Example Plugin/Properties/launchSettings.json new file mode 100644 index 0000000..d6364a9 --- /dev/null +++ b/Pandora Example Plugin/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Test Plugin": { + "commandName": "Project", + "workingDirectory": "C:\\Users\\minth\\source\\repos\\Pandora-Behaviour-Engine-Plus\\Pandora Behaviour Engine\\bin\\Debug\\net8.0\\Plugins" + }, + "Profile 1": { + "commandName": "Executable", + "executablePath": "C:\\Users\\minth\\source\\repos\\Pandora-Behaviour-Engine-Plus\\Pandora Behaviour Engine\\bin\\Debug\\net8.0\\Pandora Behaviour Engine+.exe" + } + } +} \ No newline at end of file diff --git a/Pandora+.sln b/Pandora+.sln index 17d54f9..43af4dc 100644 --- a/Pandora+.sln +++ b/Pandora+.sln @@ -9,6 +9,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XML Cake", "XML.Cake.NET\XM EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HKX2E", "HKX2 Enhanced Library\HKX2\HKX2E.csproj", "{39BEC9FE-DE80-4D85-B6C4-2BAD740624E4}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pandora API", "Pandora API\Pandora API.csproj", "{9A0989E3-AE1A-4DE3-992F-B3FF96DAECAC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pandora Example Plugin", "Pandora Example Plugin\Pandora Example Plugin.csproj", "{18C77AB0-4DA4-4A1E-85EA-EC7A57629D3F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +31,14 @@ Global {39BEC9FE-DE80-4D85-B6C4-2BAD740624E4}.Debug|Any CPU.Build.0 = Debug|Any CPU {39BEC9FE-DE80-4D85-B6C4-2BAD740624E4}.Release|Any CPU.ActiveCfg = Release|Any CPU {39BEC9FE-DE80-4D85-B6C4-2BAD740624E4}.Release|Any CPU.Build.0 = Release|Any CPU + {9A0989E3-AE1A-4DE3-992F-B3FF96DAECAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9A0989E3-AE1A-4DE3-992F-B3FF96DAECAC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9A0989E3-AE1A-4DE3-992F-B3FF96DAECAC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9A0989E3-AE1A-4DE3-992F-B3FF96DAECAC}.Release|Any CPU.Build.0 = Release|Any CPU + {18C77AB0-4DA4-4A1E-85EA-EC7A57629D3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18C77AB0-4DA4-4A1E-85EA-EC7A57629D3F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18C77AB0-4DA4-4A1E-85EA-EC7A57629D3F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18C77AB0-4DA4-4A1E-85EA-EC7A57629D3F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE