Skip to content

Commit

Permalink
Perfectly reasonable compat code for penumbra api.
Browse files Browse the repository at this point in the history
  • Loading branch information
Kurochi51 committed Apr 15, 2024
1 parent 827c8bf commit 669ee74
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 32 deletions.
96 changes: 96 additions & 0 deletions TickTracker/IPC/PenumbraEnums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,100 @@ public enum ModSettingChange
MultiEnableState,
}

public enum ApiCollectionType : byte
{
Yourself = 0,

MalePlayerCharacter,
FemalePlayerCharacter,
MaleNonPlayerCharacter,
FemaleNonPlayerCharacter,
NonPlayerChild,
NonPlayerElderly,

MaleMidlander,
FemaleMidlander,
MaleHighlander,
FemaleHighlander,

MaleWildwood,
FemaleWildwood,
MaleDuskwight,
FemaleDuskwight,

MalePlainsfolk,
FemalePlainsfolk,
MaleDunesfolk,
FemaleDunesfolk,

MaleSeekerOfTheSun,
FemaleSeekerOfTheSun,
MaleKeeperOfTheMoon,
FemaleKeeperOfTheMoon,

MaleSeawolf,
FemaleSeawolf,
MaleHellsguard,
FemaleHellsguard,

MaleRaen,
FemaleRaen,
MaleXaela,
FemaleXaela,

MaleHelion,
FemaleHelion,
MaleLost,
FemaleLost,

MaleRava,
FemaleRava,
MaleVeena,
FemaleVeena,

MaleMidlanderNpc,
FemaleMidlanderNpc,
MaleHighlanderNpc,
FemaleHighlanderNpc,

MaleWildwoodNpc,
FemaleWildwoodNpc,
MaleDuskwightNpc,
FemaleDuskwightNpc,

MalePlainsfolkNpc,
FemalePlainsfolkNpc,
MaleDunesfolkNpc,
FemaleDunesfolkNpc,

MaleSeekerOfTheSunNpc,
FemaleSeekerOfTheSunNpc,
MaleKeeperOfTheMoonNpc,
FemaleKeeperOfTheMoonNpc,

MaleSeawolfNpc,
FemaleSeawolfNpc,
MaleHellsguardNpc,
FemaleHellsguardNpc,

MaleRaenNpc,
FemaleRaenNpc,
MaleXaelaNpc,
FemaleXaelaNpc,

MaleHelionNpc,
FemaleHelionNpc,
MaleLostNpc,
FemaleLostNpc,

MaleRavaNpc,
FemaleRavaNpc,
MaleVeenaNpc,
FemaleVeenaNpc,

Default = 0xE0,
Interface = 0xE1,
Current = 0xE2,
}


140 changes: 112 additions & 28 deletions TickTracker/IPC/PenumbraIpc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,28 @@ public sealed class PenumbraIpc : IDisposable
{
private readonly IPluginLog log;

private readonly ICallGateSubscriber<(int Breaking, int FeatureLevel)> apiVersions;
private readonly ICallGateSubscriber<bool> penumbraModsState;
private readonly ICallGateSubscriber<object> penumbraInit;
private readonly ICallGateSubscriber<object> penumbraDispose;
private readonly ICallGateSubscriber<string> interfaceCollection;
private readonly ICallGateSubscriber<(int Breaking, int FeatureLevel)> apiVersions;
private readonly ICallGateSubscriber<IList<(string modDirectory, string modName)>> mods;
private readonly ICallGateSubscriber<string, string, string, bool, (PenumbraApiEc status, (bool modEnabled, int priority, IDictionary<string, IList<string>> optionDetails, bool settingsInherited)? settings)> modSettings;
private readonly ICallGateSubscriber<ModSettingChange, string, string, bool, Action?> modSettingsChanged;
private readonly ICallGateSubscriber<bool, Action<bool>?> penumbraEnabledChange;
/// <summary>
/// Key is directory name, Value is mod name.
/// </summary>
private readonly ICallGateSubscriber<Dictionary<string, string>> modList;
private readonly ICallGateSubscriber<Guid, string, string, bool,
(PenumbraApiEc status, (bool modEnabled, int priority, Dictionary<string, List<string>> optionDetails, bool ignoreInheritance)? settings)> modSettings;
private readonly ICallGateSubscriber<ModSettingChange, Guid, string, bool, Action?> modSettingsChanged;
private readonly (Guid Id, string Name) interfaceCollection;

[Obsolete("Changed with Penumbra API rework")]
private readonly ICallGateSubscriber<string> oldInterfaceCollection;
[Obsolete("Changed with Penumbra API rework")]
private readonly ICallGateSubscriber<IList<(string modDirectory, string modName)>> oldModList;
[Obsolete("Changed with Penumbra API rework")]
private readonly ICallGateSubscriber<string, string, string, bool, (PenumbraApiEc status, (bool modEnabled, int priority, IDictionary<string, IList<string>> optionDetails, bool settingsInherited)? settings)> oldModSettings;
[Obsolete("Changed with Penumbra API rework")]
private readonly ICallGateSubscriber<ModSettingChange, string, string, bool, Action?> oldModSettingsChanged;

public bool NativeUiBanned { get; private set; }

Expand Down Expand Up @@ -55,57 +68,113 @@ private bool penumbraModsEnabled
}
}

#pragma warning disable CS8618
public PenumbraIpc(DalamudPluginInterface _pluginInterface, IPluginLog _pluginLog)
{
log = _pluginLog;

apiVersions = _pluginInterface.GetIpcSubscriber<(int, int)>("Penumbra.ApiVersions");

if (penumbraApiVersion.Breaking is not 4)
try
{
throw new NotSupportedException("Penumbra API out of date. Version " + penumbraApiVersion.Breaking.ToString(CultureInfo.InvariantCulture));
apiVersions = _pluginInterface.GetIpcSubscriber<(int, int)>("Penumbra.ApiVersions");
}
if (penumbraApiVersion.FeatureLevel is not 0 and not 23)
catch
{
log.Debug("Penumbra API Feature Level changed {ver}", penumbraApiVersion.FeatureLevel);
apiVersions = _pluginInterface.GetIpcSubscriber<(int, int)>("Penumbra.ApiVersion");
}

if (penumbraApiVersion.Breaking is not 4 and not 5)
{
throw new NotSupportedException("Penumbra API out of date. Version " + penumbraApiVersion.Breaking.ToString(CultureInfo.InvariantCulture));
}

penumbraModsState = _pluginInterface.GetIpcSubscriber<bool>("Penumbra.GetEnabledState");
interfaceCollection = _pluginInterface.GetIpcSubscriber<string>("Penumbra.GetInterfaceCollectionName");
mods = _pluginInterface.GetIpcSubscriber<IList<(string modDirectory, string modName)>>("Penumbra.GetMods");
modSettings = _pluginInterface.GetIpcSubscriber<string, string, string, bool, (PenumbraApiEc status, (bool modEnabled, int priority, IDictionary<string, IList<string>> optionDetails, bool settingsInherited)? settings)>("Penumbra.GetCurrentModSettings");
penumbraInit = _pluginInterface.GetIpcSubscriber<object>("Penumbra.Initialized");
penumbraDispose = _pluginInterface.GetIpcSubscriber<object>("Penumbra.Disposed");
penumbraEnabledChange = _pluginInterface.GetIpcSubscriber<bool, Action<bool>?>("Penumbra.EnabledChange");
modSettingsChanged = _pluginInterface.GetIpcSubscriber<ModSettingChange, string, string, bool, Action?>("Penumbra.ModSettingChanged");
if (penumbraApiVersion.Breaking is 4)
{
oldInterfaceCollection = _pluginInterface.GetIpcSubscriber<string>("Penumbra.GetInterfaceCollectionName");
oldModList = _pluginInterface.GetIpcSubscriber<IList<(string modDirectory, string modName)>>("Penumbra.GetMods");
oldModSettings = _pluginInterface.GetIpcSubscriber<string, string, string, bool, (PenumbraApiEc status, (bool modEnabled, int priority, IDictionary<string, IList<string>> optionDetails, bool settingsInherited)? settings)>("Penumbra.GetCurrentModSettings");
oldModSettingsChanged = _pluginInterface.GetIpcSubscriber<ModSettingChange, string, string, bool, Action?>("Penumbra.ModSettingChanged");
oldModSettingsChanged.Subscribe(OldCheckModChanges);
}
else if (penumbraApiVersion.Breaking is 5)
{
interfaceCollection = _pluginInterface.GetIpcSubscriber<ApiCollectionType, (Guid id, string Name)>("Penumbra.GetCollection").InvokeFunc(ApiCollectionType.Interface);
modList = _pluginInterface.GetIpcSubscriber<Dictionary<string, string>>("Penumbra.GetModList");
modSettings = _pluginInterface.GetIpcSubscriber<Guid, string, string, bool,
(PenumbraApiEc status, (bool modEnabled, int priority, Dictionary<string, List<string>> optionDetails, bool ignoreInheritance)? settings)>
("Penumbra.GetCurrentModSettings");
modSettingsChanged = _pluginInterface.GetIpcSubscriber<ModSettingChange, Guid, string, bool, Action?>("Penumbra.ModSettingChanged");
modSettingsChanged.Subscribe(CheckModChanges);
}

penumbraEnabledChange.Subscribe(CheckState);
modSettingsChanged.Subscribe(CheckModChanges);
penumbraInit.Subscribe(PenumbraInit);
penumbraDispose.Subscribe(PenumbraDispose);

if (penumbraModsEnabled)
{
NativeUiBanned = CheckMUIPresence(mods.InvokeFunc(), interfaceCollection.InvokeFunc());
NativeUiBanned = penumbraApiVersion.Breaking is 4 ? OldCheckMUIPresence(oldModList!.InvokeFunc(), oldInterfaceCollection!.InvokeFunc())
: CheckMUIPresence(modList!.InvokeFunc(), interfaceCollection.Id);
}
}
#pragma warning restore CS8618

private void CheckState(bool penumbraEnabled)
{
NativeUiBanned = penumbraEnabled && CheckMUIPresence(mods.InvokeFunc(), interfaceCollection.InvokeFunc());
NativeUiBanned = penumbraEnabled
&& penumbraApiVersion.Breaking == 4 ? OldCheckMUIPresence(oldModList.InvokeFunc(), oldInterfaceCollection.InvokeFunc())
: CheckMUIPresence(modList.InvokeFunc(), interfaceCollection.Id);
}

private void CheckModChanges(ModSettingChange type, Guid collectionId, string modDirectory, bool inherited)
{
if ((type is not ModSettingChange.EnableState && type is not ModSettingChange.Inheritance) || interfaceCollection.Id != collectionId)
{
return;
}
var currentMods = modList.InvokeFunc().Where(currentMod => currentMod.Key.Equals(modDirectory, StringComparison.Ordinal)).ToDictionary(StringComparer.Ordinal);
NativeUiBanned = CheckMUIPresence(currentMods, collectionId);
}

private bool CheckMUIPresence(Dictionary<string, string> modList, Guid collection)
{
if (modList.Count < 1)
{
return false;
}
foreach (var mod in modList)
{
if (!mod.Value.Contains("Material UI", StringComparison.OrdinalIgnoreCase)
&& !mod.Key.Contains("Material UI", StringComparison.OrdinalIgnoreCase))
{
continue;
}
var modDetails = modSettings.InvokeFunc(collection, mod.Key, mod.Value, arg4: false);
if (modDetails.status is not PenumbraApiEc.Success || !modDetails.settings.HasValue)
{
log.Error("Failed to retrieve mod details. {status}", modDetails.status);
continue;
}
return modDetails.settings.Value.modEnabled;
}
return false;
}

private void CheckModChanges(ModSettingChange type, string collectionName, string modDirectory, bool inherited)

