mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Try to filter meta entries for relevance.
This commit is contained in:
parent
1fca78fa71
commit
f51f8a7bf8
2 changed files with 194 additions and 8 deletions
|
|
@ -1,7 +1,10 @@
|
||||||
|
using System.Collections.Frozen;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Penumbra.Collections.Cache;
|
using Penumbra.Collections.Cache;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Files.AtchStructs;
|
using Penumbra.GameData.Files.AtchStructs;
|
||||||
|
using Penumbra.GameData.Interop;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using Penumbra.Util;
|
using Penumbra.Util;
|
||||||
using ImcEntry = Penumbra.GameData.Structs.ImcEntry;
|
using ImcEntry = Penumbra.GameData.Structs.ImcEntry;
|
||||||
|
|
@ -40,6 +43,165 @@ public class MetaDictionary
|
||||||
foreach (var geqp in cache.GlobalEqp.Keys)
|
foreach (var geqp in cache.GlobalEqp.Keys)
|
||||||
Add(geqp);
|
Add(geqp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static unsafe Wrapper Filtered(MetaCache cache, Actor actor)
|
||||||
|
{
|
||||||
|
if (!actor.IsCharacter)
|
||||||
|
return new Wrapper(cache);
|
||||||
|
|
||||||
|
var model = actor.Model;
|
||||||
|
if (!model.IsHuman)
|
||||||
|
return new Wrapper(cache);
|
||||||
|
|
||||||
|
var headId = model.GetModelId(HumanSlot.Head);
|
||||||
|
var bodyId = model.GetModelId(HumanSlot.Body);
|
||||||
|
var equipIdSet = ((IEnumerable<PrimaryId>)
|
||||||
|
[
|
||||||
|
headId,
|
||||||
|
bodyId,
|
||||||
|
model.GetModelId(HumanSlot.Hands),
|
||||||
|
model.GetModelId(HumanSlot.Legs),
|
||||||
|
model.GetModelId(HumanSlot.Feet),
|
||||||
|
]).ToFrozenSet();
|
||||||
|
var earsId = model.GetModelId(HumanSlot.Ears);
|
||||||
|
var neckId = model.GetModelId(HumanSlot.Neck);
|
||||||
|
var wristId = model.GetModelId(HumanSlot.Wrists);
|
||||||
|
var rFingerId = model.GetModelId(HumanSlot.RFinger);
|
||||||
|
var lFingerId = model.GetModelId(HumanSlot.LFinger);
|
||||||
|
|
||||||
|
var wrapper = new Wrapper();
|
||||||
|
// Check for all relevant primary IDs due to slot overlap.
|
||||||
|
foreach (var (eqp, value) in cache.Eqp)
|
||||||
|
{
|
||||||
|
if (eqp.Slot.IsEquipment())
|
||||||
|
{
|
||||||
|
if (equipIdSet.Contains(eqp.SetId))
|
||||||
|
wrapper.Eqp.Add(eqp, new EqpEntryInternal(value.Entry, eqp.Slot));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (eqp.Slot)
|
||||||
|
{
|
||||||
|
case EquipSlot.Ears when eqp.SetId == earsId:
|
||||||
|
case EquipSlot.Neck when eqp.SetId == neckId:
|
||||||
|
case EquipSlot.Wrists when eqp.SetId == wristId:
|
||||||
|
case EquipSlot.RFinger when eqp.SetId == rFingerId:
|
||||||
|
case EquipSlot.LFinger when eqp.SetId == lFingerId:
|
||||||
|
wrapper.Eqp.Add(eqp, new EqpEntryInternal(value.Entry, eqp.Slot));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check also for body IDs due to body occupying head.
|
||||||
|
foreach (var (gmp, value) in cache.Gmp)
|
||||||
|
{
|
||||||
|
if (gmp.SetId == headId || gmp.SetId == bodyId)
|
||||||
|
wrapper.Gmp.Add(gmp, value.Entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for all races due to inheritance and all slots due to overlap.
|
||||||
|
foreach (var (eqdp, value) in cache.Eqdp)
|
||||||
|
{
|
||||||
|
if (eqdp.Slot.IsEquipment())
|
||||||
|
{
|
||||||
|
if (equipIdSet.Contains(eqdp.SetId))
|
||||||
|
wrapper.Eqdp.Add(eqdp, new EqdpEntryInternal(value.Entry, eqdp.Slot));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (eqdp.Slot)
|
||||||
|
{
|
||||||
|
case EquipSlot.Ears when eqdp.SetId == earsId:
|
||||||
|
case EquipSlot.Neck when eqdp.SetId == neckId:
|
||||||
|
case EquipSlot.Wrists when eqdp.SetId == wristId:
|
||||||
|
case EquipSlot.RFinger when eqdp.SetId == rFingerId:
|
||||||
|
case EquipSlot.LFinger when eqdp.SetId == lFingerId:
|
||||||
|
wrapper.Eqdp.Add(eqdp, new EqdpEntryInternal(value.Entry, eqdp.Slot));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var genderRace = (GenderRace)model.AsHuman->RaceSexId;
|
||||||
|
var hairId = model.GetModelId(HumanSlot.Hair);
|
||||||
|
var faceId = model.GetModelId(HumanSlot.Face);
|
||||||
|
// We do not need to care for racial inheritance for ESTs.
|
||||||
|
foreach (var (est, value) in cache.Est)
|
||||||
|
{
|
||||||
|
switch (est.Slot)
|
||||||
|
{
|
||||||
|
case EstType.Hair when est.SetId == hairId && est.GenderRace == genderRace:
|
||||||
|
case EstType.Face when est.SetId == faceId && est.GenderRace == genderRace:
|
||||||
|
case EstType.Body when est.SetId == bodyId && est.GenderRace == genderRace:
|
||||||
|
case EstType.Head when (est.SetId == headId || est.SetId == bodyId) && est.GenderRace == genderRace:
|
||||||
|
wrapper.Est.Add(est, value.Entry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (geqp, _) in cache.GlobalEqp)
|
||||||
|
{
|
||||||
|
switch (geqp.Type)
|
||||||
|
{
|
||||||
|
case GlobalEqpType.DoNotHideEarrings when geqp.Condition != earsId:
|
||||||
|
case GlobalEqpType.DoNotHideNecklace when geqp.Condition != neckId:
|
||||||
|
case GlobalEqpType.DoNotHideBracelets when geqp.Condition != wristId:
|
||||||
|
case GlobalEqpType.DoNotHideRingR when geqp.Condition != rFingerId:
|
||||||
|
case GlobalEqpType.DoNotHideRingL when geqp.Condition != lFingerId:
|
||||||
|
continue;
|
||||||
|
default: wrapper.Add(geqp); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (_, _, main, off) = model.GetWeapons(actor);
|
||||||
|
foreach (var (imc, value) in cache.Imc)
|
||||||
|
{
|
||||||
|
switch (imc.ObjectType)
|
||||||
|
{
|
||||||
|
case ObjectType.Equipment when equipIdSet.Contains(imc.PrimaryId): wrapper.Imc.Add(imc, value.Entry); break;
|
||||||
|
|
||||||
|
case ObjectType.Weapon:
|
||||||
|
if (imc.PrimaryId == main.Skeleton && imc.SecondaryId == main.Weapon)
|
||||||
|
wrapper.Imc.Add(imc, value.Entry);
|
||||||
|
else if (imc.PrimaryId == off.Skeleton && imc.SecondaryId == off.Weapon)
|
||||||
|
wrapper.Imc.Add(imc, value.Entry);
|
||||||
|
break;
|
||||||
|
case ObjectType.Accessory:
|
||||||
|
switch (imc.EquipSlot)
|
||||||
|
{
|
||||||
|
case EquipSlot.Ears when imc.PrimaryId == earsId:
|
||||||
|
case EquipSlot.Neck when imc.PrimaryId == neckId:
|
||||||
|
case EquipSlot.Wrists when imc.PrimaryId == wristId:
|
||||||
|
case EquipSlot.RFinger when imc.PrimaryId == rFingerId:
|
||||||
|
case EquipSlot.LFinger when imc.PrimaryId == lFingerId:
|
||||||
|
wrapper.Imc.Add(imc, value.Entry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var subRace = (SubRace)model.AsHuman->Customize[4];
|
||||||
|
foreach (var (rsp, value) in cache.Rsp)
|
||||||
|
{
|
||||||
|
if (rsp.SubRace == subRace)
|
||||||
|
wrapper.Rsp.Add(rsp, value.Entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep all atch, atr and shp.
|
||||||
|
wrapper.Atch.EnsureCapacity(cache.Atch.Count);
|
||||||
|
wrapper.Shp.EnsureCapacity(cache.Shp.Count);
|
||||||
|
wrapper.Atr.EnsureCapacity(cache.Atr.Count);
|
||||||
|
foreach (var (atch, value) in cache.Atch)
|
||||||
|
wrapper.Atch.Add(atch, value.Entry);
|
||||||
|
foreach (var (shp, value) in cache.Shp)
|
||||||
|
wrapper.Shp.Add(shp, value.Entry);
|
||||||
|
foreach (var (atr, value) in cache.Atr)
|
||||||
|
wrapper.Atr.Add(atr, value.Entry);
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Wrapper? _data;
|
private Wrapper? _data;
|
||||||
|
|
@ -934,4 +1096,24 @@ public class MetaDictionary
|
||||||
_data = new Wrapper(cache);
|
_data = new Wrapper(cache);
|
||||||
Count = cache.Count;
|
Count = cache.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MetaDictionary(MetaCache? cache, Actor actor)
|
||||||
|
{
|
||||||
|
if (cache is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_data = Wrapper.Filtered(cache, actor);
|
||||||
|
Count = _data.Count
|
||||||
|
+ _data.Eqp.Count
|
||||||
|
+ _data.Eqdp.Count
|
||||||
|
+ _data.Est.Count
|
||||||
|
+ _data.Gmp.Count
|
||||||
|
+ _data.Imc.Count
|
||||||
|
+ _data.Rsp.Count
|
||||||
|
+ _data.Atch.Count
|
||||||
|
+ _data.Atr.Count
|
||||||
|
+ _data.Shp.Count;
|
||||||
|
if (Count is 0)
|
||||||
|
_data = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,7 @@ public class PcpService : IApiService, IDisposable
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Penumbra.Log.Information($"[PCPService] Creating PCP file for game object {objectIndex.Index}.");
|
Penumbra.Log.Information($"[PCPService] Creating PCP file for game object {objectIndex.Index}.");
|
||||||
var (identifier, tree, collection) = await _framework.Framework.RunOnFrameworkThread(() =>
|
var (identifier, tree, meta) = await _framework.Framework.RunOnFrameworkThread(() =>
|
||||||
{
|
{
|
||||||
var (actor, identifier) = CheckActor(objectIndex);
|
var (actor, identifier) = CheckActor(objectIndex);
|
||||||
cancel.ThrowIfCancellationRequested();
|
cancel.ThrowIfCancellationRequested();
|
||||||
|
|
@ -178,13 +178,14 @@ public class PcpService : IApiService, IDisposable
|
||||||
if (_treeFactory.FromCharacter(actor, 0) is not { } tree)
|
if (_treeFactory.FromCharacter(actor, 0) is not { } tree)
|
||||||
throw new Exception($"Unable to fetch modded resources for {identifier}.");
|
throw new Exception($"Unable to fetch modded resources for {identifier}.");
|
||||||
|
|
||||||
return (identifier.CreatePermanent(), tree, collection);
|
var meta = new MetaDictionary(collection.ModCollection.MetaCache, actor.Address);
|
||||||
|
return (identifier.CreatePermanent(), tree, meta);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cancel.ThrowIfCancellationRequested();
|
cancel.ThrowIfCancellationRequested();
|
||||||
var time = DateTime.Now;
|
var time = DateTime.Now;
|
||||||
var modDirectory = CreateMod(identifier, note, time);
|
var modDirectory = CreateMod(identifier, note, time);
|
||||||
await CreateDefaultMod(modDirectory, collection.ModCollection, tree, cancel);
|
await CreateDefaultMod(modDirectory, meta, tree, cancel);
|
||||||
await CreateCollectionInfo(modDirectory, objectIndex, identifier, note, time, cancel);
|
await CreateCollectionInfo(modDirectory, objectIndex, identifier, note, time, cancel);
|
||||||
var file = ZipUp(modDirectory);
|
var file = ZipUp(modDirectory);
|
||||||
return (true, file);
|
return (true, file);
|
||||||
|
|
@ -242,11 +243,15 @@ public class PcpService : IApiService, IDisposable
|
||||||
?? throw new Exception($"Unable to create mod {modName} in {directory.FullName}.");
|
?? throw new Exception($"Unable to create mod {modName} in {directory.FullName}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CreateDefaultMod(DirectoryInfo modDirectory, ModCollection collection, ResourceTree tree,
|
private async Task CreateDefaultMod(DirectoryInfo modDirectory, MetaDictionary meta, ResourceTree tree,
|
||||||
CancellationToken cancel = default)
|
CancellationToken cancel = default)
|
||||||
{
|
{
|
||||||
var subDirectory = modDirectory.CreateSubdirectory("files");
|
var subDirectory = modDirectory.CreateSubdirectory("files");
|
||||||
var subMod = new DefaultSubMod(null!);
|
var subMod = new DefaultSubMod(null!)
|
||||||
|
{
|
||||||
|
Manipulations = meta,
|
||||||
|
};
|
||||||
|
|
||||||
foreach (var node in tree.FlatNodes)
|
foreach (var node in tree.FlatNodes)
|
||||||
{
|
{
|
||||||
cancel.ThrowIfCancellationRequested();
|
cancel.ThrowIfCancellationRequested();
|
||||||
|
|
@ -269,7 +274,6 @@ public class PcpService : IApiService, IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel.ThrowIfCancellationRequested();
|
cancel.ThrowIfCancellationRequested();
|
||||||
subMod.Manipulations = new MetaDictionary(collection.MetaCache);
|
|
||||||
|
|
||||||
var saveGroup = new ModSaveGroup(modDirectory, subMod, _config.ReplaceNonAsciiOnImport);
|
var saveGroup = new ModSaveGroup(modDirectory, subMod, _config.ReplaceNonAsciiOnImport);
|
||||||
var filePath = _files.FileNames.OptionGroupFile(modDirectory.FullName, -1, string.Empty, _config.ReplaceNonAsciiOnImport);
|
var filePath = _files.FileNames.OptionGroupFile(modDirectory.FullName, -1, string.Empty, _config.ReplaceNonAsciiOnImport);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue