mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-14 20:54:16 +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
|
|
@ -3,10 +3,7 @@ using Penumbra.Collections;
|
|||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.String;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Api;
|
||||
|
|
@ -19,319 +16,120 @@ public enum RedirectResult
|
|||
FilteredGamePath = 3,
|
||||
}
|
||||
|
||||
public class TempModManager
|
||||
public class TempModManager : IDisposable
|
||||
{
|
||||
public int GlobalChangeCounter { get; private set; } = 0;
|
||||
private readonly Dictionary< ModCollection, List< Mod.TemporaryMod > > _mods = new();
|
||||
private readonly List< Mod.TemporaryMod > _modsForAllCollections = new();
|
||||
private readonly Dictionary< string, ModCollection > _customCollections = new();
|
||||
public readonly IndividualCollections Collections = new(Penumbra.Actors);
|
||||
private readonly CommunicatorService _communicator;
|
||||
|
||||
public event ModCollection.Manager.CollectionChangeDelegate? CollectionChanged;
|
||||
private readonly Dictionary<ModCollection, List<Mod.TemporaryMod>> _mods = new();
|
||||
private readonly List<Mod.TemporaryMod> _modsForAllCollections = new();
|
||||
|
||||
public IReadOnlyDictionary< ModCollection, List< Mod.TemporaryMod > > Mods
|
||||
public TempModManager(CommunicatorService communicator)
|
||||
{
|
||||
_communicator = communicator;
|
||||
_communicator.CollectionChange.Event += OnCollectionChange;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_communicator.CollectionChange.Event -= OnCollectionChange;
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<ModCollection, List<Mod.TemporaryMod>> Mods
|
||||
=> _mods;
|
||||
|
||||
public IReadOnlyList< Mod.TemporaryMod > ModsForAllCollections
|
||||
public IReadOnlyList<Mod.TemporaryMod> ModsForAllCollections
|
||||
=> _modsForAllCollections;
|
||||
|
||||
public IReadOnlyDictionary< string, ModCollection > CustomCollections
|
||||
=> _customCollections;
|
||||
|
||||
public bool CollectionByName( string name, [NotNullWhen( true )] out ModCollection? collection )
|
||||
=> _customCollections.TryGetValue( name.ToLowerInvariant(), out collection );
|
||||
|
||||
// These functions to check specific redirections or meta manipulations for existence are currently unused.
|
||||
//public bool IsRegistered( string tag, ModCollection? collection, Utf8GamePath gamePath, out FullPath? fullPath, out int priority )
|
||||
//{
|
||||
// var mod = GetExistingMod( tag, collection, null );
|
||||
// if( mod == null )
|
||||
// {
|
||||
// priority = 0;
|
||||
// fullPath = null;
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// priority = mod.Priority;
|
||||
// if( mod.Default.Files.TryGetValue( gamePath, out var f ) )
|
||||
// {
|
||||
// fullPath = f;
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// fullPath = null;
|
||||
// return false;
|
||||
//}
|
||||
//
|
||||
//public bool IsRegistered( string tag, ModCollection? collection, MetaManipulation meta, out MetaManipulation? manipulation,
|
||||
// out int priority )
|
||||
//{
|
||||
// var mod = GetExistingMod( tag, collection, null );
|
||||
// if( mod == null )
|
||||
// {
|
||||
// priority = 0;
|
||||
// manipulation = null;
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// priority = mod.Priority;
|
||||
// // IReadOnlySet has no TryGetValue for some reason.
|
||||
// if( ( ( HashSet< MetaManipulation > )mod.Default.Manipulations ).TryGetValue( meta, out var manip ) )
|
||||
// {
|
||||
// manipulation = manip;
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// manipulation = null;
|
||||
// return false;
|
||||
//}
|
||||
|
||||
// These functions for setting single redirections or manips are currently unused.
|
||||
//public RedirectResult Register( string tag, ModCollection? collection, Utf8GamePath path, FullPath file, int priority )
|
||||
//{
|
||||
// if( Mod.FilterFile( path ) )
|
||||
// {
|
||||
// return RedirectResult.FilteredGamePath;
|
||||
// }
|
||||
//
|
||||
// var mod = GetOrCreateMod( tag, collection, priority, out var created );
|
||||
//
|
||||
// var changes = !mod.Default.Files.TryGetValue( path, out var oldFile ) || !oldFile.Equals( file );
|
||||
// mod.SetFile( path, file );
|
||||
// ApplyModChange( mod, collection, created, false );
|
||||
// return changes ? RedirectResult.IdenticalFileRegistered : RedirectResult.Success;
|
||||
//}
|
||||
//
|
||||
//public RedirectResult Register( string tag, ModCollection? collection, MetaManipulation meta, int priority )
|
||||
//{
|
||||
// var mod = GetOrCreateMod( tag, collection, priority, out var created );
|
||||
// var changes = !( ( HashSet< MetaManipulation > )mod.Default.Manipulations ).TryGetValue( meta, out var oldMeta )
|
||||
// || !oldMeta.Equals( meta );
|
||||
// mod.SetManipulation( meta );
|
||||
// ApplyModChange( mod, collection, created, false );
|
||||
// return changes ? RedirectResult.IdenticalFileRegistered : RedirectResult.Success;
|
||||
//}
|
||||
|
||||
public RedirectResult Register( string tag, ModCollection? collection, Dictionary< Utf8GamePath, FullPath > dict,
|
||||
HashSet< MetaManipulation > manips, int priority )
|
||||
public RedirectResult Register(string tag, ModCollection? collection, Dictionary<Utf8GamePath, FullPath> dict,
|
||||
HashSet<MetaManipulation> manips, int priority)
|
||||
{
|
||||
var mod = GetOrCreateMod( tag, collection, priority, out var created );
|
||||
mod.SetAll( dict, manips );
|
||||
ApplyModChange( mod, collection, created, false );
|
||||
var mod = GetOrCreateMod(tag, collection, priority, out var created);
|
||||
mod.SetAll(dict, manips);
|
||||
ApplyModChange(mod, collection, created, false);
|
||||
return RedirectResult.Success;
|
||||
}
|
||||
|
||||
public RedirectResult Unregister( string tag, ModCollection? collection, int? priority )
|
||||
public RedirectResult Unregister(string tag, ModCollection? collection, int? priority)
|
||||
{
|
||||
var list = collection == null ? _modsForAllCollections : _mods.TryGetValue( collection, out var l ) ? l : null;
|
||||
if( list == null )
|
||||
{
|
||||
var list = collection == null ? _modsForAllCollections : _mods.TryGetValue(collection, out var l) ? l : null;
|
||||
if (list == null)
|
||||
return RedirectResult.NotRegistered;
|
||||
}
|
||||
|
||||
var removed = list.RemoveAll( m =>
|
||||
var removed = list.RemoveAll(m =>
|
||||
{
|
||||
if( m.Name != tag || priority != null && m.Priority != priority.Value )
|
||||
{
|
||||
if (m.Name != tag || priority != null && m.Priority != priority.Value)
|
||||
return false;
|
||||
}
|
||||
|
||||
ApplyModChange( m, collection, false, true );
|
||||
ApplyModChange(m, collection, false, true);
|
||||
return true;
|
||||
} );
|
||||
});
|
||||
|
||||
if( removed == 0 )
|
||||
{
|
||||
if (removed == 0)
|
||||
return RedirectResult.NotRegistered;
|
||||
}
|
||||
|
||||
if( list.Count == 0 && collection != null )
|
||||
{
|
||||
_mods.Remove( collection );
|
||||
}
|
||||
if (list.Count == 0 && collection != null)
|
||||
_mods.Remove(collection);
|
||||
|
||||
return RedirectResult.Success;
|
||||
}
|
||||
|
||||
public string CreateTemporaryCollection( string name )
|
||||
{
|
||||
if( Penumbra.CollectionManager.ByName( name, out _ ) )
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if( GlobalChangeCounter == int.MaxValue )
|
||||
GlobalChangeCounter = 0;
|
||||
var collection = ModCollection.CreateNewTemporary( name, GlobalChangeCounter++ );
|
||||
if( _customCollections.TryAdd( collection.Name.ToLowerInvariant(), collection ) )
|
||||
{
|
||||
return collection.Name;
|
||||
}
|
||||
|
||||
collection.ClearCache();
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public bool RemoveTemporaryCollection( string collectionName )
|
||||
{
|
||||
if( !_customCollections.Remove( collectionName.ToLowerInvariant(), out var collection ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
GlobalChangeCounter += Math.Max(collection.ChangeCounter + 1 - GlobalChangeCounter, 0);
|
||||
_mods.Remove( collection );
|
||||
collection.ClearCache();
|
||||
for( var i = 0; i < Collections.Count; ++i )
|
||||
{
|
||||
if( Collections[ i ].Collection == collection )
|
||||
{
|
||||
CollectionChanged?.Invoke( CollectionType.Temporary, collection, null, Collections[ i ].DisplayName );
|
||||
Collections.Delete( i );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool AddIdentifier( ModCollection collection, params ActorIdentifier[] identifiers )
|
||||
{
|
||||
if( Collections.Add( identifiers, collection ) )
|
||||
{
|
||||
CollectionChanged?.Invoke( CollectionType.Temporary, null, collection, Collections.Last().DisplayName );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool AddIdentifier( string collectionName, params ActorIdentifier[] identifiers )
|
||||
{
|
||||
if( !_customCollections.TryGetValue( collectionName.ToLowerInvariant(), out var collection ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return AddIdentifier( collection, identifiers );
|
||||
}
|
||||
|
||||
public bool AddIdentifier( string collectionName, string characterName, ushort worldId = ushort.MaxValue )
|
||||
{
|
||||
if( !ByteString.FromString( characterName, out var byteString, false ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var identifier = Penumbra.Actors.CreatePlayer( byteString, worldId );
|
||||
if( !identifier.IsValid )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return AddIdentifier( collectionName, identifier );
|
||||
}
|
||||
|
||||
internal bool RemoveByCharacterName( string characterName, ushort worldId = ushort.MaxValue )
|
||||
{
|
||||
if( !ByteString.FromString( characterName, out var byteString, false ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var identifier = Penumbra.Actors.CreatePlayer( byteString, worldId );
|
||||
return Collections.Individuals.TryGetValue( identifier, out var collection ) && RemoveTemporaryCollection( collection.Name );
|
||||
}
|
||||
|
||||
|
||||
// Apply any new changes to the temporary mod.
|
||||
private static void ApplyModChange( Mod.TemporaryMod mod, ModCollection? collection, bool created, bool removed )
|
||||
private void ApplyModChange(Mod.TemporaryMod mod, ModCollection? collection, bool created, bool removed)
|
||||
{
|
||||
if( collection == null )
|
||||
if (collection != null)
|
||||
{
|
||||
if( removed )
|
||||
{
|
||||
foreach( var c in Penumbra.CollectionManager )
|
||||
{
|
||||
c.Remove( mod );
|
||||
}
|
||||
}
|
||||
if (removed)
|
||||
collection.Remove(mod);
|
||||
else
|
||||
{
|
||||
foreach( var c in Penumbra.CollectionManager )
|
||||
{
|
||||
c.Apply( mod, created );
|
||||
}
|
||||
}
|
||||
collection.Apply(mod, created);
|
||||
}
|
||||
else
|
||||
{
|
||||
if( removed )
|
||||
{
|
||||
collection.Remove( mod );
|
||||
}
|
||||
else
|
||||
{
|
||||
collection.Apply( mod, created );
|
||||
}
|
||||
_communicator.TemporaryGlobalModChange.Invoke(mod, created, removed);
|
||||
}
|
||||
}
|
||||
|
||||
// Only find already existing mods, currently unused.
|
||||
//private Mod.TemporaryMod? GetExistingMod( string tag, ModCollection? collection, int? priority )
|
||||
//{
|
||||
// var list = collection == null ? _modsForAllCollections : _mods.TryGetValue( collection, out var l ) ? l : null;
|
||||
// if( list == null )
|
||||
// {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// if( priority != null )
|
||||
// {
|
||||
// return list.Find( m => m.Priority == priority.Value && m.Name == tag );
|
||||
// }
|
||||
//
|
||||
// Mod.TemporaryMod? highestMod = null;
|
||||
// var highestPriority = int.MinValue;
|
||||
// foreach( var m in list )
|
||||
// {
|
||||
// if( highestPriority < m.Priority && m.Name == tag )
|
||||
// {
|
||||
// highestPriority = m.Priority;
|
||||
// highestMod = m;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return highestMod;
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Apply a mod change to a set of collections.
|
||||
/// </summary>
|
||||
public static void OnGlobalModChange(IEnumerable<ModCollection> collections, Mod.TemporaryMod mod, bool created, bool removed)
|
||||
{
|
||||
if (removed)
|
||||
foreach (var c in collections)
|
||||
c.Remove(mod);
|
||||
else
|
||||
foreach (var c in collections)
|
||||
c.Apply(mod, created);
|
||||
}
|
||||
|
||||
// Find or create a mod with the given tag as name and the given priority, for the given collection (or all collections).
|
||||
// Returns the found or created mod and whether it was newly created.
|
||||
private Mod.TemporaryMod GetOrCreateMod( string tag, ModCollection? collection, int priority, out bool created )
|
||||
private Mod.TemporaryMod GetOrCreateMod(string tag, ModCollection? collection, int priority, out bool created)
|
||||
{
|
||||
List< Mod.TemporaryMod > list;
|
||||
if( collection == null )
|
||||
List<Mod.TemporaryMod> list;
|
||||
if (collection == null)
|
||||
{
|
||||
list = _modsForAllCollections;
|
||||
}
|
||||
else if( _mods.TryGetValue( collection, out var l ) )
|
||||
else if (_mods.TryGetValue(collection, out var l))
|
||||
{
|
||||
list = l;
|
||||
}
|
||||
else
|
||||
{
|
||||
list = new List< Mod.TemporaryMod >();
|
||||
_mods.Add( collection, list );
|
||||
list = new List<Mod.TemporaryMod>();
|
||||
_mods.Add(collection, list);
|
||||
}
|
||||
|
||||
var mod = list.Find( m => m.Priority == priority && m.Name == tag );
|
||||
if( mod == null )
|
||||
var mod = list.Find(m => m.Priority == priority && m.Name == tag);
|
||||
if (mod == null)
|
||||
{
|
||||
mod = new Mod.TemporaryMod()
|
||||
{
|
||||
Name = tag,
|
||||
Priority = priority,
|
||||
};
|
||||
list.Add( mod );
|
||||
list.Add(mod);
|
||||
created = true;
|
||||
}
|
||||
else
|
||||
|
|
@ -341,4 +139,11 @@ public class TempModManager
|
|||
|
||||
return mod;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCollectionChange(CollectionType collectionType, ModCollection? oldCollection, ModCollection? newCollection,
|
||||
string _)
|
||||
{
|
||||
if (collectionType is CollectionType.Temporary or CollectionType.Inactive && newCollection == null && oldCollection != null)
|
||||
_mods.Remove(oldCollection);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue