mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Config change event (#1301)
* Add events for config changes * Update ConfigChangeEvent.cs * change event names --------- Co-authored-by: goat <16760685+goaaats@users.noreply.github.com>
This commit is contained in:
parent
e52f7696ba
commit
7109f21387
5 changed files with 131 additions and 5 deletions
7
Dalamud/Game/Config/ConfigChangeEvent.cs
Normal file
7
Dalamud/Game/Config/ConfigChangeEvent.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Dalamud.Game.Config;
|
||||||
|
|
||||||
|
public abstract record ConfigChangeEvent(Enum Option);
|
||||||
|
|
||||||
|
public record ConfigChangeEvent<T>(T ConfigOption) : ConfigChangeEvent(ConfigOption) where T : Enum;
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
using Dalamud.IoC;
|
using System;
|
||||||
|
using Dalamud.Hooking;
|
||||||
|
using Dalamud.IoC;
|
||||||
using Dalamud.IoC.Internal;
|
using Dalamud.IoC.Internal;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
using FFXIVClientStructs.FFXIV.Common.Configuration;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Dalamud.Game.Config;
|
namespace Dalamud.Game.Config;
|
||||||
|
|
@ -14,10 +18,13 @@ namespace Dalamud.Game.Config;
|
||||||
#pragma warning disable SA1015
|
#pragma warning disable SA1015
|
||||||
[ResolveVia<IGameConfig>]
|
[ResolveVia<IGameConfig>]
|
||||||
#pragma warning restore SA1015
|
#pragma warning restore SA1015
|
||||||
public sealed class GameConfig : IServiceType, IGameConfig
|
public sealed class GameConfig : IServiceType, IGameConfig, IDisposable
|
||||||
{
|
{
|
||||||
|
private readonly GameConfigAddressResolver address = new();
|
||||||
|
private Hook<ConfigChangeDelegate>? configChangeHook;
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
private unsafe GameConfig(Framework framework)
|
private unsafe GameConfig(Framework framework, SigScanner sigScanner)
|
||||||
{
|
{
|
||||||
framework.RunOnTick(() =>
|
framework.RunOnTick(() =>
|
||||||
{
|
{
|
||||||
|
|
@ -27,9 +34,18 @@ public sealed class GameConfig : IServiceType, IGameConfig
|
||||||
this.System = new GameConfigSection("System", framework, &commonConfig->ConfigBase);
|
this.System = new GameConfigSection("System", framework, &commonConfig->ConfigBase);
|
||||||
this.UiConfig = new GameConfigSection("UiConfig", framework, &commonConfig->UiConfig);
|
this.UiConfig = new GameConfigSection("UiConfig", framework, &commonConfig->UiConfig);
|
||||||
this.UiControl = new GameConfigSection("UiControl", framework, () => this.UiConfig.TryGetBool("PadMode", out var padMode) && padMode ? &commonConfig->UiControlGamepadConfig : &commonConfig->UiControlConfig);
|
this.UiControl = new GameConfigSection("UiControl", framework, () => this.UiConfig.TryGetBool("PadMode", out var padMode) && padMode ? &commonConfig->UiControlGamepadConfig : &commonConfig->UiControlConfig);
|
||||||
|
|
||||||
|
this.address.Setup(sigScanner);
|
||||||
|
this.configChangeHook = Hook<ConfigChangeDelegate>.FromAddress(this.address.ConfigChangeAddress, this.OnConfigChanged);
|
||||||
|
this.configChangeHook?.Enable();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private unsafe delegate nint ConfigChangeDelegate(ConfigBase* configBase, ConfigEntry* configEntry);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public event EventHandler<ConfigChangeEvent> Changed;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public GameConfigSection System { get; private set; }
|
public GameConfigSection System { get; private set; }
|
||||||
|
|
||||||
|
|
@ -110,4 +126,43 @@ public sealed class GameConfig : IServiceType, IGameConfig
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Set(UiControlOption option, string value) => this.UiControl.Set(option.GetName(), value);
|
public void Set(UiControlOption option, string value) => this.UiControl.Set(option.GetName(), value);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
void IDisposable.Dispose()
|
||||||
|
{
|
||||||
|
this.configChangeHook?.Disable();
|
||||||
|
this.configChangeHook?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe nint OnConfigChanged(ConfigBase* configBase, ConfigEntry* configEntry)
|
||||||
|
{
|
||||||
|
var returnValue = this.configChangeHook!.Original(configBase, configEntry);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ConfigChangeEvent? eventArgs = null;
|
||||||
|
|
||||||
|
if (configBase == this.System.GetConfigBase())
|
||||||
|
{
|
||||||
|
eventArgs = this.System.InvokeChange<SystemConfigOption>(configEntry);
|
||||||
|
}
|
||||||
|
else if (configBase == this.UiConfig.GetConfigBase())
|
||||||
|
{
|
||||||
|
eventArgs = this.UiConfig.InvokeChange<UiConfigOption>(configEntry);
|
||||||
|
}
|
||||||
|
else if (configBase == this.UiControl.GetConfigBase())
|
||||||
|
{
|
||||||
|
eventArgs = this.UiControl.InvokeChange<UiControlOption>(configEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventArgs == null) return returnValue;
|
||||||
|
|
||||||
|
this.Changed?.InvokeSafely(this, eventArgs);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, $"Exception thrown handing {nameof(this.OnConfigChanged)} events.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
Dalamud/Game/Config/GameConfigAddressResolver.cs
Normal file
18
Dalamud/Game/Config/GameConfigAddressResolver.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
namespace Dalamud.Game.Config;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Game config system address resolver.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class GameConfigAddressResolver : BaseAddressResolver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the address of the method called when any config option is changed.
|
||||||
|
/// </summary>
|
||||||
|
public nint ConfigChangeAddress { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void Setup64Bit(SigScanner scanner)
|
||||||
|
{
|
||||||
|
this.ConfigChangeAddress = scanner.ScanText("E8 ?? ?? ?? ?? 48 8B 3F 49 3B 3E");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
|
using Dalamud.Utility;
|
||||||
using FFXIVClientStructs.FFXIV.Common.Configuration;
|
using FFXIVClientStructs.FFXIV.Common.Configuration;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
|
|
@ -16,6 +17,12 @@ public class GameConfigSection
|
||||||
private readonly Framework framework;
|
private readonly Framework framework;
|
||||||
private readonly Dictionary<string, uint> indexMap = new();
|
private readonly Dictionary<string, uint> indexMap = new();
|
||||||
private readonly Dictionary<uint, string> nameMap = new();
|
private readonly Dictionary<uint, string> nameMap = new();
|
||||||
|
private readonly Dictionary<uint, object> enumMap = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event which is fired when a game config option is changed within the section.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<ConfigChangeEvent> Changed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="GameConfigSection"/> class.
|
/// Initializes a new instance of the <see cref="GameConfigSection"/> class.
|
||||||
|
|
@ -59,7 +66,10 @@ public class GameConfigSection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SectionName { get; }
|
public string SectionName { get; }
|
||||||
|
|
||||||
private GetConfigBaseDelegate GetConfigBase { get; }
|
/// <summary>
|
||||||
|
/// Gets the pointer to the config section container.
|
||||||
|
/// </summary>
|
||||||
|
internal GetConfigBaseDelegate GetConfigBase { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to get a boolean config option.
|
/// Attempts to get a boolean config option.
|
||||||
|
|
@ -380,6 +390,35 @@ public class GameConfigSection
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes a change event within the config section.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entry">The config entry that was changed.</param>
|
||||||
|
/// <typeparam name="TEnum">SystemConfigOption, UiConfigOption, or UiControlOption.</typeparam>
|
||||||
|
/// <returns>The ConfigChangeEvent record.</returns>
|
||||||
|
internal unsafe ConfigChangeEvent? InvokeChange<TEnum>(ConfigEntry* entry) where TEnum : Enum
|
||||||
|
{
|
||||||
|
if (!this.enumMap.TryGetValue(entry->Index, out var enumObject))
|
||||||
|
{
|
||||||
|
if (entry->Name == null) return null;
|
||||||
|
var name = MemoryHelper.ReadStringNullTerminated(new IntPtr(entry->Name));
|
||||||
|
if (Enum.TryParse(typeof(TEnum), name, out enumObject))
|
||||||
|
{
|
||||||
|
this.enumMap.Add(entry->Index, enumObject);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
enumObject = null;
|
||||||
|
this.enumMap.Add(entry->Index, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enumObject == null) return null;
|
||||||
|
var eventArgs = new ConfigChangeEvent<TEnum>((TEnum)enumObject);
|
||||||
|
this.Changed?.InvokeSafely(this, eventArgs);
|
||||||
|
return eventArgs;
|
||||||
|
}
|
||||||
|
|
||||||
private unsafe bool TryGetIndex(string name, out uint index)
|
private unsafe bool TryGetIndex(string name, out uint index)
|
||||||
{
|
{
|
||||||
if (this.indexMap.TryGetValue(name, out index))
|
if (this.indexMap.TryGetValue(name, out index))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
using System.Diagnostics;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
using Dalamud.Game.Config;
|
using Dalamud.Game.Config;
|
||||||
|
using FFXIVClientStructs.FFXIV.Common.Configuration;
|
||||||
|
|
||||||
namespace Dalamud.Plugin.Services;
|
namespace Dalamud.Plugin.Services;
|
||||||
|
|
||||||
|
|
@ -9,6 +11,11 @@ namespace Dalamud.Plugin.Services;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IGameConfig
|
public interface IGameConfig
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event which is fired when a game config option is changed.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<ConfigChangeEvent> Changed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the collection of config options that persist between characters.
|
/// Gets the collection of config options that persist between characters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue