mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-01-01 05:13:43 +01:00
A few comments, further cleanup. A few TODOs handled.
This commit is contained in:
parent
dbb9931189
commit
c78725d7d5
47 changed files with 347 additions and 3664 deletions
|
|
@ -12,14 +12,19 @@ public partial class Mod
|
|||
public delegate void ModPathChangeDelegate( ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory,
|
||||
DirectoryInfo? newDirectory );
|
||||
|
||||
public event ModPathChangeDelegate? ModPathChanged;
|
||||
public event ModPathChangeDelegate ModPathChanged;
|
||||
|
||||
// Rename/Move a mod directory.
|
||||
// Updates all collection settings and sort order settings.
|
||||
public void MoveModDirectory( Index idx, DirectoryInfo newDirectory )
|
||||
{
|
||||
var mod = this[ idx ];
|
||||
// TODO
|
||||
}
|
||||
|
||||
// Delete a mod by its index.
|
||||
// Deletes from filesystem as well as from internal data.
|
||||
// Updates indices of later mods.
|
||||
public void DeleteMod( int idx )
|
||||
{
|
||||
var mod = this[ idx ];
|
||||
|
|
@ -41,9 +46,10 @@ public partial class Mod
|
|||
--remainingMod.Index;
|
||||
}
|
||||
|
||||
ModPathChanged?.Invoke( ModPathChangeType.Deleted, mod, mod.BasePath, null );
|
||||
ModPathChanged.Invoke( ModPathChangeType.Deleted, mod, mod.BasePath, null );
|
||||
}
|
||||
|
||||
// Load a new mod and add it to the manager if successful.
|
||||
public void AddMod( DirectoryInfo modFolder )
|
||||
{
|
||||
if( _mods.Any( m => m.BasePath.Name == modFolder.Name ) )
|
||||
|
|
@ -59,7 +65,22 @@ public partial class Mod
|
|||
|
||||
mod.Index = _mods.Count;
|
||||
_mods.Add( mod );
|
||||
ModPathChanged?.Invoke( ModPathChangeType.Added, mod, null, mod.BasePath );
|
||||
ModPathChanged.Invoke( ModPathChangeType.Added, mod, null, mod.BasePath );
|
||||
}
|
||||
|
||||
// Add new mods to NewMods and remove deleted mods from NewMods.
|
||||
private void OnModPathChange( ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory,
|
||||
DirectoryInfo? newDirectory )
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case ModPathChangeType.Added:
|
||||
NewMods.Add( mod );
|
||||
break;
|
||||
case ModPathChangeType.Deleted:
|
||||
NewMods.Remove( mod );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -197,6 +197,14 @@ public sealed partial class Mod
|
|||
return;
|
||||
}
|
||||
|
||||
if( mod._groups[ groupIdx ].Count > 63 )
|
||||
{
|
||||
PluginLog.Error(
|
||||
$"Could not add option {option.Name} to {mod._groups[ groupIdx ].Name} for mod {mod.Name}, "
|
||||
+ "since only up to 64 options are supported in one group." );
|
||||
return;
|
||||
}
|
||||
|
||||
switch( mod._groups[ groupIdx ] )
|
||||
{
|
||||
case SingleModGroup s:
|
||||
|
|
|
|||
|
|
@ -11,16 +11,19 @@ public sealed partial class Mod
|
|||
public DirectoryInfo BasePath { get; private set; } = null!;
|
||||
public bool Valid { get; private set; }
|
||||
|
||||
|
||||
public event Action? ModDiscoveryStarted;
|
||||
public event Action? ModDiscoveryFinished;
|
||||
|
||||
// Change the mod base directory and discover available mods.
|
||||
public void DiscoverMods( string newDir )
|
||||
{
|
||||
SetBaseDirectory( newDir, false );
|
||||
DiscoverMods();
|
||||
}
|
||||
|
||||
// Set the mod base directory.
|
||||
// If its not the first time, check if it is the same directory as before.
|
||||
// Also checks if the directory is available and tries to create it if it is not.
|
||||
private void SetBaseDirectory( string newPath, bool firstTime )
|
||||
{
|
||||
if( !firstTime && string.Equals( newPath, Penumbra.Config.ModDirectory, StringComparison.InvariantCultureIgnoreCase ) )
|
||||
|
|
@ -59,8 +62,10 @@ public sealed partial class Mod
|
|||
}
|
||||
}
|
||||
|
||||
// Discover new mods.
|
||||
public void DiscoverMods()
|
||||
{
|
||||
NewMods.Clear();
|
||||
ModDiscoveryStarted?.Invoke();
|
||||
_mods.Clear();
|
||||
BasePath.Refresh();
|
||||
|
|
|
|||
|
|
@ -6,16 +6,22 @@ namespace Penumbra.Mods;
|
|||
|
||||
public sealed partial class Mod
|
||||
{
|
||||
public sealed partial class Manager : IEnumerable< Mod >
|
||||
public sealed partial class Manager : IReadOnlyList< Mod >
|
||||
{
|
||||
// An easily accessible set of new mods.
|
||||
// Mods are added when they are created or imported.
|
||||
// Mods are removed when they are deleted or when they are toggled in any collection.
|
||||
// Also gets cleared on mod rediscovery.
|
||||
public readonly HashSet< Mod > NewMods = new();
|
||||
|
||||
private readonly List< Mod > _mods = new();
|
||||
|
||||
public Mod this[ int idx ]
|
||||
=> _mods[ idx ];
|
||||
|
||||
public Mod this[ Index idx ]
|
||||
=> _mods[ idx ];
|
||||
|
||||
public IReadOnlyList< Mod > Mods
|
||||
=> _mods;
|
||||
|
||||
public int Count
|
||||
=> _mods.Count;
|
||||
|
||||
|
|
@ -29,6 +35,7 @@ public sealed partial class Mod
|
|||
{
|
||||
SetBaseDirectory( modDirectory, true );
|
||||
ModOptionChanged += OnModOptionChange;
|
||||
ModPathChanged += OnModPathChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ public partial class Mod
|
|||
private Mod( DirectoryInfo basePath )
|
||||
=> BasePath = basePath;
|
||||
|
||||
public static Mod? LoadMod( DirectoryInfo basePath )
|
||||
private static Mod? LoadMod( DirectoryInfo basePath )
|
||||
{
|
||||
basePath.Refresh();
|
||||
if( !basePath.Exists )
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ public sealed partial class Mod
|
|||
public SortedList< string, object? > ChangedItems { get; } = new();
|
||||
public string LowerChangedItemsString { get; private set; } = string.Empty;
|
||||
|
||||
public void ComputeChangedItems()
|
||||
private void ComputeChangedItems()
|
||||
{
|
||||
var identifier = GameData.GameData.GetIdentifier();
|
||||
ChangedItems.Clear();
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ public partial class Mod
|
|||
public List< FullPath > FindMissingFiles()
|
||||
=> AllFiles.Where( f => !f.Exists ).ToList();
|
||||
|
||||
public static IModGroup? LoadModGroup( FileInfo file, DirectoryInfo basePath )
|
||||
private static IModGroup? LoadModGroup( FileInfo file, DirectoryInfo basePath )
|
||||
{
|
||||
if( !File.Exists( file.FullName ) )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,13 +25,13 @@ public sealed partial class Mod
|
|||
{
|
||||
public const uint CurrentFileVersion = 1;
|
||||
public uint FileVersion { get; private set; } = CurrentFileVersion;
|
||||
public LowerString Name { get; private set; } = "Mod";
|
||||
public LowerString Name { get; private set; } = "New Mod";
|
||||
public LowerString Author { get; private set; } = LowerString.Empty;
|
||||
public string Description { get; private set; } = string.Empty;
|
||||
public string Version { get; private set; } = string.Empty;
|
||||
public string Website { get; private set; } = string.Empty;
|
||||
|
||||
private FileInfo MetaFile
|
||||
internal FileInfo MetaFile
|
||||
=> new(Path.Combine( BasePath.FullName, "meta.json" ));
|
||||
|
||||
private MetaChangeType LoadMeta()
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using Penumbra.Util;
|
|||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
// TODO Everything
|
||||
//ublic class ModCleanup
|
||||
//
|
||||
// private const string Duplicates = "Duplicates";
|
||||
|
|
@ -521,7 +522,6 @@ namespace Penumbra.Mods;
|
|||
// }
|
||||
// }
|
||||
//
|
||||
// // TODO
|
||||
// var idx = Penumbra.ModManager.Mods.IndexOf( m => m.Meta == meta );
|
||||
// foreach( var collection in Penumbra.CollectionManager )
|
||||
// {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public sealed class ModFileSystem : FileSystem< Mod >, IDisposable
|
|||
// Used on construction and on mod rediscoveries.
|
||||
private void Reload()
|
||||
{
|
||||
if( Load( new FileInfo( ModFileSystemFile ), Penumbra.ModManager.Mods, ModToIdentifier, ModToName ) )
|
||||
if( Load( new FileInfo( ModFileSystemFile ), Penumbra.ModManager, ModToIdentifier, ModToName ) )
|
||||
{
|
||||
Save();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,15 @@ using System.IO;
|
|||
using Dalamud.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using OtterGui.Filesystem;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
public enum SelectType
|
||||
{
|
||||
Single,
|
||||
Multi,
|
||||
}
|
||||
|
||||
public interface IModGroup : IEnumerable< ISubMod >
|
||||
{
|
||||
public string Name { get; }
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ namespace Penumbra.Mods;
|
|||
|
||||
public partial class Mod
|
||||
{
|
||||
// Groups that allow all available options to be selected at once.
|
||||
private sealed class MultiModGroup : IModGroup
|
||||
{
|
||||
public SelectType Type
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ namespace Penumbra.Mods;
|
|||
|
||||
public partial class Mod
|
||||
{
|
||||
// Groups that allow only one of their available options to be selected.
|
||||
private sealed class SingleModGroup : IModGroup
|
||||
{
|
||||
public SelectType Type
|
||||
|
|
|
|||
|
|
@ -13,9 +13,11 @@ namespace Penumbra.Mods;
|
|||
|
||||
public partial class Mod
|
||||
{
|
||||
private string DefaultFile
|
||||
internal string DefaultFile
|
||||
=> Path.Combine( BasePath.FullName, "default_mod.json" );
|
||||
|
||||
// The default mod contains setting-independent sets of file replacements, file swaps and meta changes.
|
||||
// Every mod has an default mod, though it may be empty.
|
||||
private void SaveDefaultMod()
|
||||
{
|
||||
var defaultFile = DefaultFile;
|
||||
|
|
@ -55,6 +57,14 @@ public partial class Mod
|
|||
}
|
||||
|
||||
|
||||
// A sub mod is a collection of
|
||||
// - file replacements
|
||||
// - file swaps
|
||||
// - meta manipulations
|
||||
// that can be used either as an option or as the default data for a mod.
|
||||
// It can be loaded and reloaded from Json.
|
||||
// Nothing is checked for existence or validity when loading.
|
||||
// Objects are also not checked for uniqueness, the first appearance of a game path or meta path decides.
|
||||
private sealed class SubMod : ISubMod
|
||||
{
|
||||
public string Name { get; set; } = "Default";
|
||||
|
|
@ -78,6 +88,7 @@ public partial class Mod
|
|||
FileSwapData.Clear();
|
||||
ManipulationData.Clear();
|
||||
|
||||
// Every option has a name, but priorities are only relevant for multi group options.
|
||||
Name = json[ nameof( ISubMod.Name ) ]?.ToObject< string >() ?? string.Empty;
|
||||
priority = json[ nameof( IModGroup.Priority ) ]?.ToObject< int >() ?? 0;
|
||||
|
||||
|
|
@ -115,6 +126,8 @@ public partial class Mod
|
|||
}
|
||||
}
|
||||
|
||||
// If .meta or .rgsp files are encountered, parse them and incorporate their meta changes into the mod.
|
||||
// If delete is true, the files are deleted afterwards.
|
||||
public void IncorporateMetaChanges( DirectoryInfo basePath, bool delete )
|
||||
{
|
||||
foreach( var (key, file) in Files.ToList() )
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ public class ModSettings
|
|||
public int Priority { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
// Create an independent copy of the current settings.
|
||||
public ModSettings DeepCopy()
|
||||
=> new()
|
||||
{
|
||||
|
|
@ -22,6 +23,7 @@ public class ModSettings
|
|||
Settings = Settings.ToList(),
|
||||
};
|
||||
|
||||
// Create default settings for a given mod.
|
||||
public static ModSettings DefaultSettings( Mod mod )
|
||||
=> new()
|
||||
{
|
||||
|
|
@ -30,24 +32,30 @@ public class ModSettings
|
|||
Settings = Enumerable.Repeat( 0u, mod.Groups.Count ).ToList(),
|
||||
};
|
||||
|
||||
// Automatically react to changes in a mods available options.
|
||||
public bool HandleChanges( ModOptionChangeType type, Mod mod, int groupIdx, int optionIdx, int movedToIdx )
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case ModOptionChangeType.GroupRenamed: return true;
|
||||
case ModOptionChangeType.GroupAdded:
|
||||
// Add new empty setting for new mod.
|
||||
Settings.Insert( groupIdx, 0 );
|
||||
return true;
|
||||
case ModOptionChangeType.GroupDeleted:
|
||||
// Remove setting for deleted mod.
|
||||
Settings.RemoveAt( groupIdx );
|
||||
return true;
|
||||
case ModOptionChangeType.GroupTypeChanged:
|
||||
{
|
||||
// Fix settings for a changed group type.
|
||||
// Single -> Multi: set single as enabled, rest as disabled
|
||||
// Multi -> Single: set the first enabled option or 0.
|
||||
var group = mod.Groups[ groupIdx ];
|
||||
var config = Settings[ groupIdx ];
|
||||
Settings[ groupIdx ] = group.Type switch
|
||||
{
|
||||
SelectType.Single => ( uint )Math.Min( group.Count - 1, BitOperations.TrailingZeroCount( config ) ),
|
||||
SelectType.Single => ( uint )Math.Max( Math.Min( group.Count - 1, BitOperations.TrailingZeroCount( config ) ), 0 ),
|
||||
SelectType.Multi => 1u << ( int )config,
|
||||
_ => config,
|
||||
};
|
||||
|
|
@ -55,6 +63,8 @@ public class ModSettings
|
|||
}
|
||||
case ModOptionChangeType.OptionDeleted:
|
||||
{
|
||||
// Single -> select the previous option if any.
|
||||
// Multi -> excise the corresponding bit.
|
||||
var group = mod.Groups[ groupIdx ];
|
||||
var config = Settings[ groupIdx ];
|
||||
Settings[ groupIdx ] = group.Type switch
|
||||
|
|
@ -65,9 +75,13 @@ public class ModSettings
|
|||
};
|
||||
return config != Settings[ groupIdx ];
|
||||
}
|
||||
case ModOptionChangeType.GroupMoved: return Settings.Move( groupIdx, movedToIdx );
|
||||
case ModOptionChangeType.GroupMoved:
|
||||
// Move the group the same way.
|
||||
return Settings.Move( groupIdx, movedToIdx );
|
||||
case ModOptionChangeType.OptionMoved:
|
||||
{
|
||||
// Single -> select the moved option if it was currently selected
|
||||
// Multi -> move the corresponding bit
|
||||
var group = mod.Groups[ groupIdx ];
|
||||
var config = Settings[ groupIdx ];
|
||||
Settings[ groupIdx ] = group.Type switch
|
||||
|
|
@ -82,6 +96,7 @@ public class ModSettings
|
|||
}
|
||||
}
|
||||
|
||||
// Ensure that a value is valid for a group.
|
||||
private static uint FixSetting( IModGroup group, uint value )
|
||||
=> group.Type switch
|
||||
{
|
||||
|
|
@ -90,6 +105,7 @@ public class ModSettings
|
|||
_ => value,
|
||||
};
|
||||
|
||||
// Set a setting. Ensures that there are enough settings and fixes the setting beforehand.
|
||||
public void SetValue( Mod mod, int groupIdx, uint newValue )
|
||||
{
|
||||
AddMissingSettings( groupIdx + 1 );
|
||||
|
|
@ -97,6 +113,7 @@ public class ModSettings
|
|||
Settings[ groupIdx ] = FixSetting( group, newValue );
|
||||
}
|
||||
|
||||
// Remove a single bit, moving all further bits one down.
|
||||
private static uint RemoveBit( uint config, int bit )
|
||||
{
|
||||
var lowMask = ( 1u << bit ) - 1u;
|
||||
|
|
@ -106,6 +123,7 @@ public class ModSettings
|
|||
return low | high;
|
||||
}
|
||||
|
||||
// Move a bit in an uint from its position to another, shifting other bits accordingly.
|
||||
private static uint MoveBit( uint config, int bit1, int bit2 )
|
||||
{
|
||||
var enabled = ( config & ( 1 << bit1 ) ) != 0 ? 1u << bit2 : 0u;
|
||||
|
|
@ -116,7 +134,8 @@ public class ModSettings
|
|||
return low | enabled | high;
|
||||
}
|
||||
|
||||
internal bool AddMissingSettings( int totalCount )
|
||||
// Add defaulted settings up to the required count.
|
||||
private bool AddMissingSettings( int totalCount )
|
||||
{
|
||||
if( totalCount <= Settings.Count )
|
||||
{
|
||||
|
|
@ -127,6 +146,7 @@ public class ModSettings
|
|||
return true;
|
||||
}
|
||||
|
||||
// A simple struct conversion to easily save settings by name instead of value.
|
||||
public struct SavedSettings
|
||||
{
|
||||
public Dictionary< string, uint > Settings;
|
||||
|
|
@ -154,6 +174,7 @@ public class ModSettings
|
|||
}
|
||||
}
|
||||
|
||||
// Convert and fix.
|
||||
public bool ToSettings( Mod mod, out ModSettings settings )
|
||||
{
|
||||
var list = new List< uint >( mod.Groups.Count );
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
namespace Penumbra.Mods;
|
||||
|
||||
public enum SelectType
|
||||
{
|
||||
Single,
|
||||
Multi,
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue