From 732ca561a191b6bb1619123ea07d90a11aca6f4b Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Fri, 4 Nov 2022 22:41:50 +0100 Subject: [PATCH] Some stuff --- Penumbra.Api | 2 +- .../Actors/ActorManager.Identifiers.cs | 96 +++++++++++++++---- Penumbra.GameData/ObjectIdentification.cs | 2 +- Penumbra.String | 2 +- .../Collections/CollectionManager.Active.cs | 27 ++++-- 5 files changed, 101 insertions(+), 28 deletions(-) diff --git a/Penumbra.Api b/Penumbra.Api index f41af0fb..27e8873e 160000 --- a/Penumbra.Api +++ b/Penumbra.Api @@ -1 +1 @@ -Subproject commit f41af0fb88626f1579d3c4370b32b901f3c4d3c2 +Subproject commit 27e8873e9f4633421e736757574b502a8d65e79d diff --git a/Penumbra.GameData/Actors/ActorManager.Identifiers.cs b/Penumbra.GameData/Actors/ActorManager.Identifiers.cs index 4caba6e5..c504470e 100644 --- a/Penumbra.GameData/Actors/ActorManager.Identifiers.cs +++ b/Penumbra.GameData/Actors/ActorManager.Identifiers.cs @@ -1,8 +1,10 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime.Serialization; using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Types; +using Lumina.Excel.GeneratedSheets; using Newtonsoft.Json.Linq; using Penumbra.String; @@ -157,7 +159,8 @@ public partial class ActorManager ((FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)actor)->NameID); } - return CreateNpc(ObjectKind.BattleNpc, ((FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)actor)->NameID, actor->ObjectIndex); + return CreateNpc(ObjectKind.BattleNpc, ((FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)actor)->NameID, + actor->ObjectIndex); } case ObjectKind.EventNpc: return CreateNpc(ObjectKind.EventNpc, actor->DataID, actor->ObjectIndex); case ObjectKind.MountType: @@ -206,7 +209,7 @@ public partial class ActorManager public ActorIdentifier CreatePlayer(ByteString name, ushort homeWorld) { - if (!VerifyWorld(homeWorld) || !VerifyPlayerName(name)) + if (!VerifyWorld(homeWorld) || !VerifyPlayerName(name.Span)) return ActorIdentifier.Invalid; return new ActorIdentifier(IdentifierType.Player, ObjectKind.Player, homeWorld, 0, name); @@ -230,26 +233,25 @@ public partial class ActorManager public ActorIdentifier CreateOwned(ByteString ownerName, ushort homeWorld, ObjectKind kind, uint dataId) { - if (!VerifyWorld(homeWorld) || !VerifyPlayerName(ownerName) || !VerifyOwnedData(kind, dataId)) + if (!VerifyWorld(homeWorld) || !VerifyPlayerName(ownerName.Span) || !VerifyOwnedData(kind, dataId)) return ActorIdentifier.Invalid; return new ActorIdentifier(IdentifierType.Owned, kind, homeWorld, dataId, ownerName); } /// Checks SE naming rules. - private static bool VerifyPlayerName(ByteString name) + public static bool VerifyPlayerName(ReadOnlySpan name) { // Total no more than 20 characters + space. if (name.Length is < 5 or > 21) return false; - var split = name.Split((byte)' '); - // Forename and surname, no more spaces. - if (split.Count != 2) + var splitIndex = name.IndexOf((byte)' '); + if (splitIndex < 0 || name[(splitIndex + 1)..].IndexOf((byte)' ') >= 0) return false; - static bool CheckNamePart(ByteString part) + static bool CheckNamePart(ReadOnlySpan part) { // Each name part at least 2 and at most 15 characters. if (part.Length is < 2 or > 15) @@ -260,27 +262,83 @@ public partial class ActorManager return false; // Every other symbol needs to be lowercase letter, hyphen or apostrophe. - if (part.Skip(1).Any(c => c != (byte)'\'' && c != (byte)'-' && c is < (byte)'a' or > (byte)'z')) - return false; + var last = (byte)'\0'; + for (var i = 1; i < part.Length; ++i) + { + var current = part[i]; + if (current is not ((byte)'\'' or (byte)'-' or (>= (byte)'a' and <= (byte)'z'))) + return false; - var hyphens = part.Split((byte)'-'); - // Apostrophes can not be used in succession, after or before apostrophes. - return !hyphens.Any(p => p.Length == 0 || p[0] == (byte)'\'' || p.Last() == (byte)'\''); + // Hyphens can not be used in succession, after or before apostrophes or as the last symbol. + if (last is (byte)'\'' && current is (byte)'-') + return false; + if (last is (byte)'-' && current is (byte)'-' or (byte)'\'') + return false; + + last = current; + } + + return part[^1] != (byte)'-'; } - return CheckNamePart(split[0]) && CheckNamePart(split[1]); + return CheckNamePart(name[..splitIndex]) && CheckNamePart(name[(splitIndex + 1)..]); + } + + /// Checks SE naming rules. + public static bool VerifyPlayerName(ReadOnlySpan name) + { + // Total no more than 20 characters + space. + if (name.Length is < 5 or > 21) + return false; + + // Forename and surname, no more spaces. + var splitIndex = name.IndexOf(' '); + if (splitIndex < 0 || name[(splitIndex + 1)..].IndexOf(' ') >= 0) + return false; + + static bool CheckNamePart(ReadOnlySpan part) + { + // Each name part at least 2 and at most 15 characters. + if (part.Length is < 2 or > 15) + return false; + + // Each part starting with capitalized letter. + if (part[0] is < 'A' or > 'Z') + return false; + + // Every other symbol needs to be lowercase letter, hyphen or apostrophe. + var last = '\0'; + for (var i = 1; i < part.Length; ++i) + { + var current = part[i]; + if (current is not ('\'' or '-' or (>= 'a' and <= 'z'))) + return false; + + // Hyphens can not be used in succession, after or before apostrophes or as the last symbol. + if (last is '\'' && current is '-') + return false; + if (last is '-' && current is '-' or '\'') + return false; + + last = current; + } + + return part[^1] != '-'; + } + + return CheckNamePart(name[..splitIndex]) && CheckNamePart(name[(splitIndex + 1)..]); } /// Checks if the world is a valid public world or ushort.MaxValue (any world). - private bool VerifyWorld(ushort worldId) + public bool VerifyWorld(ushort worldId) => Worlds.ContainsKey(worldId); /// Verify that the enum value is a specific actor and return the name if it is. - private static bool VerifySpecial(SpecialActor actor) + public static bool VerifySpecial(SpecialActor actor) => actor is >= SpecialActor.CharacterScreen and <= SpecialActor.Portrait; /// Verify that the object index is a valid index for an NPC. - private static bool VerifyIndex(ushort index) + public static bool VerifyIndex(ushort index) { return index switch { @@ -291,7 +349,7 @@ public partial class ActorManager } /// Verify that the object kind is a valid owned object, and the corresponding data Id. - private bool VerifyOwnedData(ObjectKind kind, uint dataId) + public bool VerifyOwnedData(ObjectKind kind, uint dataId) { return kind switch { @@ -302,7 +360,7 @@ public partial class ActorManager }; } - private bool VerifyNpcData(ObjectKind kind, uint dataId) + public bool VerifyNpcData(ObjectKind kind, uint dataId) => kind switch { ObjectKind.BattleNpc => BNpcs.ContainsKey(dataId), diff --git a/Penumbra.GameData/ObjectIdentification.cs b/Penumbra.GameData/ObjectIdentification.cs index d958f62c..ddffe9e2 100644 --- a/Penumbra.GameData/ObjectIdentification.cs +++ b/Penumbra.GameData/ObjectIdentification.cs @@ -49,7 +49,7 @@ internal class ObjectIdentification : IObjectIdentifier var (begin, _) = FindIndexRange((List<(ulong, IReadOnlyList)>)_weapons, ((ulong)setId << 32) | ((ulong)weaponType << 16) | variant, 0xFFFFFFFFFFFF); - return begin >= 0 ? _weapons[begin].Item2 : null; + return begin >= 0 ? _weapons[begin].Item2 : Array.Empty(); } default: { diff --git a/Penumbra.String b/Penumbra.String index 99f3b4f3..81539a96 160000 --- a/Penumbra.String +++ b/Penumbra.String @@ -1 +1 @@ -Subproject commit 99f3b4f3c7fd9f83b0741089e8808566a0bbdde5 +Subproject commit 81539a968f6bfbb78c34ae1094cce88ae4c9ac88 diff --git a/Penumbra/Collections/CollectionManager.Active.cs b/Penumbra/Collections/CollectionManager.Active.cs index 624be9cc..0e6e1730 100644 --- a/Penumbra/Collections/CollectionManager.Active.cs +++ b/Penumbra/Collections/CollectionManager.Active.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Threading.Tasks; using Dalamud.Game.ClientState.Objects.Enums; using Penumbra.GameData.Actors; +using Penumbra.String; namespace Penumbra.Collections; @@ -20,20 +21,34 @@ public class IndividualCollections public readonly Dictionary< ActorIdentifier, ModCollection > Individuals; public IndividualCollections( ActorManager manager ) - { - _manager = manager; - } + => _manager = manager; public bool CanAdd( IdentifierType type, string name, ushort homeWorld, ObjectKind kind, uint dataId ) { - _manager. + return false; } - public bool CanAdd( IdentifierType type, string name, ushort homeWorld, ObjectKind kind, IReadOnlyList< uint > dataIds ) + public bool Add( string displayName, ActorIdentifier identifier, ModCollection collection ) + => Add( displayName, identifier, collection, Array.Empty< uint >() ); + + public bool Add( string displayName, ActorIdentifier identifier, ModCollection collection, IEnumerable< uint > additionalIds ) { + if( Individuals.ContainsKey( identifier ) ) + { + return false; + } + //var identifiers = additionalIds + // .Select( id => CanAdd( identifier.Type, identifier.PlayerName, identifier.HomeWorld, identifier.Kind, id, out var value ) ? value : ActorIdentifier.Invalid ) + // .Prepend( identifier ) + // .ToArray(); + //if( identifiers.Any( i => !i.IsValid || i.DataId == identifier.DataId ) ) + //{ + // return false; + //} + + return true; } - } public partial class ModCollection