Update for changed GameData.

This commit is contained in:
Ottermandias 2023-12-17 11:51:24 +01:00
parent 3305250482
commit 7d612df951
42 changed files with 374 additions and 455 deletions

View file

@ -28,9 +28,9 @@ public class ActiveCollections : ISavable, IDisposable
private readonly CommunicatorService _communicator;
private readonly SaveService _saveService;
private readonly ActiveCollectionData _data;
private readonly ActorService _actors;
private readonly ActorManager _actors;
public ActiveCollections(Configuration config, CollectionStorage storage, ActorService actors, CommunicatorService communicator,
public ActiveCollections(Configuration config, CollectionStorage storage, ActorManager actors, CommunicatorService communicator,
SaveService saveService, ActiveCollectionData data)
{
_storage = storage;
@ -475,7 +475,7 @@ public class ActiveCollections : ISavable, IDisposable
{
case IdentifierType.Player when id.HomeWorld != ushort.MaxValue:
{
var global = ByType(CollectionType.Individual, _actors.AwaitedService.CreatePlayer(id.PlayerName, ushort.MaxValue));
var global = ByType(CollectionType.Individual, _actors.CreatePlayer(id.PlayerName, ushort.MaxValue));
return global?.Index == checkAssignment.Index
? "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it."
: string.Empty;
@ -484,12 +484,12 @@ public class ActiveCollections : ISavable, IDisposable
if (id.HomeWorld != ushort.MaxValue)
{
var global = ByType(CollectionType.Individual,
_actors.AwaitedService.CreateOwned(id.PlayerName, ushort.MaxValue, id.Kind, id.DataId));
_actors.CreateOwned(id.PlayerName, ushort.MaxValue, id.Kind, id.DataId));
if (global?.Index == checkAssignment.Index)
return "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it.";
}
var unowned = ByType(CollectionType.Individual, _actors.AwaitedService.CreateNpc(id.Kind, id.DataId));
var unowned = ByType(CollectionType.Individual, _actors.CreateNpc(id.Kind, id.DataId));
return unowned?.Index == checkAssignment.Index
? "Assignment is redundant due to an identical unowned NPC assignment existing.\nYou can remove it."
: string.Empty;

View file

@ -1,6 +1,7 @@
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.Types;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.String;
namespace Penumbra.Collections.Manager;
@ -36,7 +37,7 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa
return true;
if (identifier.Retainer is not ActorIdentifier.RetainerType.Mannequin && _config.UseOwnerNameForCharacterCollection)
return CheckWorlds(_actorService.AwaitedService.GetCurrentPlayer(), out collection);
return CheckWorlds(_actors.GetCurrentPlayer(), out collection);
break;
}
@ -46,7 +47,7 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa
return true;
// Handle generic NPC
var npcIdentifier = _actorService.AwaitedService.CreateIndividualUnchecked(IdentifierType.Npc, ByteString.Empty,
var npcIdentifier = _actors.CreateIndividualUnchecked(IdentifierType.Npc, ByteString.Empty,
ushort.MaxValue,
identifier.Kind, identifier.DataId);
if (npcIdentifier.IsValid && _individuals.TryGetValue(npcIdentifier, out collection))
@ -56,7 +57,7 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa
if (!_config.UseOwnerNameForCharacterCollection)
return false;
identifier = _actorService.AwaitedService.CreateIndividualUnchecked(IdentifierType.Player, identifier.PlayerName,
identifier = _actors.CreateIndividualUnchecked(IdentifierType.Player, identifier.PlayerName,
identifier.HomeWorld.Id,
ObjectKind.None, uint.MaxValue);
return CheckWorlds(identifier, out collection);
@ -89,37 +90,37 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa
if (identifier.Type != IdentifierType.Special)
return (identifier, SpecialResult.Invalid);
if (_actorService.AwaitedService.ResolvePartyBannerPlayer(identifier.Special, out var id))
if (_actors.ResolvePartyBannerPlayer(identifier.Special, out var id))
return _config.UseCharacterCollectionsInCards ? (id, SpecialResult.PartyBanner) : (identifier, SpecialResult.Invalid);
if (_actorService.AwaitedService.ResolvePvPBannerPlayer(identifier.Special, out id))
if (_actors.ResolvePvPBannerPlayer(identifier.Special, out id))
return _config.UseCharacterCollectionsInCards ? (id, SpecialResult.PvPBanner) : (identifier, SpecialResult.Invalid);
if (_actorService.AwaitedService.ResolveMahjongPlayer(identifier.Special, out id))
if (_actors.ResolveMahjongPlayer(identifier.Special, out id))
return _config.UseCharacterCollectionsInCards ? (id, SpecialResult.Mahjong) : (identifier, SpecialResult.Invalid);
switch (identifier.Special)
{
case ScreenActor.CharacterScreen when _config.UseCharacterCollectionInMainWindow:
return (_actorService.AwaitedService.GetCurrentPlayer(), SpecialResult.CharacterScreen);
return (_actors.GetCurrentPlayer(), SpecialResult.CharacterScreen);
case ScreenActor.FittingRoom when _config.UseCharacterCollectionInTryOn:
return (_actorService.AwaitedService.GetCurrentPlayer(), SpecialResult.FittingRoom);
return (_actors.GetCurrentPlayer(), SpecialResult.FittingRoom);
case ScreenActor.DyePreview when _config.UseCharacterCollectionInTryOn:
return (_actorService.AwaitedService.GetCurrentPlayer(), SpecialResult.DyePreview);
return (_actors.GetCurrentPlayer(), SpecialResult.DyePreview);
case ScreenActor.Portrait when _config.UseCharacterCollectionsInCards:
return (_actorService.AwaitedService.GetCurrentPlayer(), SpecialResult.Portrait);
return (_actors.GetCurrentPlayer(), SpecialResult.Portrait);
case ScreenActor.ExamineScreen:
{
identifier = _actorService.AwaitedService.GetInspectPlayer();
identifier = _actors.GetInspectPlayer();
if (identifier.IsValid)
return (_config.UseCharacterCollectionInInspect ? identifier : ActorIdentifier.Invalid, SpecialResult.Inspect);
identifier = _actorService.AwaitedService.GetCardPlayer();
identifier = _actors.GetCardPlayer();
if (identifier.IsValid)
return (_config.UseCharacterCollectionInInspect ? identifier : ActorIdentifier.Invalid, SpecialResult.Card);
return _config.UseCharacterCollectionInTryOn
? (_actorService.AwaitedService.GetGlamourPlayer(), SpecialResult.Glamour)
? (_actors.GetGlamourPlayer(), SpecialResult.Glamour)
: (identifier, SpecialResult.Invalid);
}
default: return (identifier, SpecialResult.Invalid);
@ -127,10 +128,10 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa
}
public bool TryGetCollection(GameObject? gameObject, out ModCollection? collection)
=> TryGetCollection(_actorService.AwaitedService.FromObject(gameObject, true, false, false), out collection);
=> TryGetCollection(_actors.FromObject(gameObject, true, false, false), out collection);
public unsafe bool TryGetCollection(FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* gameObject, out ModCollection? collection)
=> TryGetCollection(_actorService.AwaitedService.FromObject(gameObject, out _, true, false, false), out collection);
=> TryGetCollection(_actors.FromObject(gameObject, out _, true, false, false), out collection);
private bool CheckWorlds(ActorIdentifier identifier, out ModCollection? collection)
{
@ -143,7 +144,7 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa
if (_individuals.TryGetValue(identifier, out collection))
return true;
identifier = _actorService.AwaitedService.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, ushort.MaxValue,
identifier = _actors.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, ushort.MaxValue,
identifier.Kind,
identifier.DataId);
if (identifier.IsValid && _individuals.TryGetValue(identifier, out collection))

View file

@ -3,6 +3,8 @@ using Dalamud.Interface.Internal.Notifications;
using Newtonsoft.Json.Linq;
using OtterGui.Classes;
using Penumbra.GameData.Actors;
using Penumbra.GameData.DataContainers.Bases;
using Penumbra.GameData.Structs;
using Penumbra.Services;
using Penumbra.String;
@ -26,23 +28,20 @@ public partial class IndividualCollections
public bool ReadJObject(SaveService saver, ActiveCollections parent, JArray? obj, CollectionStorage storage)
{
if (_actorService.Valid)
if (_actors.Awaiter.IsCompletedSuccessfully)
{
var ret = ReadJObjectInternal(obj, storage);
return ret;
}
void Func()
Penumbra.Log.Debug("[Collections] Delayed reading individual assignments until actor service is ready...");
_actors.Awaiter.ContinueWith(_ =>
{
if (ReadJObjectInternal(obj, storage))
saver.ImmediateSave(parent);
IsLoaded = true;
Loaded.Invoke();
_actorService.FinishedCreation -= Func;
}
Penumbra.Log.Debug("[Collections] Delayed reading individual assignments until actor service is ready...");
_actorService.FinishedCreation += Func;
});
return false;
}
@ -60,7 +59,7 @@ public partial class IndividualCollections
{
try
{
var identifier = _actorService.AwaitedService.FromJson(data as JObject);
var identifier = _actors.FromJson(data as JObject);
var group = GetGroup(identifier);
if (group.Length == 0 || group.Any(i => !i.IsValid))
{
@ -101,10 +100,10 @@ public partial class IndividualCollections
internal void Migrate0To1(Dictionary<string, ModCollection> old)
{
static bool FindDataId(string name, IReadOnlyDictionary<uint, string> data, out uint dataId)
static bool FindDataId(string name, NameDictionary data, out NpcId dataId)
{
var kvp = data.FirstOrDefault(kvp => kvp.Value.Equals(name, StringComparison.OrdinalIgnoreCase),
new KeyValuePair<uint, string>(uint.MaxValue, string.Empty));
new KeyValuePair<NpcId, string>(uint.MaxValue, string.Empty));
dataId = kvp.Key;
return kvp.Value.Length > 0;
}
@ -114,22 +113,22 @@ public partial class IndividualCollections
var kind = ObjectKind.None;
var lowerName = name.ToLowerInvariant();
// Prefer matching NPC names, fewer false positives than preferring players.
if (FindDataId(lowerName, _actorService.AwaitedService.Data.Companions, out var dataId))
if (FindDataId(lowerName, _actors.Data.Companions, out var dataId))
kind = ObjectKind.Companion;
else if (FindDataId(lowerName, _actorService.AwaitedService.Data.Mounts, out dataId))
else if (FindDataId(lowerName, _actors.Data.Mounts, out dataId))
kind = ObjectKind.MountType;
else if (FindDataId(lowerName, _actorService.AwaitedService.Data.BNpcs, out dataId))
else if (FindDataId(lowerName, _actors.Data.BNpcs, out dataId))
kind = ObjectKind.BattleNpc;
else if (FindDataId(lowerName, _actorService.AwaitedService.Data.ENpcs, out dataId))
else if (FindDataId(lowerName, _actors.Data.ENpcs, out dataId))
kind = ObjectKind.EventNpc;
var identifier = _actorService.AwaitedService.CreateNpc(kind, dataId);
var identifier = _actors.CreateNpc(kind, dataId);
if (identifier.IsValid)
{
// If the name corresponds to a valid npc, add it as a group. If this fails, notify users.
var group = GetGroup(identifier);
var ids = string.Join(", ", group.Select(i => i.DataId.ToString()));
if (Add($"{_actorService.AwaitedService.Data.ToName(kind, dataId)} ({kind.ToName()})", group, collection))
if (Add($"{_actors.Data.ToName(kind, dataId)} ({kind.ToName()})", group, collection))
Penumbra.Log.Information($"Migrated {name} ({kind.ToName()}) to NPC Identifiers [{ids}].");
else
Penumbra.Messager.NotificationMessage(
@ -137,15 +136,12 @@ public partial class IndividualCollections
NotificationType.Error);
}
// If it is not a valid NPC name, check if it can be a player name.
else if (ActorManager.VerifyPlayerName(name))
else if (ActorIdentifierFactory.VerifyPlayerName(name))
{
identifier = _actorService.AwaitedService.CreatePlayer(ByteString.FromStringUnsafe(name, false), ushort.MaxValue);
identifier = _actors.CreatePlayer(ByteString.FromStringUnsafe(name, false), ushort.MaxValue);
var shortName = string.Join(" ", name.Split().Select(n => $"{n[0]}."));
// Try to migrate the player name without logging full names.
if (Add($"{name} ({_actorService.AwaitedService.Data.ToWorldName(identifier.HomeWorld)})", new[]
{
identifier,
}, collection))
if (Add($"{name} ({_actors.Data.ToWorldName(identifier.HomeWorld)})", [identifier], collection))
Penumbra.Log.Information($"Migrated {shortName} ({collection.AnonymizedName}) to Player Identifier.");
else
Penumbra.Messager.NotificationMessage(

View file

@ -1,7 +1,9 @@
using Dalamud.Game.ClientState.Objects.Enums;
using OtterGui.Filesystem;
using Penumbra.GameData.Actors;
using Penumbra.Services;
using Penumbra.GameData.DataContainers.Bases;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.String;
namespace Penumbra.Collections.Manager;
@ -11,9 +13,9 @@ public sealed partial class IndividualCollections
public record struct IndividualAssignment(string DisplayName, IReadOnlyList<ActorIdentifier> Identifiers, ModCollection Collection);
private readonly Configuration _config;
private readonly ActorService _actorService;
private readonly Dictionary<ActorIdentifier, ModCollection> _individuals = new();
private readonly List<IndividualAssignment> _assignments = new();
private readonly ActorManager _actors;
private readonly Dictionary<ActorIdentifier, ModCollection> _individuals = [];
private readonly List<IndividualAssignment> _assignments = [];
public event Action Loaded;
public bool IsLoaded { get; private set; }
@ -21,12 +23,12 @@ public sealed partial class IndividualCollections
public IReadOnlyList<IndividualAssignment> Assignments
=> _assignments;
public IndividualCollections(ActorService actorService, Configuration config, bool temporary)
public IndividualCollections(ActorManager actors, Configuration config, bool temporary)
{
_config = config;
_actorService = actorService;
IsLoaded = temporary;
Loaded += () => Penumbra.Log.Information($"{_assignments.Count} Individual Assignments loaded after delay.");
_config = config;
_actors = actors;
IsLoaded = temporary;
Loaded += () => Penumbra.Log.Information($"{_assignments.Count} Individual Assignments loaded after delay.");
}
public enum AddResult
@ -69,44 +71,34 @@ public sealed partial class IndividualCollections
return set ? AddResult.AlreadySet : AddResult.Valid;
}
public AddResult CanAdd(IdentifierType type, string name, ushort homeWorld, ObjectKind kind, IEnumerable<uint> dataIds,
public AddResult CanAdd(IdentifierType type, string name, WorldId homeWorld, ObjectKind kind, IEnumerable<NpcId> dataIds,
out ActorIdentifier[] identifiers)
{
identifiers = Array.Empty<ActorIdentifier>();
identifiers = [];
var manager = _actorService.AwaitedService;
switch (type)
{
case IdentifierType.Player:
if (!ByteString.FromString(name, out var playerName))
return AddResult.Invalid;
identifiers = new[]
{
manager.CreatePlayer(playerName, homeWorld),
};
identifiers = [_actors.CreatePlayer(playerName, homeWorld)];
break;
case IdentifierType.Retainer:
if (!ByteString.FromString(name, out var retainerName))
return AddResult.Invalid;
identifiers = new[]
{
manager.CreateRetainer(retainerName, ActorIdentifier.RetainerType.Both),
};
identifiers = [_actors.CreateRetainer(retainerName, ActorIdentifier.RetainerType.Both)];
break;
case IdentifierType.Owned:
if (!ByteString.FromString(name, out var ownerName))
return AddResult.Invalid;
identifiers = dataIds.Select(id => manager.CreateOwned(ownerName, homeWorld, kind, id)).ToArray();
identifiers = dataIds.Select(id => _actors.CreateOwned(ownerName, homeWorld, kind, id)).ToArray();
break;
case IdentifierType.Npc:
identifiers = dataIds
.Select(id => manager.CreateIndividual(IdentifierType.Npc, ByteString.Empty, ushort.MaxValue, kind, id)).ToArray();
break;
default:
identifiers = Array.Empty<ActorIdentifier>();
.Select(id => _actors.CreateIndividual(IdentifierType.Npc, ByteString.Empty, ushort.MaxValue, kind, id)).ToArray();
break;
}
@ -116,12 +108,22 @@ public sealed partial class IndividualCollections
public ActorIdentifier[] GetGroup(ActorIdentifier identifier)
{
if (!identifier.IsValid)
return Array.Empty<ActorIdentifier>();
return [];
return identifier.Type switch
{
IdentifierType.Player => [identifier.CreatePermanent()],
IdentifierType.Special => [identifier],
IdentifierType.Retainer => [identifier.CreatePermanent()],
IdentifierType.Owned => CreateNpcs(_actors, identifier.CreatePermanent()),
IdentifierType.Npc => CreateNpcs(_actors, identifier),
_ => [],
};
static ActorIdentifier[] CreateNpcs(ActorManager manager, ActorIdentifier identifier)
{
var name = manager.Data.ToName(identifier.Kind, identifier.DataId);
var table = identifier.Kind switch
NameDictionary table = identifier.Kind switch
{
ObjectKind.BattleNpc => manager.Data.BNpcs,
ObjectKind.EventNpc => manager.Data.ENpcs,
@ -134,25 +136,6 @@ public sealed partial class IndividualCollections
.Select(kvp => manager.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, identifier.HomeWorld.Id,
identifier.Kind, kvp.Key)).ToArray();
}
return identifier.Type switch
{
IdentifierType.Player => new[]
{
identifier.CreatePermanent(),
},
IdentifierType.Special => new[]
{
identifier,
},
IdentifierType.Retainer => new[]
{
identifier.CreatePermanent(),
},
IdentifierType.Owned => CreateNpcs(_actorService.AwaitedService, identifier.CreatePermanent()),
IdentifierType.Npc => CreateNpcs(_actorService.AwaitedService, identifier),
_ => Array.Empty<ActorIdentifier>(),
};
}
internal bool Add(ActorIdentifier[] identifiers, ModCollection collection)
@ -241,12 +224,12 @@ public sealed partial class IndividualCollections
{
return identifier.Type switch
{
IdentifierType.Player => $"{identifier.PlayerName} ({_actorService.AwaitedService.Data.ToWorldName(identifier.HomeWorld)})",
IdentifierType.Player => $"{identifier.PlayerName} ({_actors.Data.ToWorldName(identifier.HomeWorld)})",
IdentifierType.Retainer => $"{identifier.PlayerName} (Retainer)",
IdentifierType.Owned =>
$"{identifier.PlayerName} ({_actorService.AwaitedService.Data.ToWorldName(identifier.HomeWorld)})'s {_actorService.AwaitedService.Data.ToName(identifier.Kind, identifier.DataId)}",
$"{identifier.PlayerName} ({_actors.Data.ToWorldName(identifier.HomeWorld)})'s {_actors.Data.ToName(identifier.Kind, identifier.DataId)}",
IdentifierType.Npc =>
$"{_actorService.AwaitedService.Data.ToName(identifier.Kind, identifier.DataId)} ({identifier.Kind.ToName()})",
$"{_actors.Data.ToName(identifier.Kind, identifier.DataId)} ({identifier.Kind.ToName()})",
_ => string.Empty,
};
}

View file

@ -14,10 +14,10 @@ public class TempCollectionManager : IDisposable
private readonly CommunicatorService _communicator;
private readonly CollectionStorage _storage;
private readonly ActorService _actors;
private readonly ActorManager _actors;
private readonly Dictionary<string, ModCollection> _customCollections = new();
public TempCollectionManager(Configuration config, CommunicatorService communicator, ActorService actors, CollectionStorage storage)
public TempCollectionManager(Configuration config, CommunicatorService communicator, ActorManager actors, CollectionStorage storage)
{
_communicator = communicator;
_actors = actors;
@ -111,7 +111,7 @@ public class TempCollectionManager : IDisposable
if (!ByteString.FromString(characterName, out var byteString, false))
return false;
var identifier = _actors.AwaitedService.CreatePlayer(byteString, worldId);
var identifier = _actors.CreatePlayer(byteString, worldId);
if (!identifier.IsValid)
return false;
@ -123,7 +123,7 @@ public class TempCollectionManager : IDisposable
if (!ByteString.FromString(characterName, out var byteString, false))
return false;
var identifier = _actors.AwaitedService.CreatePlayer(byteString, worldId);
var identifier = _actors.CreatePlayer(byteString, worldId);
return Collections.TryGetValue(identifier, out var collection) && RemoveTemporaryCollection(collection.Name);
}
}