Add Pcp Events.

This commit is contained in:
Ottermandias 2025-08-22 18:08:22 +02:00
parent fb34238530
commit 10894d451a
9 changed files with 89 additions and 16 deletions

@ -1 +1 @@
Subproject commit 2e26d9119249e67f03f415f8ebe1dcb7c28d5cf2
Subproject commit 0a970295b2398683b1e49c46fd613541e2486210

View file

@ -1,3 +1,4 @@
using Newtonsoft.Json.Linq;
using OtterGui.Compression;
using OtterGui.Services;
using Penumbra.Api.Enums;
@ -33,12 +34,8 @@ public class ModsApi : IPenumbraApiMods, IApiService, IDisposable
{
switch (type)
{
case ModPathChangeType.Deleted when oldDirectory != null:
ModDeleted?.Invoke(oldDirectory.Name);
break;
case ModPathChangeType.Added when newDirectory != null:
ModAdded?.Invoke(newDirectory.Name);
break;
case ModPathChangeType.Deleted when oldDirectory != null: ModDeleted?.Invoke(oldDirectory.Name); break;
case ModPathChangeType.Added when newDirectory != null: ModAdded?.Invoke(newDirectory.Name); break;
case ModPathChangeType.Moved when newDirectory != null && oldDirectory != null:
ModMoved?.Invoke(oldDirectory.Name, newDirectory.Name);
break;
@ -46,7 +43,9 @@ public class ModsApi : IPenumbraApiMods, IApiService, IDisposable
}
public void Dispose()
=> _communicator.ModPathChanged.Unsubscribe(OnModPathChanged);
{
_communicator.ModPathChanged.Unsubscribe(OnModPathChanged);
}
public Dictionary<string, string> GetModList()
=> _modManager.ToDictionary(m => m.ModPath.Name, m => m.Name.Text);
@ -109,6 +108,18 @@ public class ModsApi : IPenumbraApiMods, IApiService, IDisposable
public event Action<string>? ModAdded;
public event Action<string, string>? ModMoved;
public event Action<JObject, ushort>? CreatingPcp
{
add => _communicator.PcpCreation.Subscribe(value!, PcpCreation.Priority.ModsApi);
remove => _communicator.PcpCreation.Unsubscribe(value!);
}
public event Action<JObject, string, Guid>? ParsingPcp
{
add => _communicator.PcpParsing.Subscribe(value!, PcpParsing.Priority.ModsApi);
remove => _communicator.PcpParsing.Unsubscribe(value!);
}
public (PenumbraApiEc, string, bool, bool) GetModPath(string modDirectory, string modName)
{
if (!_modManager.TryGetMod(modDirectory, modName, out var mod)

View file

@ -17,7 +17,7 @@ public class PenumbraApi(
UiApi ui) : IDisposable, IApiService, IPenumbraApi
{
public const int BreakingVersion = 5;
public const int FeatureVersion = 10;
public const int FeatureVersion = 11;
public void Dispose()
{

View file

@ -54,6 +54,8 @@ public sealed class IpcProviders : IDisposable, IApiService
IpcSubscribers.ModDeleted.Provider(pi, api.Mods),
IpcSubscribers.ModAdded.Provider(pi, api.Mods),
IpcSubscribers.ModMoved.Provider(pi, api.Mods),
IpcSubscribers.CreatingPcp.Provider(pi, api.Mods),
IpcSubscribers.ParsingPcp.Provider(pi, api.Mods),
IpcSubscribers.GetModPath.Provider(pi, api.Mods),
IpcSubscribers.SetModPath.Provider(pi, api.Mods),
IpcSubscribers.GetChangedItems.Provider(pi, api.Mods),

View file

@ -0,0 +1,20 @@
using Newtonsoft.Json.Linq;
using OtterGui.Classes;
namespace Penumbra.Communication;
/// <summary>
/// Triggered when the character.json file for a .pcp file is written.
/// <list type="number">
/// <item>Parameter is the JObject that gets written to file. </item>
/// <item>Parameter is the object index of the game object this is written for. </item>
/// </list>
/// </summary>
public sealed class PcpCreation() : EventWrapper<JObject, ushort, PcpCreation.Priority>(nameof(PcpCreation))
{
public enum Priority
{
/// <seealso cref="Api.Api.ModsApi"/>
ModsApi = int.MinValue,
}
}

View file

@ -0,0 +1,21 @@
using Newtonsoft.Json.Linq;
using OtterGui.Classes;
namespace Penumbra.Communication;
/// <summary>
/// Triggered when the character.json file for a .pcp file is parsed and applied.
/// <list type="number">
/// <item>Parameter is parsed JObject that contains the data. </item>
/// <item>Parameter is the identifier of the created mod. </item>
/// <item>Parameter is the GUID of the created collection. </item>
/// </list>
/// </summary>
public sealed class PcpParsing() : EventWrapper<JObject, string, Guid, PcpParsing.Priority>(nameof(PcpParsing))
{
public enum Priority
{
/// <seealso cref="Api.Api.ModsApi"/>
ModsApi = int.MinValue,
}
}

View file

@ -69,6 +69,7 @@ public class Configuration : IPluginConfiguration, ISavable, IService
public bool DefaultTemporaryMode { get; set; } = false;
public bool EnableCustomShapes { get; set; } = true;
public bool DisablePcpHandling { get; set; } = false;
public bool AllowPcpIpc { get; set; } = true;
public RenameField ShowRename { get; set; } = RenameField.BothDataPrio;
public ChangedItemMode ChangedItemDisplay { get; set; } = ChangedItemMode.GroupedCollapsed;
public int OptionGroupCollapsibleMin { get; set; } = 5;

View file

@ -81,6 +81,12 @@ public class CommunicatorService : IDisposable, IService
/// <inheritdoc cref="Communication.ResolvedFileChanged"/>
public readonly ResolvedFileChanged ResolvedFileChanged = new();
/// <inheritdoc cref="Communication.PcpCreation"/>
public readonly PcpCreation PcpCreation = new();
/// <inheritdoc cref="Communication.PcpParsing"/>
public readonly PcpParsing PcpParsing = new();
public void Dispose()
{
CollectionChange.Dispose();
@ -105,5 +111,7 @@ public class CommunicatorService : IDisposable, IService
ChangedItemClick.Dispose();
SelectTab.Dispose();
ResolvedFileChanged.Dispose();
PcpCreation.Dispose();
PcpParsing.Dispose();
}
}

View file

@ -1,4 +1,3 @@
using System.Buffers.Text;
using Dalamud.Game.ClientState.Objects.Types;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Newtonsoft.Json;
@ -76,9 +75,16 @@ public class PcpService : IApiService, IDisposable
try
{
var file = Path.Combine(newDirectory.FullName, "collection.json");
var file = Path.Combine(newDirectory.FullName, "character.json");
if (!File.Exists(file))
return;
{
// First version had collection.json, changed.
var oldFile = Path.Combine(newDirectory.FullName, "collection.json");
if (File.Exists(oldFile))
File.Move(oldFile, file, true);
else
return;
}
var text = File.ReadAllText(file);
var jObj = JObject.Parse(text);
@ -110,10 +116,12 @@ public class PcpService : IApiService, IDisposable
// ignored.
}
}
if (_config.AllowPcpIpc)
_communicator.PcpParsing.Invoke(jObj, mod.Identifier, collection.Identity.Id);
}
catch (Exception ex)
{
Penumbra.Log.Error($"Error reading the collection.json file from {mod.Identifier}:\n{ex}");
Penumbra.Log.Error($"Error reading the character.json file from {mod.Identifier}:\n{ex}");
}
}
@ -145,7 +153,7 @@ public class PcpService : IApiService, IDisposable
var time = DateTime.Now;
var modDirectory = CreateMod(identifier, note, time);
await CreateDefaultMod(modDirectory, collection.ModCollection, tree, cancel);
await CreateCollectionInfo(modDirectory, identifier, note, time, cancel);
await CreateCollectionInfo(modDirectory, objectIndex, identifier, note, time, cancel);
var file = ZipUp(modDirectory);
return (true, file);
}
@ -163,7 +171,7 @@ public class PcpService : IApiService, IDisposable
return fileName;
}
private static async Task CreateCollectionInfo(DirectoryInfo directory, ActorIdentifier actor, string note, DateTime time,
private async Task CreateCollectionInfo(DirectoryInfo directory, ObjectIndex index, ActorIdentifier actor, string note, DateTime time,
CancellationToken cancel = default)
{
var jObj = new JObject
@ -176,7 +184,9 @@ public class PcpService : IApiService, IDisposable
};
if (note.Length > 0)
cancel.ThrowIfCancellationRequested();
var filePath = Path.Combine(directory.FullName, "collection.json");
if (_config.AllowPcpIpc)
await _framework.Framework.RunOnFrameworkThread(() => _communicator.PcpCreation.Invoke(jObj, index.Index));
var filePath = Path.Combine(directory.FullName, "character.json");
await using var file = File.Open(filePath, File.Exists(filePath) ? FileMode.Truncate : FileMode.CreateNew);
await using var stream = new StreamWriter(file);
await using var json = new JsonTextWriter(stream);