mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-01-03 14:23:43 +01:00
This is going rather well.
This commit is contained in:
parent
73e2793da6
commit
bdaff7b781
48 changed files with 2944 additions and 2952 deletions
29
Penumbra/Services/BackupService.cs
Normal file
29
Penumbra/Services/BackupService.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Log;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Services;
|
||||
|
||||
public class BackupService
|
||||
{
|
||||
public BackupService(Logger logger, StartTracker timer, FilenameService fileNames)
|
||||
{
|
||||
using var t = timer.Measure(StartTimeType.Backup);
|
||||
var files = PenumbraFiles(fileNames);
|
||||
Backup.CreateBackup(logger, new DirectoryInfo(fileNames.ConfigDirectory), files);
|
||||
}
|
||||
|
||||
// Collect all relevant files for penumbra configuration.
|
||||
private static IReadOnlyList<FileInfo> PenumbraFiles(FilenameService fileNames)
|
||||
{
|
||||
var list = fileNames.CollectionFiles.ToList();
|
||||
list.AddRange(fileNames.LocalDataFiles);
|
||||
list.Add(new FileInfo(fileNames.ConfigFile));
|
||||
list.Add(new FileInfo(fileNames.FilesystemFile));
|
||||
list.Add(new FileInfo(fileNames.ActiveCollectionsFile));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
30
Penumbra/Services/CommunicatorService.cs
Normal file
30
Penumbra/Services/CommunicatorService.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Services;
|
||||
|
||||
public class CommunicatorService : IDisposable
|
||||
{
|
||||
/// <summary> <list type="number">
|
||||
/// <item>Parameter is the type of the changed collection. (Inactive or Temporary for additions or deletions)</item>
|
||||
/// <item>Parameter is the old collection, or null on additions.</item>
|
||||
/// <item>Parameter is the new collection, or null on deletions.</item>
|
||||
/// <item>Parameter is the display name for Individual collections or an empty string otherwise.</item>
|
||||
/// </list> </summary>
|
||||
public readonly EventWrapper<CollectionType, ModCollection?, ModCollection?, string> CollectionChange = new(nameof(CollectionChange));
|
||||
|
||||
/// <summary> <list type="number">
|
||||
/// <item>Parameter added, deleted or edited temporary mod.</item>
|
||||
/// <item>Parameter is whether the mod was newly created.</item>
|
||||
/// <item>Parameter is whether the mod was deleted.</item>
|
||||
/// </list> </summary>
|
||||
public readonly EventWrapper<Mod.TemporaryMod, bool, bool> TemporaryGlobalModChange = new(nameof(TemporaryGlobalModChange));
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CollectionChange.Dispose();
|
||||
TemporaryGlobalModChange.Dispose();
|
||||
}
|
||||
}
|
||||
377
Penumbra/Services/ConfigMigrationService.cs
Normal file
377
Penumbra/Services/ConfigMigrationService.cs
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Dalamud.Plugin;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui.Filesystem;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.UI.Classes;
|
||||
using SixLabors.ImageSharp;
|
||||
|
||||
namespace Penumbra.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Contains everything to migrate from older versions of the config to the current,
|
||||
/// including deprecated fields.
|
||||
/// </summary>
|
||||
public class ConfigMigrationService
|
||||
{
|
||||
private readonly FilenameService _fileNames;
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
|
||||
private Configuration _config = null!;
|
||||
private JObject _data = null!;
|
||||
|
||||
public string CurrentCollection = ModCollection.DefaultCollection;
|
||||
public string DefaultCollection = ModCollection.DefaultCollection;
|
||||
public string ForcedCollection = string.Empty;
|
||||
public Dictionary<string, string> CharacterCollections = new();
|
||||
public Dictionary<string, string> ModSortOrder = new();
|
||||
public bool InvertModListOrder;
|
||||
public bool SortFoldersFirst;
|
||||
public SortModeV3 SortMode = SortModeV3.FoldersFirst;
|
||||
|
||||
public ConfigMigrationService(FilenameService fileNames, DalamudPluginInterface pi)
|
||||
{
|
||||
_fileNames = fileNames;
|
||||
_pluginInterface = pi;
|
||||
}
|
||||
|
||||
/// <summary> Add missing colors to the dictionary if necessary. </summary>
|
||||
private static void AddColors(Configuration config, bool forceSave)
|
||||
{
|
||||
var save = false;
|
||||
foreach (var color in Enum.GetValues<ColorId>())
|
||||
{
|
||||
save |= config.Colors.TryAdd(color, color.Data().DefaultColor);
|
||||
}
|
||||
|
||||
if (save || forceSave)
|
||||
{
|
||||
config.Save();
|
||||
}
|
||||
}
|
||||
|
||||
public void Migrate(Configuration config)
|
||||
{
|
||||
_config = config;
|
||||
// Do this on every migration from now on for a while
|
||||
// because it stayed alive for a bunch of people for some reason.
|
||||
DeleteMetaTmp();
|
||||
|
||||
if (config.Version >= Configuration.Constants.CurrentVersion || !File.Exists(_fileNames.ConfigFile))
|
||||
{
|
||||
AddColors(config, false);
|
||||
return;
|
||||
}
|
||||
|
||||
_data = JObject.Parse(File.ReadAllText(_fileNames.ConfigFile));
|
||||
CreateBackup();
|
||||
|
||||
Version0To1();
|
||||
Version1To2();
|
||||
Version2To3();
|
||||
Version3To4();
|
||||
Version4To5();
|
||||
Version5To6();
|
||||
Version6To7();
|
||||
AddColors(config, true);
|
||||
}
|
||||
|
||||
// Gendered special collections were added.
|
||||
private void Version6To7()
|
||||
{
|
||||
if (_config.Version != 6)
|
||||
return;
|
||||
|
||||
ModCollection.Manager.MigrateUngenderedCollections(_fileNames);
|
||||
_config.Version = 7;
|
||||
}
|
||||
|
||||
|
||||
// A new tutorial step was inserted in the middle.
|
||||
// The UI collection and a new tutorial for it was added.
|
||||
// The migration for the UI collection itself happens in the ActiveCollections file.
|
||||
private void Version5To6()
|
||||
{
|
||||
if (_config.Version != 5)
|
||||
return;
|
||||
|
||||
if (_config.TutorialStep == 25)
|
||||
_config.TutorialStep = 27;
|
||||
|
||||
_config.Version = 6;
|
||||
}
|
||||
|
||||
// Mod backup extension was changed from .zip to .pmp.
|
||||
// Actual migration takes place in ModManager.
|
||||
private void Version4To5()
|
||||
{
|
||||
if (_config.Version != 4)
|
||||
return;
|
||||
|
||||
Mod.Manager.MigrateModBackups = true;
|
||||
_config.Version = 5;
|
||||
}
|
||||
|
||||
// SortMode was changed from an enum to a type.
|
||||
private void Version3To4()
|
||||
{
|
||||
if (_config.Version != 3)
|
||||
return;
|
||||
|
||||
SortMode = _data[nameof(SortMode)]?.ToObject<SortModeV3>() ?? SortMode;
|
||||
_config.SortMode = SortMode switch
|
||||
{
|
||||
SortModeV3.FoldersFirst => ISortMode<Mod>.FoldersFirst,
|
||||
SortModeV3.Lexicographical => ISortMode<Mod>.Lexicographical,
|
||||
SortModeV3.InverseFoldersFirst => ISortMode<Mod>.InverseFoldersFirst,
|
||||
SortModeV3.InverseLexicographical => ISortMode<Mod>.InverseLexicographical,
|
||||
SortModeV3.FoldersLast => ISortMode<Mod>.FoldersLast,
|
||||
SortModeV3.InverseFoldersLast => ISortMode<Mod>.InverseFoldersLast,
|
||||
SortModeV3.InternalOrder => ISortMode<Mod>.InternalOrder,
|
||||
SortModeV3.InternalOrderInverse => ISortMode<Mod>.InverseInternalOrder,
|
||||
_ => ISortMode<Mod>.FoldersFirst,
|
||||
};
|
||||
_config.Version = 4;
|
||||
}
|
||||
|
||||
// SortFoldersFirst was changed from a bool to the enum SortMode.
|
||||
private void Version2To3()
|
||||
{
|
||||
if (_config.Version != 2)
|
||||
return;
|
||||
|
||||
SortFoldersFirst = _data[nameof(SortFoldersFirst)]?.ToObject<bool>() ?? false;
|
||||
SortMode = SortFoldersFirst ? SortModeV3.FoldersFirst : SortModeV3.Lexicographical;
|
||||
_config.Version = 3;
|
||||
}
|
||||
|
||||
// The forced collection was removed due to general inheritance.
|
||||
// Sort Order was moved to a separate file and may contain empty folders.
|
||||
// Active collections in general were moved to their own file.
|
||||
// Delete the penumbrametatmp folder if it exists.
|
||||
private void Version1To2()
|
||||
{
|
||||
if (_config.Version != 1)
|
||||
return;
|
||||
|
||||
// Ensure the right meta files are loaded.
|
||||
DeleteMetaTmp();
|
||||
Penumbra.CharacterUtility.LoadCharacterResources();
|
||||
ResettleSortOrder();
|
||||
ResettleCollectionSettings();
|
||||
ResettleForcedCollection();
|
||||
_config.Version = 2;
|
||||
}
|
||||
|
||||
private void DeleteMetaTmp()
|
||||
{
|
||||
var path = Path.Combine(_config.ModDirectory, "penumbrametatmp");
|
||||
if (!Directory.Exists(path))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
Directory.Delete(path, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error($"Could not delete the outdated penumbrametatmp folder:\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ResettleForcedCollection()
|
||||
{
|
||||
ForcedCollection = _data[nameof(ForcedCollection)]?.ToObject<string>() ?? ForcedCollection;
|
||||
if (ForcedCollection.Length <= 0)
|
||||
return;
|
||||
|
||||
// Add the previous forced collection to all current collections except itself as an inheritance.
|
||||
foreach (var collection in _fileNames.CollectionFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
var jObject = JObject.Parse(File.ReadAllText(collection.FullName));
|
||||
if (jObject[nameof(ModCollection.Name)]?.ToObject<string>() == ForcedCollection)
|
||||
continue;
|
||||
|
||||
jObject[nameof(ModCollection.Inheritance)] = JToken.FromObject(new List<string> { ForcedCollection });
|
||||
File.WriteAllText(collection.FullName, jObject.ToString());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error(
|
||||
$"Could not transfer forced collection {ForcedCollection} to inheritance of collection {collection}:\n{e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move the current sort order to its own file.
|
||||
private void ResettleSortOrder()
|
||||
{
|
||||
ModSortOrder = _data[nameof(ModSortOrder)]?.ToObject<Dictionary<string, string>>() ?? ModSortOrder;
|
||||
var file = _fileNames.FilesystemFile;
|
||||
using var stream = File.Open(file, File.Exists(file) ? FileMode.Truncate : FileMode.CreateNew);
|
||||
using var writer = new StreamWriter(stream);
|
||||
using var j = new JsonTextWriter(writer);
|
||||
j.Formatting = Formatting.Indented;
|
||||
j.WriteStartObject();
|
||||
j.WritePropertyName("Data");
|
||||
j.WriteStartObject();
|
||||
foreach (var (mod, path) in ModSortOrder.Where(kvp => Directory.Exists(Path.Combine(_config.ModDirectory, kvp.Key))))
|
||||
{
|
||||
j.WritePropertyName(mod, true);
|
||||
j.WriteValue(path);
|
||||
}
|
||||
|
||||
j.WriteEndObject();
|
||||
j.WritePropertyName("EmptyFolders");
|
||||
j.WriteStartArray();
|
||||
j.WriteEndArray();
|
||||
j.WriteEndObject();
|
||||
}
|
||||
|
||||
// Move the active collections to their own file.
|
||||
private void ResettleCollectionSettings()
|
||||
{
|
||||
CurrentCollection = _data[nameof(CurrentCollection)]?.ToObject<string>() ?? CurrentCollection;
|
||||
DefaultCollection = _data[nameof(DefaultCollection)]?.ToObject<string>() ?? DefaultCollection;
|
||||
CharacterCollections = _data[nameof(CharacterCollections)]?.ToObject<Dictionary<string, string>>() ?? CharacterCollections;
|
||||
SaveActiveCollectionsV0(DefaultCollection, CurrentCollection, DefaultCollection,
|
||||
CharacterCollections.Select(kvp => (kvp.Key, kvp.Value)), Array.Empty<(CollectionType, string)>());
|
||||
}
|
||||
|
||||
// Outdated saving using the Characters list.
|
||||
private void SaveActiveCollectionsV0(string def, string ui, string current, IEnumerable<(string, string)> characters,
|
||||
IEnumerable<(CollectionType, string)> special)
|
||||
{
|
||||
var file = _fileNames.ActiveCollectionsFile;
|
||||
try
|
||||
{
|
||||
using var stream = File.Open(file, File.Exists(file) ? FileMode.Truncate : FileMode.CreateNew);
|
||||
using var writer = new StreamWriter(stream);
|
||||
using var j = new JsonTextWriter(writer);
|
||||
j.Formatting = Formatting.Indented;
|
||||
j.WriteStartObject();
|
||||
j.WritePropertyName(nameof(ModCollection.Manager.Default));
|
||||
j.WriteValue(def);
|
||||
j.WritePropertyName(nameof(ModCollection.Manager.Interface));
|
||||
j.WriteValue(ui);
|
||||
j.WritePropertyName(nameof(ModCollection.Manager.Current));
|
||||
j.WriteValue(current);
|
||||
foreach (var (type, collection) in special)
|
||||
{
|
||||
j.WritePropertyName(type.ToString());
|
||||
j.WriteValue(collection);
|
||||
}
|
||||
|
||||
j.WritePropertyName("Characters");
|
||||
j.WriteStartObject();
|
||||
foreach (var (character, collection) in characters)
|
||||
{
|
||||
j.WritePropertyName(character, true);
|
||||
j.WriteValue(collection);
|
||||
}
|
||||
|
||||
j.WriteEndObject();
|
||||
j.WriteEndObject();
|
||||
Penumbra.Log.Verbose("Active Collections saved.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error($"Could not save active collections to file {file}:\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
// Collections were introduced and the previous CurrentCollection got put into ModDirectory.
|
||||
private void Version0To1()
|
||||
{
|
||||
if (_config.Version != 0)
|
||||
return;
|
||||
|
||||
_config.ModDirectory = _data[nameof(CurrentCollection)]?.ToObject<string>() ?? string.Empty;
|
||||
_config.Version = 1;
|
||||
ResettleCollectionJson();
|
||||
}
|
||||
|
||||
// Move the previous mod configurations to a new default collection file.
|
||||
private void ResettleCollectionJson()
|
||||
{
|
||||
var collectionJson = new FileInfo(Path.Combine(_config.ModDirectory, "collection.json"));
|
||||
if (!collectionJson.Exists)
|
||||
return;
|
||||
|
||||
var defaultCollection = ModCollection.CreateNewEmpty(ModCollection.DefaultCollection);
|
||||
var defaultCollectionFile = defaultCollection.FileName;
|
||||
if (defaultCollectionFile.Exists)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var text = File.ReadAllText(collectionJson.FullName);
|
||||
var data = JArray.Parse(text);
|
||||
|
||||
var maxPriority = 0;
|
||||
var dict = new Dictionary<string, ModSettings.SavedSettings>();
|
||||
foreach (var setting in data.Cast<JObject>())
|
||||
{
|
||||
var modName = (string)setting["FolderName"]!;
|
||||
var enabled = (bool)setting["Enabled"]!;
|
||||
var priority = (int)setting["Priority"]!;
|
||||
var settings = setting["Settings"]!.ToObject<Dictionary<string, long>>()
|
||||
?? setting["Conf"]!.ToObject<Dictionary<string, long>>();
|
||||
|
||||
dict[modName] = new ModSettings.SavedSettings()
|
||||
{
|
||||
Enabled = enabled,
|
||||
Priority = priority,
|
||||
Settings = settings!,
|
||||
};
|
||||
maxPriority = Math.Max(maxPriority, priority);
|
||||
}
|
||||
|
||||
InvertModListOrder = _data[nameof(InvertModListOrder)]?.ToObject<bool>() ?? InvertModListOrder;
|
||||
if (!InvertModListOrder)
|
||||
dict = dict.ToDictionary(kvp => kvp.Key, kvp => kvp.Value with { Priority = maxPriority - kvp.Value.Priority });
|
||||
|
||||
defaultCollection = ModCollection.MigrateFromV0(ModCollection.DefaultCollection, dict);
|
||||
defaultCollection.Save();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error($"Could not migrate the old collection file to new collection files:\n{e}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a backup of the configuration file specifically.
|
||||
private void CreateBackup()
|
||||
{
|
||||
var name = _fileNames.ConfigFile;
|
||||
var bakName = name + ".bak";
|
||||
try
|
||||
{
|
||||
File.Copy(name, bakName, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error($"Could not create backup copy of config at {bakName}:\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
public enum SortModeV3 : byte
|
||||
{
|
||||
FoldersFirst = 0x00,
|
||||
Lexicographical = 0x01,
|
||||
InverseFoldersFirst = 0x02,
|
||||
InverseLexicographical = 0x03,
|
||||
FoldersLast = 0x04,
|
||||
InverseFoldersLast = 0x05,
|
||||
InternalOrder = 0x06,
|
||||
InternalOrderInverse = 0x07,
|
||||
}
|
||||
}
|
||||
|
|
@ -78,21 +78,22 @@ public class DalamudServices
|
|||
services.AddSingleton(SigScanner);
|
||||
services.AddSingleton(this);
|
||||
}
|
||||
|
||||
|
||||
// TODO remove static
|
||||
// @formatter:off
|
||||
[PluginService][RequiredVersion("1.0")] public DalamudPluginInterface PluginInterface { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public CommandManager Commands { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public DataManager GameData { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public ClientState ClientState { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public ChatGui Chat { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public Framework Framework { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public Condition Conditions { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public TargetManager Targets { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public ObjectTable Objects { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public TitleScreenMenu TitleScreenMenu { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public GameGui GameGui { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public KeyState KeyState { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public SigScanner SigScanner { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public static DalamudPluginInterface PluginInterface { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public static CommandManager Commands { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public static DataManager GameData { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public static ClientState ClientState { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public static ChatGui Chat { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public static Framework Framework { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public static Condition Conditions { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public static TargetManager Targets { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public static ObjectTable Objects { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public static TitleScreenMenu TitleScreenMenu { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public static GameGui GameGui { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public static KeyState KeyState { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public static SigScanner SigScanner { get; private set; } = null!;
|
||||
// @formatter:on
|
||||
|
||||
public const string WaitingForPluginsOption = "IsResumeGameAfterPluginLoad";
|
||||
|
|
|
|||
51
Penumbra/Services/FilenameService.cs
Normal file
51
Penumbra/Services/FilenameService.cs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Dalamud.Plugin;
|
||||
using OtterGui.Filesystem;
|
||||
|
||||
namespace Penumbra.Services;
|
||||
|
||||
public class FilenameService
|
||||
{
|
||||
public readonly string ConfigDirectory;
|
||||
public readonly string CollectionDirectory;
|
||||
public readonly string LocalDataDirectory;
|
||||
public readonly string ConfigFile;
|
||||
public readonly string FilesystemFile;
|
||||
public readonly string ActiveCollectionsFile;
|
||||
|
||||
public FilenameService(DalamudPluginInterface pi)
|
||||
{
|
||||
ConfigDirectory = pi.ConfigDirectory.FullName;
|
||||
CollectionDirectory = Path.Combine(pi.GetPluginConfigDirectory(), "collections");
|
||||
LocalDataDirectory = Path.Combine(pi.ConfigDirectory.FullName, "mod_data");
|
||||
ConfigFile = pi.ConfigFile.FullName;
|
||||
FilesystemFile = Path.Combine(pi.GetPluginConfigDirectory(), "sort_order.json");
|
||||
ActiveCollectionsFile = Path.Combine(pi.ConfigDirectory.FullName, "active_collections.json");
|
||||
}
|
||||
|
||||
public string CollectionFile(string collectionName)
|
||||
=> Path.Combine(CollectionDirectory, $"{collectionName.RemoveInvalidPathSymbols()}.json");
|
||||
|
||||
public string LocalDataFile(string modPath)
|
||||
=> Path.Combine(LocalDataDirectory, $"{modPath}.json");
|
||||
|
||||
public IEnumerable<FileInfo> CollectionFiles
|
||||
{
|
||||
get
|
||||
{
|
||||
var directory = new DirectoryInfo(CollectionDirectory);
|
||||
return directory.Exists ? directory.EnumerateFiles("*.json") : Array.Empty<FileInfo>();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<FileInfo> LocalDataFiles
|
||||
{
|
||||
get
|
||||
{
|
||||
var directory = new DirectoryInfo(LocalDataDirectory);
|
||||
return directory.Exists ? directory.EnumerateFiles("*.json") : Array.Empty<FileInfo>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Plugin;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Util;
|
||||
using Action = System.Action;
|
||||
|
||||
namespace Penumbra.Services;
|
||||
|
||||
public sealed class ObjectIdentifier : IObjectIdentifier
|
||||
{
|
||||
private const string Prefix = $"[{nameof(ObjectIdentifier)}]";
|
||||
|
||||
public IObjectIdentifier? Identifier { get; private set; }
|
||||
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
public bool Ready
|
||||
=> Identifier != null && !IsDisposed;
|
||||
|
||||
public event Action? FinishedCreation;
|
||||
|
||||
public ObjectIdentifier(StartTimeTracker<StartTimeType> tracker, DalamudPluginInterface pi, DataManager data)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
using var timer = tracker.Measure(StartTimeType.Identifier);
|
||||
var identifier = GameData.GameData.GetIdentifier(pi, data);
|
||||
if (IsDisposed)
|
||||
{
|
||||
identifier.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
Identifier = identifier;
|
||||
Penumbra.Log.Verbose($"{Prefix} Created.");
|
||||
FinishedCreation?.Invoke();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Identifier?.Dispose();
|
||||
IsDisposed = true;
|
||||
Penumbra.Log.Verbose($"{Prefix} Disposed.");
|
||||
}
|
||||
|
||||
public IGamePathParser GamePathParser
|
||||
=> Identifier?.GamePathParser ?? throw new Exception($"{Prefix} Not yet ready.");
|
||||
|
||||
public void Identify(IDictionary<string, object?> set, string path)
|
||||
=> Identifier?.Identify(set, path);
|
||||
|
||||
public Dictionary<string, object?> Identify(string path)
|
||||
=> Identifier?.Identify(path) ?? new Dictionary<string, object?>();
|
||||
|
||||
public IEnumerable<Item> Identify(SetId setId, WeaponType weaponType, ushort variant, EquipSlot slot)
|
||||
=> Identifier?.Identify(setId, weaponType, variant, slot) ?? Array.Empty<Item>();
|
||||
}
|
||||
117
Penumbra/Services/ServiceWrapper.cs
Normal file
117
Penumbra/Services/ServiceWrapper.cs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Services;
|
||||
|
||||
public interface IServiceWrapper<out T> : IDisposable
|
||||
{
|
||||
public string Name { get; }
|
||||
public T? Service { get; }
|
||||
public bool Valid { get; }
|
||||
}
|
||||
|
||||
public abstract class SyncServiceWrapper<T> : IServiceWrapper<T>
|
||||
{
|
||||
public string Name { get; }
|
||||
public T Service { get; }
|
||||
private bool _isDisposed;
|
||||
|
||||
public bool Valid
|
||||
=> !_isDisposed;
|
||||
|
||||
protected SyncServiceWrapper(string name, StartTracker tracker, StartTimeType type, Func<T> factory)
|
||||
{
|
||||
Name = name;
|
||||
using var timer = tracker.Measure(type);
|
||||
Service = factory();
|
||||
Penumbra.Log.Verbose($"[{Name}] Created.");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
||||
return;
|
||||
|
||||
_isDisposed = true;
|
||||
if (Service is IDisposable d)
|
||||
d.Dispose();
|
||||
Penumbra.Log.Verbose($"[{Name}] Disposed.");
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class AsyncServiceWrapper<T> : IServiceWrapper<T>
|
||||
{
|
||||
public string Name { get; }
|
||||
public T? Service { get; private set; }
|
||||
|
||||
public T AwaitedService
|
||||
{
|
||||
get
|
||||
{
|
||||
_task.Wait();
|
||||
return Service!;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Valid
|
||||
=> Service != null && !_isDisposed;
|
||||
|
||||
public event Action? FinishedCreation;
|
||||
private readonly Task _task;
|
||||
|
||||
private bool _isDisposed;
|
||||
|
||||
protected AsyncServiceWrapper(string name, StartTracker tracker, StartTimeType type, Func<T> factory)
|
||||
{
|
||||
Name = name;
|
||||
_task = Task.Run(() =>
|
||||
{
|
||||
using var timer = tracker.Measure(type);
|
||||
var service = factory();
|
||||
if (_isDisposed)
|
||||
{
|
||||
if (service is IDisposable d)
|
||||
d.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
Service = service;
|
||||
Penumbra.Log.Verbose($"[{Name}] Created.");
|
||||
FinishedCreation?.Invoke();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected AsyncServiceWrapper(string name, Func<T> factory)
|
||||
{
|
||||
Name = name;
|
||||
_task = Task.Run(() =>
|
||||
{
|
||||
var service = factory();
|
||||
if (_isDisposed)
|
||||
{
|
||||
if (service is IDisposable d)
|
||||
d.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
Service = service;
|
||||
Penumbra.Log.Verbose($"[{Name}] Created.");
|
||||
FinishedCreation?.Invoke();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
||||
return;
|
||||
|
||||
_isDisposed = true;
|
||||
if (Service is IDisposable d)
|
||||
d.Dispose();
|
||||
Penumbra.Log.Verbose($"[{Name}] Disposed.");
|
||||
}
|
||||
}
|
||||
43
Penumbra/Services/StainService.cs
Normal file
43
Penumbra/Services/StainService.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Plugin;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Widgets;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.GameData.Files;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Services;
|
||||
|
||||
public class StainService : IDisposable
|
||||
{
|
||||
public sealed class StainTemplateCombo : FilterComboCache<ushort>
|
||||
{
|
||||
public StainTemplateCombo(IEnumerable<ushort> items)
|
||||
: base(items)
|
||||
{ }
|
||||
}
|
||||
|
||||
public readonly StainData StainData;
|
||||
public readonly FilterComboColors StainCombo;
|
||||
public readonly StmFile StmFile;
|
||||
public readonly StainTemplateCombo TemplateCombo;
|
||||
|
||||
public StainService(StartTracker timer, DalamudPluginInterface pluginInterface, DataManager dataManager)
|
||||
{
|
||||
using var t = timer.Measure(StartTimeType.Stains);
|
||||
StainData = new StainData(pluginInterface, dataManager, dataManager.Language);
|
||||
StainCombo = new FilterComboColors(140, StainData.Data.Prepend(new KeyValuePair<byte, (string Name, uint Dye, bool Gloss)>(0, ("None", 0, false))));
|
||||
StmFile = new StmFile(dataManager);
|
||||
TemplateCombo = new StainTemplateCombo(StmFile.Entries.Keys.Prepend((ushort)0));
|
||||
Penumbra.Log.Verbose($"[{nameof(StainService)}] Created.");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
StainData.Dispose();
|
||||
Penumbra.Log.Verbose($"[{nameof(StainService)}] Disposed.");
|
||||
}
|
||||
}
|
||||
37
Penumbra/Services/Wrappers.cs
Normal file
37
Penumbra/Services/Wrappers.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
using Dalamud.Data;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.Plugin;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.Interop.Resolver;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Services;
|
||||
|
||||
public sealed class IdentifierService : AsyncServiceWrapper<IObjectIdentifier>
|
||||
{
|
||||
public IdentifierService(StartTracker tracker, DalamudPluginInterface pi, DataManager data)
|
||||
: base(nameof(IdentifierService), tracker, StartTimeType.Identifier, () => GameData.GameData.GetIdentifier(pi, data))
|
||||
{ }
|
||||
}
|
||||
|
||||
public sealed class ItemService : AsyncServiceWrapper<ItemData>
|
||||
{
|
||||
public ItemService(StartTracker tracker, DalamudPluginInterface pi, DataManager gameData)
|
||||
: base(nameof(ItemService), tracker, StartTimeType.Items, () => new ItemData(pi, gameData, gameData.Language))
|
||||
{ }
|
||||
}
|
||||
|
||||
public sealed class ActorService : AsyncServiceWrapper<ActorManager>
|
||||
{
|
||||
public ActorService(StartTracker tracker, DalamudPluginInterface pi, ObjectTable objects, ClientState clientState,
|
||||
Framework framework, DataManager gameData, GameGui gui, CutsceneCharacters cutscene)
|
||||
: base(nameof(ActorService), tracker, StartTimeType.Actors,
|
||||
() => new ActorManager(pi, objects, clientState, framework, gameData, gui, idx => (short)cutscene.GetParentIndex(idx)))
|
||||
{ }
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue