Skip to content

Commit

Permalink
Add Penumbra IPC to disable native ui elements when MaterialUI is det…
Browse files Browse the repository at this point in the history
…ected.

Changed default NativeUI color, and more accurately assign ImageNode color.
  • Loading branch information
Kurochi51 committed Feb 8, 2024
1 parent e2e36c9 commit c6a882b
Show file tree
Hide file tree
Showing 8 changed files with 304 additions and 16 deletions.
2 changes: 2 additions & 0 deletions TickTracker/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ indent_style = space
indent_size = 4

# disable redundant style warnings
MA0051.maximum_lines_per_method = 80
MA0051.skip_local_functions = true

# .NET diagnostic warnings
dotnet_diagnostic.CS8524.severity = suggestion
Expand Down
48 changes: 48 additions & 0 deletions TickTracker/Helpers/Utilities.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Linq;
using System.Timers;
using System.Numerics;
using System.Threading;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Collections.Generic;
Expand Down Expand Up @@ -294,6 +296,52 @@ public async Task Loading(long pollingPeriodMilliseconds)
loadingTimer.Reset();
}

public async Task<bool> CheckIPC(double msInterval, System.Action func, CancellationToken cToken)
{
try
{
func.DynamicInvoke();
return true;
}
catch
{
var timer = new System.Timers.Timer();
timer.Interval = msInterval;
timer.AutoReset = true;
timer.Elapsed += timerCheck;
timer.Start();
var ipcAvailable = false;

void timerCheck(object? sender, ElapsedEventArgs e)
{
if (cToken.IsCancellationRequested)
{
timer.Stop();
return;
}
try
{
func.DynamicInvoke();
ipcAvailable = true;
timer.Stop();
}
catch
{
log.Warning("IPC not available.");
ipcAvailable = false;
}
}

while (timer.Enabled && !cToken.IsCancellationRequested)
{
await Task.Delay((int)msInterval, cToken).ConfigureAwait(false);
}
timer.Dispose();

return ipcAvailable;
}
}

/// <summary>
/// Attempt to retrieve an <see cref="ExcelSheet{T}"/>, optionally in a specific <paramref name="language"/>.
/// </summary>
Expand Down
40 changes: 40 additions & 0 deletions TickTracker/IPC/PenumbraEnums.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
namespace TickTracker.IPC;

#pragma warning disable MA0048 // File name must match type name

public enum PenumbraApiEc
{
Success = 0,
NothingChanged = 1,
CollectionMissing = 2,
ModMissing = 3,
OptionGroupMissing = 4,
OptionMissing = 5,

CharacterCollectionExists = 6,
LowerPriority = 7,
InvalidGamePath = 8,
FileMissing = 9,
InvalidManipulation = 10,
InvalidArgument = 11,
PathRenameFailed = 12,
CollectionExists = 13,
AssignmentCreationDisallowed = 14,
AssignmentDeletionDisallowed = 15,
InvalidIdentifier = 16,
SystemDisposed = 17,
AssignmentDeletionFailed = 18,
UnknownError = 255,
}

public enum ModSettingChange
{
Inheritance,
EnableState,
Priority,
Setting,
MultiInheritance,
MultiEnableState,
}


153 changes: 153 additions & 0 deletions TickTracker/IPC/PenumbraIpc.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using System.Linq;

using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using Dalamud.Plugin.Services;

namespace TickTracker.IPC;

public sealed class PenumbraIpc : IDisposable
{
private readonly IPluginLog log;

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;

public bool NativeUiBanned { get; private set; }

private bool penumbraModsEnabled
{
get
{
try
{
return penumbraModsState.InvokeFunc();
}
catch
{
return false;
}
}
}

private int penumbraApiVersion
{
get
{
try
{
return apiVersions.InvokeFunc().Breaking;
}
catch
{
return 0;
}
}
}

public PenumbraIpc(DalamudPluginInterface _pluginInterface, IPluginLog _pluginLog)
{
log = _pluginLog;

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

if (penumbraApiVersion != 4)
{
throw new NotSupportedException("Penumbra API out of date.");
}

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");

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

if (penumbraModsEnabled)
{
NativeUiBanned = CheckMUIPresence(mods.InvokeFunc(), interfaceCollection.InvokeFunc());
}
}

private void CheckState(bool penumbraEnabled)
{
var state = penumbraEnabled ? "enabled" : "disabled";
log.Warning("Penumbra is " + state);
NativeUiBanned = penumbraEnabled && CheckMUIPresence(mods.InvokeFunc(), interfaceCollection.InvokeFunc());
}

private void CheckModChanges(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))
{
return;
}
var modList = mods.InvokeFunc().Where(currentMod => currentMod.modDirectory.Equals(modDirectory, StringComparison.Ordinal));
NativeUiBanned = CheckMUIPresence(modList, collectionName);
}

private bool CheckMUIPresence(IEnumerable<(string modDirectory, string modName)> modList, string collection)
{
if (!modList.Any())
{
return false;
}
foreach (var mod in modList)
{
if (!mod.modName.Contains("Material UI", StringComparison.OrdinalIgnoreCase) && !mod.modDirectory.Contains("Material UI", StringComparison.OrdinalIgnoreCase))
{
continue;
}
var modDetails = modSettings.InvokeFunc(collection, mod.modDirectory, mod.modName, false);
if (modDetails.status is not PenumbraApiEc.Success || !modDetails.settings.HasValue)
{
log.Error("Failed to retrieve mod details. {stat}", modDetails.status);
continue;
}
var state = modDetails.settings.Value.modEnabled ? "enabled" : "disabled";
log.Warning("{name} is " + state, mod.modName);
return modDetails.settings.Value.modEnabled;
}
return false;
}

private void PenumbraInit()
{
penumbraEnabledChange.Subscribe(CheckState);
modSettingsChanged.Subscribe(CheckModChanges);
if (penumbraModsEnabled)
{
NativeUiBanned = CheckMUIPresence(mods.InvokeFunc(), interfaceCollection.InvokeFunc());
}
}

private void PenumbraDispose()
{
penumbraEnabledChange.Unsubscribe(CheckState);
modSettingsChanged.Unsubscribe(CheckModChanges);
NativeUiBanned = false;
}

public void Dispose()
{
PenumbraDispose();
penumbraInit.Unsubscribe(PenumbraInit);
penumbraDispose.Unsubscribe(PenumbraDispose);
}
}
1 change: 1 addition & 0 deletions TickTracker/NativeNodes/ImageNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ public void ChangeNodeColorAndAlpha(Vector4 Color)
{
return;
}
imageNode->AtkResNode.Color = Dalamud.Utility.Numerics.VectorExtensions.ToByteColor(Color);
imageNode->AtkResNode.MultiplyRed = (byte)(255 * Color.X);
imageNode->AtkResNode.MultiplyGreen = (byte)(255 * Color.Y);
imageNode->AtkResNode.MultiplyBlue = (byte)(255 * Color.Z);
Expand Down
Loading

0 comments on commit c6a882b

Please sign in to comment.