Add GameConfig service

This commit is contained in:
Caraxi 2023-03-12 11:22:34 +10:30
parent 03f33adbce
commit 1f262ce7d5
11 changed files with 6879 additions and 0 deletions

View file

@ -0,0 +1,19 @@
using System;
namespace Dalamud.Game.Config;
/// <summary>
/// An exception thrown when a matching config option is not present in the config section.
/// </summary>
public class ConfigOptionNotFoundException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ConfigOptionNotFoundException"/> class.
/// </summary>
/// <param name="sectionName">Name of the section being accessed.</param>
/// <param name="configOptionName">Name of the config option that was not found.</param>
public ConfigOptionNotFoundException(string sectionName, string configOptionName)
: base($"The option '{configOptionName}' is not available in {sectionName}.")
{
}
}

View file

@ -0,0 +1,261 @@
using System.Diagnostics;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Serilog;
namespace Dalamud.Game.Config;
/// <summary>
/// This class represents the game's configuration.
/// </summary>
[InterfaceVersion("1.0")]
[PluginInterface]
[ServiceManager.EarlyLoadedService]
public sealed class GameConfig : IServiceType
{
[ServiceManager.ServiceConstructor]
private unsafe GameConfig(Framework framework)
{
framework.RunOnTick(() =>
{
Log.Verbose("[GameConfig] Initalizing");
var csFramework = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance();
var commonConfig = &csFramework->SystemConfig.CommonSystemConfig;
this.System = new GameConfigSection("System", framework, &commonConfig->ConfigBase);
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);
});
}
/// <summary>
/// Gets the collection of config options that persist between characters.
/// </summary>
public GameConfigSection System { get; private set; }
/// <summary>
/// Gets the collection of config options that are character specific.
/// </summary>
public GameConfigSection UiConfig { get; private set; }
/// <summary>
/// Gets the collection of config options that are control mode specific. (Mouse & Keyboard / Gamepad).
/// </summary>
public GameConfigSection UiControl { get; private set; }
/// <summary>
/// Attempts to get a boolean config value from the System section.
/// </summary>
/// <param name="option">Option to get the value of.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public bool TryGet(SystemConfigOption option, out bool value) => this.System.TryGet(option.GetName(), out value);
/// <summary>
/// Attempts to get a uint config value from the System section.
/// </summary>
/// <param name="option">Option to get the value of.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public bool TryGet(SystemConfigOption option, out uint value) => this.System.TryGet(option.GetName(), out value);
/// <summary>
/// Attempts to get a float config value from the System section.
/// </summary>
/// <param name="option">Option to get the value of.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public bool TryGet(SystemConfigOption option, out float value) => this.System.TryGet(option.GetName(), out value);
/// <summary>
/// Attempts to get a string config value from the System section.
/// </summary>
/// <param name="option">Option to get the value of.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public bool TryGet(SystemConfigOption option, out string value) => this.System.TryGet(option.GetName(), out value);
/// <summary>
/// Attempts to get a boolean config value from the UiConfig section.
/// </summary>
/// <param name="option">Option to get the value of.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public bool TryGet(UiConfigOption option, out bool value) => this.UiConfig.TryGet(option.GetName(), out value);
/// <summary>
/// Attempts to get a uint config value from the UiConfig section.
/// </summary>
/// <param name="option">Option to get the value of.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public bool TryGet(UiConfigOption option, out uint value) => this.UiConfig.TryGet(option.GetName(), out value);
/// <summary>
/// Attempts to get a float config value from the UiConfig section.
/// </summary>
/// <param name="option">Option to get the value of.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public bool TryGet(UiConfigOption option, out float value) => this.UiConfig.TryGet(option.GetName(), out value);
/// <summary>
/// Attempts to get a string config value from the UiConfig section.
/// </summary>
/// <param name="option">Option to get the value of.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public bool TryGet(UiConfigOption option, out string value) => this.UiControl.TryGet(option.GetName(), out value);
/// <summary>
/// Attempts to get a boolean config value from the UiControl section.
/// </summary>
/// <param name="option">Option to get the value of.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public bool TryGet(UiControlOption option, out bool value) => this.UiControl.TryGet(option.GetName(), out value);
/// <summary>
/// Attempts to get a uint config value from the UiControl section.
/// </summary>
/// <param name="option">Option to get the value of.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public bool TryGet(UiControlOption option, out uint value) => this.UiControl.TryGet(option.GetName(), out value);
/// <summary>
/// Attempts to get a float config value from the UiControl section.
/// </summary>
/// <param name="option">Option to get the value of.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public bool TryGet(UiControlOption option, out float value) => this.UiControl.TryGet(option.GetName(), out value);
/// <summary>
/// Attempts to get a string config value from the UiControl section.
/// </summary>
/// <param name="option">Option to get the value of.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public bool TryGet(UiControlOption option, out string value) => this.System.TryGet(option.GetName(), out value);
/// <summary>
/// Set a boolean config option in the System config section.
/// Note: Not all config options will be be immediately reflected in the game.
/// </summary>
/// <param name="option">Name of the config option.</param>
/// <param name="value">New value of the config option.</param>
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
public void Set(SystemConfigOption option, bool value) => this.System.Set(option.GetName(), value);
/// <summary>
/// Set a unsigned integer config option in the System config section.
/// Note: Not all config options will be be immediately reflected in the game.
/// </summary>
/// <param name="option">Name of the config option.</param>
/// <param name="value">New value of the config option.</param>
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
public void Set(SystemConfigOption option, uint value) => this.System.Set(option.GetName(), value);
/// <summary>
/// Set a float config option in the System config section.
/// Note: Not all config options will be be immediately reflected in the game.
/// </summary>
/// <param name="option">Name of the config option.</param>
/// <param name="value">New value of the config option.</param>
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
public void Set(SystemConfigOption option, float value) => this.System.Set(option.GetName(), value);
/// <summary>
/// Set a string config option in the System config section.
/// Note: Not all config options will be be immediately reflected in the game.
/// </summary>
/// <param name="option">Name of the config option.</param>
/// <param name="value">New value of the config option.</param>
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
public void Set(SystemConfigOption option, string value) => this.System.Set(option.GetName(), value);
/// <summary>
/// Set a boolean config option in the UiConfig section.
/// Note: Not all config options will be be immediately reflected in the game.
/// </summary>
/// <param name="option">Name of the config option.</param>
/// <param name="value">New value of the config option.</param>
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
public void Set(UiConfigOption option, bool value) => this.UiConfig.Set(option.GetName(), value);
/// <summary>
/// Set a unsigned integer config option in the UiConfig section.
/// Note: Not all config options will be be immediately reflected in the game.
/// </summary>
/// <param name="option">Name of the config option.</param>
/// <param name="value">New value of the config option.</param>
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
public void Set(UiConfigOption option, uint value) => this.UiConfig.Set(option.GetName(), value);
/// <summary>
/// Set a float config option in the UiConfig section.
/// Note: Not all config options will be be immediately reflected in the game.
/// </summary>
/// <param name="option">Name of the config option.</param>
/// <param name="value">New value of the config option.</param>
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
public void Set(UiConfigOption option, float value) => this.UiConfig.Set(option.GetName(), value);
/// <summary>
/// Set a string config option in the UiConfig section.
/// Note: Not all config options will be be immediately reflected in the game.
/// </summary>
/// <param name="option">Name of the config option.</param>
/// <param name="value">New value of the config option.</param>
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
public void Set(UiConfigOption option, string value) => this.UiConfig.Set(option.GetName(), value);
/// <summary>
/// Set a boolean config option in the UiControl config section.
/// Note: Not all config options will be be immediately reflected in the game.
/// </summary>
/// <param name="option">Name of the config option.</param>
/// <param name="value">New value of the config option.</param>
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
public void Set(UiControlOption option, bool value) => this.UiControl.Set(option.GetName(), value);
/// <summary>
/// Set a uint config option in the UiControl config section.
/// Note: Not all config options will be be immediately reflected in the game.
/// </summary>
/// <param name="option">Name of the config option.</param>
/// <param name="value">New value of the config option.</param>
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
public void Set(UiControlOption option, uint value) => this.UiControl.Set(option.GetName(), value);
/// <summary>
/// Set a float config option in the UiControl config section.
/// Note: Not all config options will be be immediately reflected in the game.
/// </summary>
/// <param name="option">Name of the config option.</param>
/// <param name="value">New value of the config option.</param>
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
public void Set(UiControlOption option, float value) => this.UiControl.Set(option.GetName(), value);
/// <summary>
/// Set a string config option in the UiControl config section.
/// Note: Not all config options will be be immediately reflected in the game.
/// </summary>
/// <param name="option">Name of the config option.</param>
/// <param name="value">New value of the config option.</param>
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
public void Set(UiControlOption option, string value) => this.UiControl.Set(option.GetName(), value);
}

View file

@ -0,0 +1,39 @@
using Dalamud.Utility;
namespace Dalamud.Game.Config;
/// <summary>
/// Helper functions for accessing GameConfigOptions.
/// </summary>
internal static class GameConfigEnumExtensions
{
/// <summary>
/// Gets the name of a SystemConfigOption from it's attribute.
/// </summary>
/// <param name="systemConfigOption">The SystemConfigOption.</param>
/// <returns>Name of the option.</returns>
public static string GetName(this SystemConfigOption systemConfigOption)
{
return systemConfigOption.GetAttribute<GameConfigOptionAttribute>()?.Name ?? $"{systemConfigOption}";
}
/// <summary>
/// Gets the name of a UiConfigOption from it's attribute.
/// </summary>
/// <param name="uiConfigOption">The UiConfigOption.</param>
/// <returns>Name of the option.</returns>
public static string GetName(this UiConfigOption uiConfigOption)
{
return uiConfigOption.GetAttribute<GameConfigOptionAttribute>()?.Name ?? $"{uiConfigOption}";
}
/// <summary>
/// Gets the name of a UiControlOption from it's attribute.
/// </summary>
/// <param name="uiControlOption">The UiControlOption.</param>
/// <returns>Name of the option.</returns>
public static string GetName(this UiControlOption uiControlOption)
{
return uiControlOption.GetAttribute<GameConfigOptionAttribute>()?.Name ?? $"{uiControlOption}";
}
}

View file

@ -0,0 +1,40 @@
using System;
using FFXIVClientStructs.FFXIV.Common.Configuration;
namespace Dalamud.Game.Config;
/// <summary>
/// An attribute for defining GameConfig options.
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public class GameConfigOptionAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="GameConfigOptionAttribute"/> class.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <param name="type">The type of the config option.</param>
/// <param name="settable">False if the game does not take changes to the config option.</param>
public GameConfigOptionAttribute(string name, ConfigType type, bool settable = true)
{
this.Name = name;
this.Type = type;
this.Settable = settable;
}
/// <summary>
/// Gets the Name of the config option.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the type of the config option.
/// </summary>
public ConfigType Type { get; }
/// <summary>
/// Gets a value indicating whether the config option will update immediately or not.
/// </summary>
public bool Settable { get; }
}

View file

@ -0,0 +1,407 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Dalamud.Memory;
using FFXIVClientStructs.FFXIV.Common.Configuration;
using Serilog;
namespace Dalamud.Game.Config;
/// <summary>
/// Represents a section of the game config and contains helper functions for accessing and setting values.
/// </summary>
public class GameConfigSection
{
private readonly Framework framework;
private readonly Dictionary<string, uint> indexMap = new();
private readonly Dictionary<uint, string> nameMap = new();
/// <summary>
/// Initializes a new instance of the <see cref="GameConfigSection"/> class.
/// </summary>
/// <param name="sectionName">Name of the section.</param>
/// <param name="framework">The framework service.</param>
/// <param name="configBase">Unmanaged ConfigBase instance.</param>
internal unsafe GameConfigSection(string sectionName, Framework framework, ConfigBase* configBase)
: this(sectionName, framework, () => configBase)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GameConfigSection"/> class.
/// </summary>
/// <param name="sectionName">Name of the section.</param>
/// <param name="framework">The framework service.</param>
/// <param name="getConfigBase">A function that determines which ConfigBase instance should be used.</param>
internal GameConfigSection(string sectionName, Framework framework, GetConfigBaseDelegate getConfigBase)
{
this.SectionName = sectionName;
this.framework = framework;
this.GetConfigBase = getConfigBase;
Log.Verbose("[GameConfig] Initalizing {SectionName} with {ConfigCount} entries.", this.SectionName, this.ConfigCount);
}
/// <summary>
/// Delegate that gets the struct the section accesses.
/// </summary>
/// <returns>Pointer to unmanaged ConfigBase.</returns>
internal unsafe delegate ConfigBase* GetConfigBaseDelegate();
/// <summary>
/// Gets the number of config entries contained within the section.
/// Some entries may be empty with no data.
/// </summary>
public unsafe uint ConfigCount => this.GetConfigBase()->ConfigCount;
/// <summary>
/// Gets the name of the config section.
/// </summary>
public string SectionName { get; }
private GetConfigBaseDelegate GetConfigBase { get; }
/// <summary>
/// Attempts to get a boolean config option.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public unsafe bool TryGetBool(string name, out bool value) {
value = false;
if (!this.TryGetIndex(name, out var index))
{
return false;
}
if (!this.TryGetEntry(index, out var entry))
{
return false;
}
value = entry->Value.UInt != 0;
return true;
}
/// <summary>
/// Attempts to get a boolean config option.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public bool TryGet(string name, out bool value) => this.TryGetBool(name, out value);
/// <summary>
/// Get a boolean config option.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <returns>Value of the config option.</returns>
/// <exception cref="ConfigOptionNotFoundException">Thrown if the config option is not found.</exception>
public bool GetBool(string name) {
if (!this.TryGetBool(name, out var value))
{
throw new ConfigOptionNotFoundException(this.SectionName, name);
}
return value;
}
/// <summary>
/// Set a boolean config option.
/// Note: Not all config options will be be immediately reflected in the game.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <param name="value">New value of the config option.</param>
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
public unsafe void Set(string name, bool value) {
if (!this.TryGetIndex(name, out var index))
{
throw new ConfigOptionNotFoundException(this.SectionName, name);
}
if (!this.TryGetEntry(index, out var entry))
{
throw new UnreachableException($"An unexpected error was encountered setting {name} in {this.SectionName}");
}
if ((ConfigType)entry->Type != ConfigType.UInt)
{
throw new IncorrectConfigTypeException(this.SectionName, name, (ConfigType)entry->Type, ConfigType.UInt);
}
entry->SetValue(value ? 1U : 0U);
}
/// <summary>
/// Attempts to get an unsigned integer config value.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public unsafe bool TryGetUInt(string name, out uint value) {
value = 0;
if (!this.TryGetIndex(name, out var index))
{
return false;
}
if (!this.TryGetEntry(index, out var entry))
{
return false;
}
value = entry->Value.UInt;
return true;
}
/// <summary>
/// Attempts to get an unsigned integer config value.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public bool TryGet(string name, out uint value) => this.TryGetUInt(name, out value);
/// <summary>
/// Get an unsigned integer config option.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <returns>Value of the config option.</returns>
/// <exception cref="ConfigOptionNotFoundException">Thrown if the config option is not found.</exception>
public uint GetUInt(string name) {
if (!this.TryGetUInt(name, out var value))
{
throw new ConfigOptionNotFoundException(this.SectionName, name);
}
return value;
}
/// <summary>
/// Set an unsigned integer config option.
/// Note: Not all config options will be be immediately reflected in the game.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <param name="value">New value of the config option.</param>
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
public unsafe void Set(string name, uint value) {
this.framework.RunOnFrameworkThread(() => {
if (!this.TryGetIndex(name, out var index))
{
throw new ConfigOptionNotFoundException(this.SectionName, name);
}
if (!this.TryGetEntry(index, out var entry))
{
throw new UnreachableException($"An unexpected error was encountered setting {name} in {this.SectionName}");
}
if ((ConfigType)entry->Type != ConfigType.UInt)
{
throw new IncorrectConfigTypeException(this.SectionName, name, (ConfigType)entry->Type, ConfigType.UInt);
}
entry->SetValue(value);
});
}
/// <summary>
/// Attempts to get a float config value.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public unsafe bool TryGetFloat(string name, out float value) {
value = 0;
if (!this.TryGetIndex(name, out var index))
{
return false;
}
if (!this.TryGetEntry(index, out var entry))
{
return false;
}
value = entry->Value.Float;
return true;
}
/// <summary>
/// Attempts to get a float config value.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public bool TryGet(string name, out float value) => this.TryGetFloat(name, out value);
/// <summary>
/// Get a float config option.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <returns>Value of the config option.</returns>
/// <exception cref="ConfigOptionNotFoundException">Thrown if the config option is not found.</exception>
public float GetFloat(string name) {
if (!this.TryGetFloat(name, out var value))
{
throw new ConfigOptionNotFoundException(this.SectionName, name);
}
return value;
}
/// <summary>
/// Set a float config option.
/// Note: Not all config options will be be immediately reflected in the game.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <param name="value">New value of the config option.</param>
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
public unsafe void Set(string name, float value) {
this.framework.RunOnFrameworkThread(() => {
if (!this.TryGetIndex(name, out var index))
{
throw new ConfigOptionNotFoundException(this.SectionName, name);
}
if (!this.TryGetEntry(index, out var entry))
{
throw new UnreachableException($"An unexpected error was encountered setting {name} in {this.SectionName}");
}
if ((ConfigType)entry->Type != ConfigType.Float)
{
throw new IncorrectConfigTypeException(this.SectionName, name, (ConfigType)entry->Type, ConfigType.Float);
}
entry->SetValue(value);
});
}
/// <summary>
/// Attempts to get a string config value.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public unsafe bool TryGetString(string name, out string value) {
value = string.Empty;
if (!this.TryGetIndex(name, out var index))
{
return false;
}
if (!this.TryGetEntry(index, out var entry))
{
return false;
}
if (entry->Type != 4)
{
return false;
}
if (entry->Value.String == null)
{
return false;
}
value = entry->Value.String->ToString();
return true;
}
/// <summary>
/// Attempts to get a string config value.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <param name="value">The returned value of the config option.</param>
/// <returns>A value representing the success.</returns>
public bool TryGet(string name, out string value) => this.TryGetString(name, out value);
/// <summary>
/// Get a string config option.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <returns>Value of the config option.</returns>
/// <exception cref="ConfigOptionNotFoundException">Thrown if the config option is not found.</exception>
public string GetString(string name) {
if (!this.TryGetString(name, out var value))
{
throw new ConfigOptionNotFoundException(this.SectionName, name);
}
return value;
}
/// <summary>
/// Set a string config option.
/// Note: Not all config options will be be immediately reflected in the game.
/// </summary>
/// <param name="name">Name of the config option.</param>
/// <param name="value">New value of the config option.</param>
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
public unsafe void Set(string name, string value) {
this.framework.RunOnFrameworkThread(() => {
if (!this.TryGetIndex(name, out var index))
{
throw new ConfigOptionNotFoundException(this.SectionName, name);
}
if (!this.TryGetEntry(index, out var entry))
{
throw new UnreachableException($"An unexpected error was encountered setting {name} in {this.SectionName}");
}
if ((ConfigType)entry->Type != ConfigType.String)
{
throw new IncorrectConfigTypeException(this.SectionName, name, (ConfigType)entry->Type, ConfigType.String);
}
entry->SetValue(value);
});
}
private unsafe bool TryGetIndex(string name, out uint index) {
if (this.indexMap.TryGetValue(name, out index))
{
return true;
}
var configBase = this.GetConfigBase();
var e = configBase->ConfigEntry;
for (var i = 0U; i < configBase->ConfigCount; i++, e++) {
if (e->Name == null)
{
continue;
}
var eName = MemoryHelper.ReadStringNullTerminated(new IntPtr(e->Name));
if (eName.Equals(name)) {
this.indexMap.TryAdd(name, i);
this.nameMap.TryAdd(i, name);
index = i;
return true;
}
}
index = 0;
return false;
}
private unsafe bool TryGetEntry(uint index, out ConfigEntry* entry) {
entry = null;
var configBase = this.GetConfigBase();
if (configBase->ConfigEntry == null || index >= configBase->ConfigCount)
{
return false;
}
entry = configBase->ConfigEntry;
entry += index;
return true;
}
}

View file

@ -0,0 +1,23 @@
using System;
using FFXIVClientStructs.FFXIV.Common.Configuration;
namespace Dalamud.Game.Config;
/// <summary>
/// An exception thrown when attempting to assign a value to a config option with the wrong type.
/// </summary>
public class IncorrectConfigTypeException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="IncorrectConfigTypeException"/> class.
/// </summary>
/// <param name="sectionName">Name of the section being accessed.</param>
/// <param name="configOptionName">Name of the config option that was not found.</param>
/// <param name="correctType">The correct type for the config option.</param>
/// <param name="incorrectType">The type that was attempted.</param>
public IncorrectConfigTypeException(string sectionName, string configOptionName, ConfigType correctType, ConfigType incorrectType)
: base($"The option '{configOptionName}' in {sectionName} is of the type {correctType}. Assigning {incorrectType} is invalid.")
{
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,107 @@
using Dalamud.Game.Config;
using ImGuiNET;
namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps;
/// <summary>
/// Test of GameConfig.
/// </summary>
internal class GameConfigAgingStep : IAgingStep
{
private bool started;
private bool isStartedLegacy;
private bool isSwitchedLegacy;
private bool isSwitchedStandard;
/// <inheritdoc/>
public string Name => "Test GameConfig";
/// <inheritdoc/>
public SelfTestStepResult RunStep()
{
var gameConfig = Service<GameConfig>.Get();
if (!gameConfig.UiControl.TryGetBool("MoveMode", out var isLegacy))
{
return SelfTestStepResult.Fail;
}
if (!this.started)
{
this.started = true;
this.isStartedLegacy = isLegacy;
return SelfTestStepResult.Waiting;
}
if (this.isStartedLegacy)
{
if (!this.isSwitchedStandard)
{
if (!isLegacy)
{
this.isSwitchedStandard = true;
}
else
{
ImGui.Text("Switch Movement Type to Standard");
}
return SelfTestStepResult.Waiting;
}
if (!this.isSwitchedLegacy)
{
if (isLegacy)
{
this.isSwitchedLegacy = true;
}
else
{
ImGui.Text("Switch Movement Type to Legacy");
}
return SelfTestStepResult.Waiting;
}
}
else
{
if (!this.isSwitchedLegacy)
{
if (isLegacy)
{
this.isSwitchedLegacy = true;
}
else
{
ImGui.Text("Switch Movement Type to Legacy");
}
return SelfTestStepResult.Waiting;
}
if (!this.isSwitchedStandard)
{
if (!isLegacy)
{
this.isSwitchedStandard = true;
}
else
{
ImGui.Text("Switch Movement Type to Standard");
}
return SelfTestStepResult.Waiting;
}
}
return SelfTestStepResult.Pass;
}
/// <inheritdoc/>
public void CleanUp()
{
this.isSwitchedLegacy = false;
this.isSwitchedStandard = false;
this.started = false;
}
}

View file

@ -42,6 +42,7 @@ internal class SelfTestWindow : Window
new PartyFinderAgingStep(),
new HandledExceptionAgingStep(),
new DutyStateAgingStep(),
new GameConfigAgingStep(),
new LogoutEventAgingStep(),
};