Penumbra/Penumbra/Mods/ModCollection.cs
2021-09-01 22:04:20 +02:00

250 lines
No EOL
7.8 KiB
C#

using Dalamud.Plugin;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Dalamud.Logging;
using Penumbra.GameData.Util;
using Penumbra.Interop;
using Penumbra.Mod;
using Penumbra.Util;
namespace Penumbra.Mods
{
// A ModCollection is a named set of ModSettings to all of the users' installed mods.
// It is meant to be local only, and thus should always contain settings for every mod, not just the enabled ones.
// Settings to mods that are not installed anymore are kept as long as no call to CleanUnavailableSettings is made.
// Active ModCollections build a cache of currently relevant data.
public class ModCollection
{
public const string DefaultCollection = "Default";
public string Name { get; set; }
public Dictionary< string, ModSettings > Settings { get; }
public ModCollection()
{
Name = DefaultCollection;
Settings = new Dictionary< string, ModSettings >();
}
public ModCollection( string name, Dictionary< string, ModSettings > settings )
{
Name = name;
Settings = settings.ToDictionary( kvp => kvp.Key, kvp => kvp.Value.DeepCopy() );
}
public Mod.Mod GetMod( ModData mod )
{
if( Cache != null && Cache.AvailableMods.TryGetValue( mod.BasePath.Name, out var ret ) )
{
return ret;
}
if( Settings.TryGetValue( mod.BasePath.Name, out var settings ) )
{
return new Mod.Mod( settings, mod );
}
var newSettings = ModSettings.DefaultSettings( mod.Meta );
Settings.Add( mod.BasePath.Name, newSettings );
Save();
return new Mod.Mod( newSettings, mod );
}
private bool CleanUnavailableSettings( Dictionary< string, ModData > data )
{
var removeList = Settings.Where( settingKvp => !data.ContainsKey( settingKvp.Key ) ).ToArray();
foreach( var s in removeList )
{
Settings.Remove( s.Key );
}
return removeList.Length > 0;
}
public void CreateCache( DirectoryInfo modDirectory, IEnumerable< ModData > data )
{
Cache = new ModCollectionCache( Name, modDirectory );
var changedSettings = false;
foreach( var mod in data )
{
if( Settings.TryGetValue( mod.BasePath.Name, out var settings ) )
{
Cache.AddMod( settings, mod, false );
}
else
{
changedSettings = true;
var newSettings = ModSettings.DefaultSettings( mod.Meta );
Settings.Add( mod.BasePath.Name, newSettings );
Cache.AddMod( newSettings, mod, false );
}
}
if( changedSettings )
{
Save();
}
CalculateEffectiveFileList( modDirectory, true, false );
}
public void ClearCache()
=> Cache = null;
public void UpdateSetting( ModData mod )
{
if( !Settings.TryGetValue( mod.BasePath.Name, out var settings ) )
{
return;
}
if( settings.FixInvalidSettings( mod.Meta ) )
{
Save();
}
}
public void UpdateSettings()
{
if( Cache == null )
{
return;
}
var changes = false;
foreach( var mod in Cache.AvailableMods.Values )
{
changes |= mod.FixSettings();
}
if( changes )
{
Save();
}
}
public void CalculateEffectiveFileList( DirectoryInfo modDir, bool withMetaManipulations, bool activeCollection )
{
PluginLog.Debug( "Recalculating effective file list for {CollectionName} [{WithMetaManipulations}] [{IsActiveCollection}]", Name,
withMetaManipulations, activeCollection );
Cache ??= new ModCollectionCache( Name, modDir );
UpdateSettings();
Cache.CalculateEffectiveFileList();
if( withMetaManipulations )
{
Cache.UpdateMetaManipulations();
if( activeCollection )
{
Service< ResidentResources >.Get().ReloadPlayerResources();
}
}
}
[JsonIgnore]
public ModCollectionCache? Cache { get; private set; }
public static ModCollection? LoadFromFile( FileInfo file )
{
if( !file.Exists )
{
PluginLog.Error( $"Could not read collection because {file.FullName} does not exist." );
return null;
}
try
{
var collection = JsonConvert.DeserializeObject< ModCollection >( File.ReadAllText( file.FullName ) );
return collection;
}
catch( Exception e )
{
PluginLog.Error( $"Could not read collection information from {file.FullName}:\n{e}" );
}
return null;
}
private void SaveToFile( FileInfo file )
{
try
{
File.WriteAllText( file.FullName, JsonConvert.SerializeObject( this, Formatting.Indented ) );
}
catch( Exception e )
{
PluginLog.Error( $"Could not write collection {Name} to {file.FullName}:\n{e}" );
}
}
public static DirectoryInfo CollectionDir()
=> new( Path.Combine( Dalamud.PluginInterface.GetPluginConfigDirectory(), "collections" ) );
private static FileInfo FileName( DirectoryInfo collectionDir, string name )
=> new( Path.Combine( collectionDir.FullName, $"{name.RemoveInvalidPathSymbols()}.json" ) );
public FileInfo FileName()
=> new( Path.Combine( Service< DalamudPluginInterface >.Get().GetPluginConfigDirectory(),
$"{Name.RemoveInvalidPathSymbols()}.json" ) );
public void Save()
{
try
{
var dir = CollectionDir();
dir.Create();
var file = FileName( dir, Name );
SaveToFile( file );
}
catch( Exception e )
{
PluginLog.Error( $"Could not save collection {Name}:\n{e}" );
}
}
public static ModCollection? Load( string name )
{
var file = FileName( CollectionDir(), name );
return file.Exists ? LoadFromFile( file ) : null;
}
public void Delete()
{
var file = FileName( CollectionDir(), Name );
if( file.Exists )
{
try
{
file.Delete();
}
catch( Exception e )
{
PluginLog.Error( $"Could not delete collection file {file} for {Name}:\n{e}" );
}
}
}
public void AddMod( ModData data )
{
if( Cache == null )
{
return;
}
Cache.AddMod( Settings.TryGetValue( data.BasePath.Name, out var settings )
? settings
: ModSettings.DefaultSettings( data.Meta ),
data );
}
public string? ResolveSwappedOrReplacementPath( GamePath gameResourcePath )
=> Cache?.ResolveSwappedOrReplacementPath( gameResourcePath );
public static readonly ModCollection Empty = new() { Name = "" };
}
}