private void OldCheckModChanges(ModSettingChange type, string collectionName, string modDirectory, bool inherited)
{
if ((type is not ModSettingChange.EnableState && type is not ModSettingChange.Inheritance) || !interfaceCollection.InvokeFunc().Equals(collectionName, StringComparison.Ordinal))
if ((type is not ModSettingChange.EnableState && type is not ModSettingChange.Inheritance) || !oldInterfaceCollection.InvokeFunc().Equals(collectionName, StringComparison.Ordinal))
{
return;
}
var modList = mods.InvokeFunc().Where(currentMod => currentMod.modDirectory.Equals(modDirectory, StringComparison.Ordinal));
NativeUiBanned = CheckMUIPresence(modList, collectionName);
var currentMods = oldModList.InvokeFunc().Where(currentMod => currentMod.modDirectory.Equals(modDirectory, StringComparison.Ordinal));
NativeUiBanned = OldCheckMUIPresence(currentMods, collectionName);
}

private bool CheckMUIPresence(IEnumerable<(string modDirectory, string modName)> modList, string collection)
private bool OldCheckMUIPresence(IEnumerable<(string modDirectory, string modName)> modList, string collection)
{
if (!modList.Any())
{
Expand All @@ -117,8 +186,7 @@ private bool CheckMUIPresence(IEnumerable<(string modDirectory, string modName)>
{
continue;
}
// Inheritance bool arg4 is flipped
var modDetails = modSettings.InvokeFunc(collection, mod.modDirectory, mod.modName, arg4: false);
var modDetails = oldModSettings.InvokeFunc(collection, mod.modDirectory, mod.modName, arg4: false);
if (modDetails.status is not PenumbraApiEc.Success || !modDetails.settings.HasValue)
{
log.Error("Failed to retrieve mod details. {stat}", modDetails.status);
Expand All @@ -128,21 +196,37 @@ private bool CheckMUIPresence(IEnumerable<(string modDirectory, string modName)>
}
return false;
}


private void PenumbraInit()
{
penumbraEnabledChange.Subscribe(CheckState);
modSettingsChanged.Subscribe(CheckModChanges);
if (penumbraApiVersion.Breaking is 4)
{
oldModSettingsChanged.Subscribe(OldCheckModChanges);
}
else if (penumbraApiVersion.Breaking is 5)
{
modSettingsChanged.Subscribe(CheckModChanges);
}
if (penumbraModsEnabled)
{
NativeUiBanned = CheckMUIPresence(mods.InvokeFunc(), interfaceCollection.InvokeFunc());
NativeUiBanned = penumbraApiVersion.Breaking is 4 ? OldCheckMUIPresence(oldModList.InvokeFunc(), oldInterfaceCollection.InvokeFunc())
: CheckMUIPresence(modList.InvokeFunc(), interfaceCollection.Id);
}
}

private void PenumbraDispose()
{
penumbraEnabledChange.Unsubscribe(CheckState);
modSettingsChanged.Unsubscribe(CheckModChanges);
if (penumbraApiVersion.Breaking is 4)
{
oldModSettingsChanged.Unsubscribe(OldCheckModChanges);
}
else if (penumbraApiVersion.Breaking is 5)
{
modSettingsChanged.Unsubscribe(CheckModChanges);
}
NativeUiBanned = false;
}

Expand Down
2 changes: 1 addition & 1 deletion TickTracker/TickTracker.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

<ItemGroup>
<PackageReference Include="DalamudPackager" Version="2.1.12" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.146">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.147">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
6 changes: 3 additions & 3 deletions TickTracker/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
},
"Meziantou.Analyzer": {
"type": "Direct",
"requested": "[2.0.146, )",
"resolved": "2.0.146",
"contentHash": "pJ3k+3UjD2WwPsZR7fkgVYviXP3PCQM0hjwN1TuM4C0u1TuGKC9WdZCo0RAPZ+azhRPmYGSUjkjsN2fkQByghQ=="
"requested": "[2.0.147, )",
"resolved": "2.0.147",
"contentHash": "bloVeZFFQKeJ8QO65XgN1FTHvzKGU523otZjDvQU4Wu7FLWbd/AjWoDFlCaSpBiC+awD/B8Q7CuwINJEVMsk+Q=="
}
}
}
Expand Down

0 comments on commit 669ee74

Please sign in to comment.