- ConfigService.cs alpha testing.

This commit is contained in:
MapleWheels
2026-01-22 14:58:03 -05:00
committed by Maplewheels
parent cc07db941f
commit 7e0e671539
27 changed files with 795 additions and 546 deletions
@@ -1,103 +0,0 @@
using System;
using System.Xml.Linq;
using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs.Services;
using Barotrauma.Networking;
using OneOf;
namespace Barotrauma.LuaCs.Configuration;
public class ConfigEntry<T> : IConfigEntry<T> where T : IEquatable<T>
{
private readonly Action<ConfigEntry<T>, INetReadMessage> _readMessageHandler;
private readonly Action<ConfigEntry<T>, INetWriteMessage> _writeMessageHandler;
public ConfigEntry(IConfigInfo configInfo, Action<ConfigEntry<T>, INetReadMessage> readMessageHandler,
Action<ConfigEntry<T>, INetWriteMessage> writeMessageHandler)
{
_readMessageHandler = readMessageHandler;
_writeMessageHandler = writeMessageHandler;
}
public string InternalName { get; init; }
public ContentPackage OwnerPackage { get; init; }
public bool Equals(IConfigBase other)
{
if (ReferenceEquals(this, other))
return true;
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
public Type GetValueType()
{
throw new NotImplementedException();
}
public string GetValue()
{
throw new NotImplementedException();
}
public bool TrySetValue(OneOf<string, XElement> value)
{
throw new NotImplementedException();
}
public bool IsAssignable(OneOf<string, XElement> value)
{
throw new NotImplementedException();
}
private event Action<IConfigEntry<T>> _onValueChanged;
public event Action<IConfigEntry<T>> OnValueChanged
{
add => _onValueChanged += value;
remove => _onValueChanged -= value;
}
event Action<IConfigBase> IConfigBase.OnValueChanged
{
add => _onValueChanged += value;
remove => _onValueChanged -= value;
}
public OneOf<string, XElement> GetSerializableValue()
{
throw new NotImplementedException();
}
public Guid InstanceId => throw new NotImplementedException();
public NetSync SyncType => throw new NotImplementedException();
public ClientPermissions WritePermissions => throw new NotImplementedException();
public void ReadNetMessage(INetReadMessage message)
{
throw new NotImplementedException();
}
public void WriteNetMessage(INetWriteMessage message)
{
throw new NotImplementedException();
}
public T Value => throw new NotImplementedException();
public bool TrySetValue(T value)
{
throw new NotImplementedException();
}
public bool IsAssignable(T value)
{
throw new NotImplementedException();
}
}
@@ -1,111 +0,0 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs.Services;
using Barotrauma.Networking;
using OneOf;
namespace Barotrauma.LuaCs.Configuration;
public class ConfigList<T> : IConfigList<T> where T : IEquatable<T>
{
private readonly Action<ConfigList<T>, INetReadMessage> _readMessageHandler;
private readonly Action<ConfigList<T>, INetWriteMessage> _writeMessageHandler;
public ConfigList(IConfigInfo configInfo, Action<ConfigList<T>, INetReadMessage> readMessageHandler,
Action<ConfigList<T>, INetWriteMessage> writeMessageHandler)
{
_readMessageHandler = readMessageHandler;
_writeMessageHandler = writeMessageHandler;
}
public string InternalName => throw new NotImplementedException();
public ContentPackage OwnerPackage => throw new NotImplementedException();
public bool Equals(IConfigBase other)
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
public Type GetValueType()
{
throw new NotImplementedException();
}
public string GetValue()
{
throw new NotImplementedException();
}
public bool TrySetValue(OneOf<string, XElement> value)
{
throw new NotImplementedException();
}
public bool IsAssignable(OneOf<string, XElement> value)
{
throw new NotImplementedException();
}
private event Action<IConfigList<T>> _onValueChanged;
public event Action<IConfigList<T>> OnValueChanged
{
add => _onValueChanged += value;
remove => _onValueChanged -= value;
}
event Action<IConfigEntry<T>> IConfigEntry<T>.OnValueChanged
{
add => _onValueChanged += value;
remove => _onValueChanged -= value;
}
event Action<IConfigBase> IConfigBase.OnValueChanged
{
add => _onValueChanged += value;
remove => _onValueChanged -= value;
}
public T Value => throw new NotImplementedException();
public bool TrySetValue(T value)
{
throw new NotImplementedException();
}
public bool IsAssignable(T value)
{
throw new NotImplementedException();
}
public OneOf<string, XElement> GetSerializableValue()
{
throw new NotImplementedException();
}
public Guid InstanceId => throw new NotImplementedException();
public NetSync SyncType => throw new NotImplementedException();
public ClientPermissions WritePermissions => throw new NotImplementedException();
public void ReadNetMessage(INetReadMessage message)
{
throw new NotImplementedException();
}
public void WriteNetMessage(INetWriteMessage message)
{
throw new NotImplementedException();
}
public IReadOnlyList<T> Options => throw new NotImplementedException();
}
@@ -6,12 +6,13 @@ using Barotrauma.Networking;
namespace Barotrauma.LuaCs.Configuration;
public partial interface IConfigBase : IDataInfo, IEquatable<IConfigBase>, IDisposable
public partial interface ISettingBase : IDataInfo, IEquatable<ISettingBase>, IDisposable
{
Type GetValueType();
string GetValue();
string GetStringValue();
bool TrySetValue(OneOf.OneOf<string, XElement> value);
bool IsAssignable(OneOf.OneOf<string, XElement> value);
event Action<IConfigBase> OnValueChanged;
event Func<OneOf.OneOf<string, XElement>, bool> IsNewValueValid;
event Action<ISettingBase> OnValueChanged;
OneOf.OneOf<string, XElement> GetSerializableValue();
}
@@ -3,10 +3,10 @@ using Barotrauma.LuaCs.Services;
namespace Barotrauma.LuaCs.Configuration;
public interface IConfigEntry<T> : IConfigBase, INetworkSyncEntity where T : IEquatable<T>
public interface ISettingEntry<T> : ISettingBase, INetworkSyncEntity where T : IEquatable<T>
{
T Value { get; }
bool TrySetValue(T value);
bool IsAssignable(T value);
new event Action<IConfigEntry<T>> OnValueChanged;
new event Action<ISettingEntry<T>> OnValueChanged;
}
@@ -3,7 +3,7 @@ using Barotrauma.LuaCs.Services;
namespace Barotrauma.LuaCs.Configuration;
public interface IConfigEnum : IConfigBase, INetworkSyncEntity
public interface ISettingEnum : ISettingBase, INetworkSyncEntity
{
}
@@ -4,8 +4,8 @@ using Barotrauma.LuaCs.Services;
namespace Barotrauma.LuaCs.Configuration;
public interface IConfigList<T> : IConfigEntry<T>, INetworkSyncEntity where T : IEquatable<T>
public interface ISettingList<T> : ISettingEntry<T>, INetworkSyncEntity where T : IEquatable<T>
{
IReadOnlyList<T> Options { get; }
new event Action<IConfigList<T>> OnValueChanged;
new event Action<ISettingList<T>> OnValueChanged;
}
@@ -2,7 +2,7 @@ using System;
namespace Barotrauma.LuaCs.Configuration;
public interface IConfigRangeEntry<T> : IConfigEntry<T> where T : IConvertible, IEquatable<T>
public interface ISettingRangeEntry<T> : ISettingEntry<T> where T : IConvertible, IEquatable<T>
{
T MinValue { get; }
T MaxValue { get; }
@@ -0,0 +1,85 @@
using System;
using System.Xml.Linq;
using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs.Services;
using Barotrauma.Networking;
using OneOf;
namespace Barotrauma.LuaCs.Configuration;
public class SettingEntry<T> : ISettingEntry<T> where T : IEquatable<T>
{
public string InternalName { get; }
public ContentPackage OwnerPackage { get; }
public bool Equals(ISettingBase other)
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
public Type GetValueType()
{
throw new NotImplementedException();
}
public string GetStringValue()
{
throw new NotImplementedException();
}
public bool TrySetValue(OneOf<string, XElement> value)
{
throw new NotImplementedException();
}
public bool IsAssignable(OneOf<string, XElement> value)
{
throw new NotImplementedException();
}
public event Func<OneOf<string, XElement>, bool> IsNewValueValid;
public T Value { get; }
public bool TrySetValue(T value)
{
throw new NotImplementedException();
}
public bool IsAssignable(T value)
{
throw new NotImplementedException();
}
event Action<ISettingEntry<T>> ISettingEntry<T>.OnValueChanged
{
add => throw new NotImplementedException();
remove => throw new NotImplementedException();
}
event Action<ISettingBase> ISettingBase.OnValueChanged
{
add => throw new NotImplementedException();
remove => throw new NotImplementedException();
}
public OneOf<string, XElement> GetSerializableValue()
{
throw new NotImplementedException();
}
public Guid InstanceId { get; }
public NetSync SyncType { get; }
public ClientPermissions WritePermissions { get; }
public void ReadNetMessage(INetReadMessage message)
{
throw new NotImplementedException();
}
public void WriteNetMessage(INetWriteMessage message)
{
throw new NotImplementedException();
}
}
@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs.Services;
using Barotrauma.Networking;
using OneOf;
namespace Barotrauma.LuaCs.Configuration;
public class SettingList<T> : ISettingList<T> where T : IEquatable<T>
{
public string InternalName { get; }
public ContentPackage OwnerPackage { get; }
public bool Equals(ISettingBase other)
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
public Type GetValueType()
{
throw new NotImplementedException();
}
public string GetStringValue()
{
throw new NotImplementedException();
}
public bool TrySetValue(OneOf<string, XElement> value)
{
throw new NotImplementedException();
}
public bool IsAssignable(OneOf<string, XElement> value)
{
throw new NotImplementedException();
}
public event Func<OneOf<string, XElement>, bool> IsNewValueValid;
public T Value { get; }
public bool TrySetValue(T value)
{
throw new NotImplementedException();
}
public bool IsAssignable(T value)
{
throw new NotImplementedException();
}
event Action<ISettingList<T>> ISettingList<T>.OnValueChanged
{
add => throw new NotImplementedException();
remove => throw new NotImplementedException();
}
public IReadOnlyList<T> Options { get; }
event Action<ISettingEntry<T>> ISettingEntry<T>.OnValueChanged
{
add => throw new NotImplementedException();
remove => throw new NotImplementedException();
}
event Action<ISettingBase> ISettingBase.OnValueChanged
{
add => throw new NotImplementedException();
remove => throw new NotImplementedException();
}
public OneOf<string, XElement> GetSerializableValue()
{
throw new NotImplementedException();
}
public Guid InstanceId { get; }
public NetSync SyncType { get; }
public ClientPermissions WritePermissions { get; }
public void ReadNetMessage(INetReadMessage message)
{
throw new NotImplementedException();
}
public void WriteNetMessage(INetWriteMessage message)
{
throw new NotImplementedException();
}
}
@@ -67,9 +67,8 @@ public record ConfigInfo : IConfigInfo
{
public string InternalName { get; init; }
public ContentPackage OwnerPackage { get; init; }
public Type DataType { get; init; }
public OneOf<string, XElement> DefaultValue { get; init; }
public OneOf<string, XElement> Value { get; init; }
public string DataType { get; init; }
public XElement Element { get; init; }
public RunState EditableStates { get; init; }
public NetSync NetSync { get; init; }
@@ -79,7 +78,7 @@ public record ConfigInfo : IConfigInfo
public string DisplayCategory { get; init; }
public bool ShowInMenus { get; init; }
public string Tooltip { get; init; }
public string ImageIconPath { get; init; }
public ContentPath ImageIconPath { get; init; }
#endif
}
@@ -87,7 +86,7 @@ public record ConfigProfileInfo : IConfigProfileInfo
{
public string InternalName { get; init; }
public ContentPackage OwnerPackage { get; init; }
public IReadOnlyList<(string ConfigName, OneOf<string, XElement> Value)> ProfileValues { get; init; }
public IReadOnlyList<(string ConfigName, XElement Element)> ProfileValues { get; init; }
}
#endregion
@@ -13,26 +13,18 @@ public partial interface IConfigInfo : IDataInfo
/// <summary>
/// Specifies the type initializer that will be used to instantiate the config var.
/// </summary>
Type DataType { get; }
string DataType { get; }
/// <summary>
/// The default value.
/// The 'Setting' XML element.
/// </summary>
OneOf.OneOf<string, XElement> DefaultValue { get; }
/// <summary>
/// The value the last time this config was saved. If not found, returns the default value.
/// <br/><b>[If(Type='<see cref="string"/>')]</b><br/>
/// The value is from the 'Value' Attribute. Typically used for types with single/simple values, such as primitives.
/// <br/><b>[If(Type='<see cref="XElement"/>')]</b><br/>
/// The value is from the first 'Value' child element. Typically used with complex config types, such as range and list.
/// </summary>
OneOf.OneOf<string, XElement> Value { get; }
XElement Element { get; }
/// <summary>
/// In what <see cref="RunState"/>(s) is this config editable. Will be editable in the selected state, and lower value states.
/// <br/><br/>
/// <b>[Important]</b><br/> Setting this to value lower than 'Configuration` will render this config read-only.
/// <br/><br/><b>Expected Behaviour</b>:
/// <br/><b>[<see cref="RunState.Unloaded"/>|<see cref="RunState.Parsed"/>]</b>: Read-Only.
/// <br/><b>[<see cref="RunState.Configuration"/>]</b>: Can only be changed at the Main Menu (not in a lobby).
/// <br/><b>[<see cref="RunState.Unloaded"/>|<see cref="RunState.Unloaded"/>]</b>: Read-Only.
/// <br/><b>[<see cref="RunState.LoadedNoExec"/>]</b>: Can only be changed at the Main Menu (not in a lobby).
/// <br/><b>[<see cref="RunState.Running"/>]</b>: Can be changed at the Main Menu and while a lobby is active.
/// </summary>
RunState EditableStates { get; }
@@ -5,5 +5,5 @@ namespace Barotrauma.LuaCs.Data;
public interface IConfigProfileInfo : IDataInfo
{
IReadOnlyList<(string ConfigName, OneOf.OneOf<string, XElement> Value)> ProfileValues { get; }
IReadOnlyList<(string ConfigName, XElement Element)> ProfileValues { get; }
}
@@ -30,7 +30,7 @@ public interface IEvent<out T> : IEvent where T : IEvent<T>
}
}
#region RuntimeEvents
#region RuntimeServiceEvents
/// <summary>
/// Called when the current <see cref="Screen"/> (game state) changes. Upstream Type 'Screen' is internal.
@@ -61,6 +61,12 @@ internal interface IEventReloadAllPackages : IEvent<IEventReloadAllPackages>
void OnReloadAllPackages();
}
internal interface IEventSettingInstanceLifetime : IEvent<IEventSettingInstanceLifetime>
{
void OnSettingInstanceCreated<T>(T configInstance) where T : ISettingBase;
void OnSettingInstanceDisposed<T>(T configInstance) where T : ISettingBase;
}
#endregion
#region GameEvents
@@ -99,56 +99,56 @@ namespace Barotrauma
/// <summary>
/// Whether C# plugin code is enabled.
/// </summary>
internal IConfigEntry<bool> IsCsEnabled { get; private set; }
internal ISettingEntry<bool> IsCsEnabled { get; private set; }
/// <summary>
/// Whether mods marked as 'forced' or 'always load' should only be loaded if they're in the enabled mods list.
/// </summary>
internal IConfigEntry<bool> TreatForcedModsAsNormal { get; private set; }
internal ISettingEntry<bool> TreatForcedModsAsNormal { get; private set; }
/// <summary>
/// Whether the lua script runner from Workshop package should be used over the in-built version.
/// </summary>
internal IConfigEntry<bool> PreferToUseWorkshopLuaSetup { get; private set; }
internal ISettingEntry<bool> PreferToUseWorkshopLuaSetup { get; private set; }
/// <summary>
/// Whether the popup error GUI should be hidden/suppressed.
/// </summary>
internal IConfigEntry<bool> DisableErrorGUIOverlay { get; private set; }
internal ISettingEntry<bool> DisableErrorGUIOverlay { get; private set; }
/// <summary>
/// Whether usernames are anonymized or show in logs.
/// </summary>
internal IConfigEntry<bool> HideUserNamesInLogs { get; private set; }
internal ISettingEntry<bool> HideUserNamesInLogs { get; private set; }
/// <summary>
/// The SteamId of the Workshop LuaCs CPackage in use, if available.
/// </summary>
internal IConfigEntry<ulong> LuaForBarotraumaSteamId { get; private set; }
internal ISettingEntry<ulong> LuaForBarotraumaSteamId { get; private set; }
/// <summary>
/// TODO: @evilfactory@users.noreply.github.com
/// </summary>
internal IConfigEntry<bool> RestrictMessageSize { get; private set; }
internal ISettingEntry<bool> RestrictMessageSize { get; private set; }
/// <summary>
/// The local save path for all local data storage for mods.
/// </summary>
internal IConfigEntry<string> LocalDataSavePath { get; private set; }
internal ISettingEntry<string> LocalDataSavePath { get; private set; }
void LoadLuaCsConfig()
{
IsCsEnabled = ConfigService.TryGetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "IsCsEnabled", out var val1) ? val1
IsCsEnabled = ConfigService.TryGetConfig<ISettingEntry<bool>>(ContentPackageManager.VanillaCorePackage, "IsCsEnabled", out var val1) ? val1
: throw new NullReferenceException($"{nameof(IsCsEnabled)} cannot be loaded.");
TreatForcedModsAsNormal = ConfigService.TryGetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "TreatForcedModsAsNormal", out var val2) ? val2
TreatForcedModsAsNormal = ConfigService.TryGetConfig<ISettingEntry<bool>>(ContentPackageManager.VanillaCorePackage, "TreatForcedModsAsNormal", out var val2) ? val2
: throw new NullReferenceException($"{nameof(TreatForcedModsAsNormal)} cannot be loaded.");
DisableErrorGUIOverlay = ConfigService.TryGetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "DisableErrorGUIOverlay", out var val3) ? val3
DisableErrorGUIOverlay = ConfigService.TryGetConfig<ISettingEntry<bool>>(ContentPackageManager.VanillaCorePackage, "DisableErrorGUIOverlay", out var val3) ? val3
: throw new NullReferenceException($"{nameof(DisableErrorGUIOverlay)} cannot be loaded.");
HideUserNamesInLogs = ConfigService.TryGetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "HideUserNamesInLogs", out var val4) ? val4
HideUserNamesInLogs = ConfigService.TryGetConfig<ISettingEntry<bool>>(ContentPackageManager.VanillaCorePackage, "HideUserNamesInLogs", out var val4) ? val4
: throw new NullReferenceException($"{nameof(HideUserNamesInLogs)} cannot be loaded.");
LuaForBarotraumaSteamId = ConfigService.TryGetConfig<IConfigEntry<ulong>>(ContentPackageManager.VanillaCorePackage, "LuaForBarotraumaSteamId", out var val5) ? val5
LuaForBarotraumaSteamId = ConfigService.TryGetConfig<ISettingEntry<ulong>>(ContentPackageManager.VanillaCorePackage, "LuaForBarotraumaSteamId", out var val5) ? val5
: throw new NullReferenceException($"{nameof(LuaForBarotraumaSteamId)} cannot be loaded.");
RestrictMessageSize = ConfigService.TryGetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "RestrictMessageSize", out var val7) ? val7
RestrictMessageSize = ConfigService.TryGetConfig<ISettingEntry<bool>>(ContentPackageManager.VanillaCorePackage, "RestrictMessageSize", out var val7) ? val7
: throw new NullReferenceException($"{nameof(RestrictMessageSize)} cannot be loaded.");
}
@@ -170,9 +170,11 @@ namespace Barotrauma
// TODO: INetworkingService
servicesProvider.RegisterServiceType<IConfigService, ConfigService>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<IModConfigService, ModConfigService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<IParserServiceAsync<ResourceParserInfo, IAssemblyResourceInfo>, ConfigFileParserService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<IParserServiceAsync<ResourceParserInfo, ILuaScriptResourceInfo>, ConfigFileParserService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<IParserServiceAsync<ResourceParserInfo, IConfigResourceInfo>, ConfigFileParserService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<IParserServiceAsync<ResourceParserInfo, IAssemblyResourceInfo>, ModConfigFileParserService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<IParserServiceAsync<ResourceParserInfo, ILuaScriptResourceInfo>, ModConfigFileParserService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<IParserServiceAsync<ResourceParserInfo, IConfigResourceInfo>, ModConfigFileParserService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<IParserServiceOneToManyAsync<IConfigResourceInfo, IConfigInfo>, SettingsFileParserService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<IParserServiceOneToManyAsync<IConfigResourceInfo, IConfigProfileInfo>, SettingsFileParserService>(ServiceLifetime.Transient);
// service config data
servicesProvider.RegisterServiceType<IStorageServiceConfig, StorageServiceConfig>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<ILuaScriptServicesConfig, LuaScriptServicesConfig>(ServiceLifetime.Singleton);
@@ -26,37 +26,19 @@ public class ConfigInitializers : IService
// stateless service
public bool IsDisposed => false;
private Result<IConfigEntry<T>> CreateConfigEntry<T>(IConfigInfo configInfo,
Action<ConfigEntry<T>, INetReadMessage> readHandler,
Action<ConfigEntry<T>, INetWriteMessage> writeHandler)
private Result<ISettingEntry<T>> CreateConfigEntry<T>(IConfigInfo configInfo,
Action<SettingEntry<T>, INetReadMessage> readHandler,
Action<SettingEntry<T>, INetWriteMessage> writeHandler)
where T : IEquatable<T>
{
try
{
var ice = new ConfigEntry<T>(configInfo, readHandler, writeHandler);
return FluentResults.Result.Ok<IConfigEntry<T>>(ice);
}
catch (Exception e)
{
return FluentResults.Result.Fail($"Error while initializing config var: {configInfo?.OwnerPackage} - {configInfo?.InternalName}")
.WithError(new ExceptionalError(e));
}
throw new NotImplementedException();
}
private Result<IConfigList<T>> CreateConfigList<T>(IConfigInfo configInfo,
Action<IConfigList<T>, INetReadMessage> readHandler, Action<IConfigList<T>, INetWriteMessage> writeHandler)
private Result<ISettingList<T>> CreateConfigList<T>(IConfigInfo configInfo,
Action<ISettingList<T>, INetReadMessage> readHandler, Action<ISettingList<T>, INetWriteMessage> writeHandler)
where T : IEquatable<T>
{
try
{
var icl = new ConfigList<T>(configInfo, readHandler, writeHandler);
return FluentResults.Result.Ok<IConfigList<T>>(icl);
}
catch (Exception e)
{
return FluentResults.Result.Fail($"Error while initializing config var: {configInfo?.OwnerPackage} - {configInfo?.InternalName}")
.WithError(new ExceptionalError(e));
}
throw new NotImplementedException();
}
public void RegisterTypeInitializers(IConfigService configService)
@@ -64,30 +46,30 @@ public class ConfigInitializers : IService
if (configService == null)
throw new ArgumentNullException($"{nameof(RegisterTypeInitializers)}: {nameof(IConfigService)} is null.");
configService.RegisterTypeInitializer<bool, IConfigEntry<bool>>(this.CreateConfigBool);
configService.RegisterTypeInitializer<sbyte, IConfigEntry<sbyte>>(this.CreateConfigSbyte);
configService.RegisterTypeInitializer<byte, IConfigEntry<byte>>(this.CreateConfigByte);
configService.RegisterTypeInitializer<short, IConfigEntry<short>>(this.CreateConfigShort);
configService.RegisterTypeInitializer<ushort, IConfigEntry<ushort>>(this.CreateConfigUShort);
configService.RegisterTypeInitializer<int, IConfigEntry<int>>(this.CreateConfigInt32);
configService.RegisterTypeInitializer<uint, IConfigEntry<uint>>(this.CreateConfigUInt32);
configService.RegisterTypeInitializer<long, IConfigEntry<long>>(this.CreateConfigInt64);
configService.RegisterTypeInitializer<ulong, IConfigEntry<ulong>>(this.CreateConfigUInt64);
configService.RegisterTypeInitializer<float, IConfigEntry<float>>(this.CreateConfigFloat32);
configService.RegisterTypeInitializer<double, IConfigEntry<double>>(this.CreateConfigFloat64);
configService.RegisterTypeInitializer<decimal, IConfigEntry<decimal>>(this.CreateConfigFloat128);
configService.RegisterTypeInitializer<char, IConfigEntry<char>>(this.CreateConfigChar);
configService.RegisterTypeInitializer<string, IConfigEntry<string>>(this.CreateConfigString);
configService.RegisterTypeInitializer<Color, IConfigEntry<Color>>(this.CreateConfigColor);
configService.RegisterTypeInitializer<Vector2, IConfigEntry<Vector2>>(this.CreateConfigVector2);
configService.RegisterTypeInitializer<Vector3, IConfigEntry<Vector3>>(this.CreateConfigVector3);
configService.RegisterTypeInitializer<Vector4, IConfigEntry<Vector4>>(this.CreateConfigVector4);
/*configService.RegisterTypeInitializer<bool, ISettingEntry<bool>>(this.CreateConfigBool);
configService.RegisterTypeInitializer<sbyte, ISettingEntry<sbyte>>(this.CreateConfigSbyte);
configService.RegisterTypeInitializer<byte, ISettingEntry<byte>>(this.CreateConfigByte);
configService.RegisterTypeInitializer<short, ISettingEntry<short>>(this.CreateConfigShort);
configService.RegisterTypeInitializer<ushort, ISettingEntry<ushort>>(this.CreateConfigUShort);
configService.RegisterTypeInitializer<int, ISettingEntry<int>>(this.CreateConfigInt32);
configService.RegisterTypeInitializer<uint, ISettingEntry<uint>>(this.CreateConfigUInt32);
configService.RegisterTypeInitializer<long, ISettingEntry<long>>(this.CreateConfigInt64);
configService.RegisterTypeInitializer<ulong, ISettingEntry<ulong>>(this.CreateConfigUInt64);
configService.RegisterTypeInitializer<float, ISettingEntry<float>>(this.CreateConfigFloat32);
configService.RegisterTypeInitializer<double, ISettingEntry<double>>(this.CreateConfigFloat64);
configService.RegisterTypeInitializer<decimal, ISettingEntry<decimal>>(this.CreateConfigFloat128);
configService.RegisterTypeInitializer<char, ISettingEntry<char>>(this.CreateConfigChar);
configService.RegisterTypeInitializer<string, ISettingEntry<string>>(this.CreateConfigString);
configService.RegisterTypeInitializer<Color, ISettingEntry<Color>>(this.CreateConfigColor);
configService.RegisterTypeInitializer<Vector2, ISettingEntry<Vector2>>(this.CreateConfigVector2);
configService.RegisterTypeInitializer<Vector3, ISettingEntry<Vector3>>(this.CreateConfigVector3);
configService.RegisterTypeInitializer<Vector4, ISettingEntry<Vector4>>(this.CreateConfigVector4);*/
}
#region InitializerWrappers_NetworkInjected
private void AssignValueConditional<T>(T val, IConfigEntry<T> inst) where T : IEquatable<T>
private void AssignValueConditional<T>(T val, ISettingEntry<T> inst) where T : IEquatable<T>
{
#if SERVER
if (inst.SyncType is NetSync.None or NetSync.ServerAuthority)
@@ -100,7 +82,7 @@ public class ConfigInitializers : IService
#endif
}
private Result<IConfigEntry<bool>> CreateConfigBool(IConfigInfo configInfo)
private Result<ISettingEntry<bool>> CreateConfigBool(IConfigInfo configInfo)
{
return CreateConfigEntry<bool>(configInfo, (inst, readMsg) =>
{
@@ -112,7 +94,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<sbyte>> CreateConfigSbyte(IConfigInfo configInfo)
private Result<ISettingEntry<sbyte>> CreateConfigSbyte(IConfigInfo configInfo)
{
return CreateConfigEntry<sbyte>(configInfo, (inst, readMsg) =>
{
@@ -124,7 +106,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<byte>> CreateConfigByte(IConfigInfo configInfo)
private Result<ISettingEntry<byte>> CreateConfigByte(IConfigInfo configInfo)
{
return CreateConfigEntry<byte>(configInfo, (inst, readMsg) =>
{
@@ -136,7 +118,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<short>> CreateConfigShort(IConfigInfo configInfo)
private Result<ISettingEntry<short>> CreateConfigShort(IConfigInfo configInfo)
{
return CreateConfigEntry<short>(configInfo, (inst, readMsg) =>
{
@@ -148,7 +130,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<ushort>> CreateConfigUShort(IConfigInfo configInfo)
private Result<ISettingEntry<ushort>> CreateConfigUShort(IConfigInfo configInfo)
{
return CreateConfigEntry<ushort>(configInfo, (inst, readMsg) =>
{
@@ -160,7 +142,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<int>> CreateConfigInt32(IConfigInfo configInfo)
private Result<ISettingEntry<int>> CreateConfigInt32(IConfigInfo configInfo)
{
return CreateConfigEntry<int>(configInfo, (inst, readMsg) =>
{
@@ -172,7 +154,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<uint>> CreateConfigUInt32(IConfigInfo configInfo)
private Result<ISettingEntry<uint>> CreateConfigUInt32(IConfigInfo configInfo)
{
return CreateConfigEntry<uint>(configInfo, (inst, readMsg) =>
{
@@ -184,7 +166,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<long>> CreateConfigInt64(IConfigInfo configInfo)
private Result<ISettingEntry<long>> CreateConfigInt64(IConfigInfo configInfo)
{
return CreateConfigEntry<long>(configInfo, (inst, readMsg) =>
{
@@ -196,7 +178,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<ulong>> CreateConfigUInt64(IConfigInfo configInfo)
private Result<ISettingEntry<ulong>> CreateConfigUInt64(IConfigInfo configInfo)
{
return CreateConfigEntry<ulong>(configInfo, (inst, readMsg) =>
{
@@ -208,7 +190,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<float>> CreateConfigFloat32(IConfigInfo configInfo)
private Result<ISettingEntry<float>> CreateConfigFloat32(IConfigInfo configInfo)
{
return CreateConfigEntry<float>(configInfo, (inst, readMsg) =>
{
@@ -220,7 +202,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<double>> CreateConfigFloat64(IConfigInfo configInfo)
private Result<ISettingEntry<double>> CreateConfigFloat64(IConfigInfo configInfo)
{
return CreateConfigEntry<double>(configInfo, (inst, readMsg) =>
{
@@ -232,7 +214,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<decimal>> CreateConfigFloat128(IConfigInfo configInfo)
private Result<ISettingEntry<decimal>> CreateConfigFloat128(IConfigInfo configInfo)
{
return CreateConfigEntry<decimal>(configInfo, (inst, readMsg) =>
{
@@ -253,7 +235,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<char>> CreateConfigChar(IConfigInfo configInfo)
private Result<ISettingEntry<char>> CreateConfigChar(IConfigInfo configInfo)
{
return CreateConfigEntry<char>(configInfo, (inst, readMsg) =>
{
@@ -265,7 +247,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<string>> CreateConfigString(IConfigInfo configInfo)
private Result<ISettingEntry<string>> CreateConfigString(IConfigInfo configInfo)
{
return CreateConfigEntry<string>(configInfo, (inst, readMsg) =>
{
@@ -277,7 +259,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<Color>> CreateConfigColor(IConfigInfo configInfo)
private Result<ISettingEntry<Color>> CreateConfigColor(IConfigInfo configInfo)
{
return CreateConfigEntry<Color>(configInfo, (inst, readMsg) =>
{
@@ -289,7 +271,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<Vector2>> CreateConfigVector2(IConfigInfo configInfo)
private Result<ISettingEntry<Vector2>> CreateConfigVector2(IConfigInfo configInfo)
{
return CreateConfigEntry<Vector2>(configInfo, (inst, readMsg) =>
{
@@ -302,7 +284,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<Vector3>> CreateConfigVector3(IConfigInfo configInfo)
private Result<ISettingEntry<Vector3>> CreateConfigVector3(IConfigInfo configInfo)
{
return CreateConfigEntry<Vector3>(configInfo, (inst, readMsg) =>
{
@@ -316,7 +298,7 @@ public class ConfigInitializers : IService
});
}
private Result<IConfigEntry<Vector4>> CreateConfigVector4(IConfigInfo configInfo)
private Result<ISettingEntry<Vector4>> CreateConfigVector4(IConfigInfo configInfo)
{
return CreateConfigEntry<Vector4>(configInfo, (inst, readMsg) =>
{
@@ -6,217 +6,318 @@ using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Barotrauma.LuaCs.Configuration;
using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs.Events;
using Barotrauma.LuaCs.Services.Processing;
using Barotrauma.Networking;
using Dynamitey.DynamicObjects;
using FluentResults;
using Microsoft.Xna.Framework;
using OneOf;
using Path = Barotrauma.IO.Path;
using Microsoft.Toolkit.Diagnostics;
namespace Barotrauma.LuaCs.Services;
public partial class ConfigService : IConfigService
public sealed partial class ConfigService : IConfigService
{
#region Disposal_Locks_Reset
private readonly AsyncReaderWriterLock _operationLock = new ();
private readonly AsyncReaderWriterLock _settingsByPackageLock = new ();
private int _isDisposed = 0;
public bool IsDisposed
{
get => ModUtils.Threading.GetBool(ref _isDisposed);
private set => ModUtils.Threading.SetBool(ref _isDisposed, value);
}
public void Dispose()
{
throw new NotImplementedException();
}
using var lck = _operationLock.AcquireWriterLock().ConfigureAwait(false).GetAwaiter().GetResult();
using var settingsLck = _settingsByPackageLock.AcquireWriterLock().ConfigureAwait(false).GetAwaiter().GetResult();
if (!ModUtils.Threading.CheckIfClearAndSetBool(ref _isDisposed))
{
return;
}
_logger.LogDebug($"{nameof(ConfigService)}: Disposing.");
_configInfoParserService.Dispose();
_configProfileInfoParserService.Dispose();
public bool IsDisposed { get; }
if (!_settingsInstances.IsEmpty)
{
foreach (var instance in _settingsInstances)
{
try
{
if (instance.Value is null)
{
continue;
}
_eventService.PublishEvent<IEventSettingInstanceLifetime>(sub =>
// ReSharper disable once AccessToDisposedClosure
sub.OnSettingInstanceDisposed(instance.Value));
instance.Value.Dispose();
}
catch
{
// ignored
continue;
}
}
}
_settingsInstances.Clear();
_instanceFactory.Clear();
_settingsInstancesByPackage.Clear();
_storageService = null;
_logger = null;
_eventService = null;
_configInfoParserService = null;
_configProfileInfoParserService = null;
}
public FluentResults.Result Reset()
{
throw new NotImplementedException();
}
using var lck = _operationLock.AcquireWriterLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
var result = new FluentResults.Result();
if (!_settingsInstances.IsEmpty)
{
foreach (var instance in _settingsInstances)
{
try
{
if (instance.Value is null)
{
continue;
}
#region LuaInterface
_eventService.PublishEvent<IEventSettingInstanceLifetime>(sub =>
// ReSharper disable once AccessToDisposedClosure
sub.OnSettingInstanceDisposed(instance.Value));
instance.Value.Dispose();
}
catch (Exception e)
{
result.WithError(new ExceptionalError(e));
}
}
}
_settingsInstances.Clear();
_instanceFactory.Clear();
_settingsInstancesByPackage.Clear();
public bool TryGetConfigBool(string packageName, string configName, out bool value)
{
throw new NotImplementedException();
return result;
}
public bool TryGetConfigInt(string packageName, string configName, out int value)
{
throw new NotImplementedException();
}
public bool TryGetConfigFloat(string packageName, string configName, out float value)
{
throw new NotImplementedException();
}
public bool TryGetConfigNumber(string packageName, string configName, out double value)
{
throw new NotImplementedException();
}
public bool TryGetConfigString(string packageName, string configName, out string value)
{
throw new NotImplementedException();
}
public bool TryGetConfigVector2(string packageName, string configName, out Vector2 value)
{
throw new NotImplementedException();
}
public bool TryGetConfigVector3(string packageName, string configName, out Vector3 value)
{
throw new NotImplementedException();
}
public bool TryGetConfigColor(string packageName, string configName, out Color value)
{
throw new NotImplementedException();
}
public bool TryGetConfigList(string packageName, string configName, out IReadOnlyList<string> value)
{
throw new NotImplementedException();
}
public void SetConfigBool(string packageName, string configName, bool value)
{
throw new NotImplementedException();
}
public void SetConfigInt(string packageName, string configName, int value)
{
throw new NotImplementedException();
}
public void SetConfigFloat(string packageName, string configName, float value)
{
throw new NotImplementedException();
}
public void SetConfigNumber(string packageName, string configName, double value)
{
throw new NotImplementedException();
}
public void SetConfigString(string packageName, string configName, string value)
{
throw new NotImplementedException();
}
public void SetConfigVector2(string packageName, string configName, Vector2 value)
{
throw new NotImplementedException();
}
public void SetConfigVector3(string packageName, string configName, Vector3 value)
{
throw new NotImplementedException();
}
public void SetConfigColor(string packageName, string configName, Color value)
{
throw new NotImplementedException();
}
public void SetConfigList(string packageName, string configName, string value)
{
throw new NotImplementedException();
}
public bool TryApplyProfileSettings(string packageName, string profileName)
{
throw new NotImplementedException();
}
#endregion
public void RegisterTypeInitializer<TData, TConfig>(Func<IConfigInfo, Result<TConfig>> initializer, bool replaceIfExists = false) where TData : IEquatable<TData> where TConfig : IConfigBase
private readonly ConcurrentDictionary<(ContentPackage OwnerPackage, string InternalName), ISettingBase>
_settingsInstances = new();
private readonly ConcurrentDictionary<string, Func<IConfigInfo, ISettingBase>>
_instanceFactory = new();
private readonly ConcurrentDictionary<ContentPackage, ConcurrentBag<ISettingBase>>
_settingsInstancesByPackage = new();
private IStorageService _storageService;
private ILoggerService _logger;
private IEventService _eventService;
private IParserServiceOneToManyAsync<IConfigResourceInfo, IConfigInfo> _configInfoParserService;
private IParserServiceOneToManyAsync<IConfigResourceInfo, IConfigProfileInfo> _configProfileInfoParserService;
public ConfigService(ILoggerService logger,
IStorageService storageService,
IParserServiceOneToManyAsync<IConfigResourceInfo, IConfigInfo> configInfoParserService,
IParserServiceOneToManyAsync<IConfigResourceInfo, IConfigProfileInfo> configProfileInfoParserService, IEventService eventService)
{
throw new NotImplementedException();
_logger = logger;
_storageService = storageService;
_configInfoParserService = configInfoParserService;
_configProfileInfoParserService = configProfileInfoParserService;
_eventService = eventService;
}
public void RegisterSettingTypeInitializer<T>(string typeIdentifier, Func<IConfigInfo, T> settingFactory) where T : class, ISettingBase
{
Guard.IsNotNullOrWhiteSpace(typeIdentifier, nameof(typeIdentifier));
Guard.IsNotNull(settingFactory, nameof(settingFactory));
using var lck = _operationLock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
if (_instanceFactory.ContainsKey(typeIdentifier))
{
ThrowHelper.ThrowArgumentException($"{nameof(RegisterSettingTypeInitializer)}: The type identifier {typeIdentifier} is already registered.");
}
_instanceFactory[typeIdentifier] = settingFactory;
}
public async Task<FluentResults.Result> LoadConfigsAsync(ImmutableArray<IConfigResourceInfo> configResources)
{
#if DEBUG
return FluentResults.Result.Ok(); // just for startup testing
#endif
throw new NotImplementedException();
using var lck = _operationLock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
if (configResources.IsDefaultOrEmpty)
{
return FluentResults.Result.Ok();
}
var taskBuilder = ImmutableArray.CreateBuilder<Task<ImmutableArray<IConfigInfo>>>();
var toProcessErrors = new ConcurrentStack<IError>();
var taskCtx = TaskScheduler.FromCurrentSynchronizationContext();
foreach (var resource in configResources)
{
taskBuilder.Add(await Task.Factory.StartNew<Task<ImmutableArray<IConfigInfo>>>(async Task<ImmutableArray<IConfigInfo>> () =>
{
var r = await _configInfoParserService.TryParseResourcesAsync(resource);
if (r.IsFailed)
{
toProcessErrors.PushRange(r.Errors.ToArray());
return ImmutableArray<IConfigInfo>.Empty;
}
return r.Value;
}));
}
var taskResults = await Task.WhenAll(taskBuilder.MoveToImmutable());
if (toProcessErrors.Count > 0)
{
return FluentResults.Result.Fail($"{nameof(LoadConfigsAsync)}: Errors while loading configuration info: ").WithErrors(toProcessErrors.ToArray());
}
var toProcessDocs = taskResults
.Where(tr => !tr.IsDefaultOrEmpty)
.SelectMany(tr => tr)
.ToImmutableArray();
var instanceQueue = new Queue<(IConfigInfo configInfo, Func<IConfigInfo, ISettingBase> factory)>();
foreach (var info in toProcessDocs)
{
if (!_instanceFactory.TryGetValue(info.DataType, out var factory))
{
return FluentResults.Result.Fail($"{nameof(LoadConfigsAsync)}: Could not retrieve the instance factory for the data type of '{info.DataType}'!");
}
if (_settingsInstances.ContainsKey((info.OwnerPackage, info.InternalName)))
{
// duplicate for some reason (ie. double loading). This should never happen.
ThrowHelper.ThrowInvalidOperationException($"{nameof(LoadConfigsAsync)}: A setting for the [ContentPackage].[InternalName] of '[{info.OwnerPackage.Name}].[{info.InternalName}]' already exists!");
}
instanceQueue.Enqueue((info, factory));
}
var toProcessInstanceQueue = new Queue<(IConfigInfo info, ISettingBase instance)>();
while (instanceQueue.TryDequeue(out var instanceFactoryInfo))
{
try
{
toProcessInstanceQueue.Enqueue((instanceFactoryInfo.configInfo, instanceFactoryInfo.factory(instanceFactoryInfo.configInfo)));
}
catch (Exception e)
{
FluentResults.Result.Fail(
$"{nameof(LoadConfigsAsync)}: Error while instancing setting for '{instanceFactoryInfo.configInfo.OwnerPackage}.{instanceFactoryInfo.configInfo.InternalName}': {e.Message}!");
}
}
using var settingsLck = await _settingsByPackageLock.AcquireWriterLock(); // block to protect new bag instance creation
var result = new FluentResults.Result();
while (toProcessInstanceQueue.TryDequeue(out var newInstanceData))
{
_settingsInstances[(newInstanceData.info.OwnerPackage, newInstanceData.info.InternalName)] = newInstanceData.instance;
if (!_settingsInstancesByPackage.TryGetValue(newInstanceData.info.OwnerPackage, out _))
{
_settingsInstancesByPackage[newInstanceData.info.OwnerPackage] = new ConcurrentBag<ISettingBase>();
}
_settingsInstancesByPackage[newInstanceData.info.OwnerPackage].Add(newInstanceData.instance);
result.WithReasons(_eventService.PublishEvent<IEventSettingInstanceLifetime>(sub =>
sub.OnSettingInstanceCreated(newInstanceData.instance)).Reasons);
}
return result;
}
public async Task<FluentResults.Result> LoadConfigsProfilesAsync(ImmutableArray<IConfigResourceInfo> configProfileResources)
{
#if DEBUG
return FluentResults.Result.Ok(); // just for startup testing
// TODO: Implement profiles.
return FluentResults.Result.Ok();
#endif
throw new NotImplementedException();
}
public Result<TConfig> AddConfig<TConfig>(IConfigInfo configInfo) where TConfig : IConfigBase
{
throw new NotImplementedException();
}
public FluentResults.Result ApplyProfileSettings(ContentPackage package, string profileName)
{
throw new NotImplementedException();
}
public FluentResults.Result DisposePackageData(ContentPackage package)
{
#if DEBUG
return FluentResults.Result.Ok(); // just for startup testing
#endif
throw new NotImplementedException();
Guard.IsNotNull(package, nameof(package));
using var lck = _operationLock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
ConcurrentBag<ISettingBase> toDispose;
using (var settingsLck = _settingsByPackageLock.AcquireWriterLock().ConfigureAwait(false).GetAwaiter().GetResult())
{
if (!_settingsInstancesByPackage.TryRemove(package, out toDispose) || toDispose is null)
{
return FluentResults.Result.Ok();
}
}
var result = new FluentResults.Result();
foreach (var setting in toDispose)
{
result.WithReasons(_eventService.PublishEvent<IEventSettingInstanceLifetime>(sub => sub.OnSettingInstanceDisposed(setting)).Reasons);
try
{
setting.Dispose();
}
catch (Exception e)
{
result.WithError(new ExceptionalError(e));
}
}
return result;
}
public FluentResults.Result DisposeAllPackageData()
{
#if DEBUG
return FluentResults.Result.Ok(); // just for startup testing
#endif
throw new NotImplementedException();
return this.Reset();
}
public Result<IReadOnlyDictionary<(ContentPackage Package, string ConfigName), IConfigBase>> GetConfigsForPackage(ContentPackage package)
public bool TryGetConfig<T>(ContentPackage package, string internalName, out T instance) where T : ISettingBase
{
throw new NotImplementedException();
}
Guard.IsNotNull(package, nameof(package));
Guard.IsNotNullOrWhiteSpace(internalName, nameof(internalName));
using var lck = _operationLock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
using var settingsLck =
_settingsByPackageLock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
instance = default;
if(!_settingsInstances.TryGetValue((package, internalName), out var inst))
{
return false;
}
public Result<IReadOnlyDictionary<(ContentPackage Package, string ConfigName), ImmutableArray<(string ConfigName, OneOf<string, XElement> Value)>>> GetProfilesForPackage(ContentPackage package)
{
throw new NotImplementedException();
}
public IReadOnlyDictionary<(ContentPackage Package, string Name), IConfigBase> GetAllConfigs()
{
throw new NotImplementedException();
}
public bool TryGetConfig<T>(ContentPackage package, string name, out T config) where T : IConfigBase
{
#if DEBUG
config = default(T);
return true; // just for startup testing
#endif
throw new NotImplementedException();
}
public async Task<FluentResults.Result> SaveAllConfigs()
{
throw new NotImplementedException();
}
public async Task<FluentResults.Result> SaveConfigsForPackage(ContentPackage package)
{
throw new NotImplementedException();
}
public async Task<FluentResults.Result> SaveConfig((ContentPackage Package, string ConfigName) config)
{
throw new NotImplementedException();
if (inst is not T instanceT)
{
return false;
}
instance = instanceT;
return true;
}
}
@@ -325,6 +325,9 @@ public class EventService : IEventService, IEventAssemblyContextUnloading
}
catch (Exception e)
{
#if DEBUG
throw; //make errors apparent
#endif
errors.Enqueue(new Error($"Error while executing runner for {eventType.Name} on type {eventSub.GetType().Name}.")
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, eventSub)
@@ -18,3 +18,8 @@ public interface IParserServiceAsync<in TSrc, TOut> : IService
Task<Result<TOut>> TryParseResourceAsync(TSrc src);
Task<ImmutableArray<Result<TOut>>> TryParseResourcesAsync(IEnumerable<TSrc> sources);
}
public interface IParserServiceOneToManyAsync<in TSrc, TOut> : IService
{
Task<Result<ImmutableArray<TOut>>> TryParseResourcesAsync(TSrc src);
}
@@ -10,7 +10,7 @@ using Microsoft.Toolkit.Diagnostics;
namespace Barotrauma.LuaCs.Services.Processing;
public sealed class ConfigFileParserService :
public sealed class ModConfigFileParserService :
IParserServiceAsync<ResourceParserInfo, IAssemblyResourceInfo>,
IParserServiceAsync<ResourceParserInfo, ILuaScriptResourceInfo>,
IParserServiceAsync<ResourceParserInfo, IConfigResourceInfo>
@@ -18,7 +18,7 @@ public sealed class ConfigFileParserService :
private IStorageService _storageService;
private readonly AsyncReaderWriterLock _operationsLock = new();
public ConfigFileParserService(IStorageService storageService)
public ModConfigFileParserService(IStorageService storageService)
{
_storageService = storageService;
}
@@ -0,0 +1,212 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq;
using Barotrauma.LuaCs.Data;
using FluentResults;
using Microsoft.Toolkit.Diagnostics;
using OneOf;
namespace Barotrauma.LuaCs.Services.Processing;
public sealed class SettingsFileParserService :
IParserServiceOneToManyAsync<IConfigResourceInfo, IConfigInfo>,
IParserServiceOneToManyAsync<IConfigResourceInfo, IConfigProfileInfo>
{
#region DisposalControl
private AsyncReaderWriterLock _operationLock = new();
public void Dispose()
{
using var lck = _operationLock.AcquireWriterLock().ConfigureAwait(false).GetAwaiter().GetResult();
if (!ModUtils.Threading.CheckIfClearAndSetBool(ref _isDisposed))
{
return;
}
_storageService.Dispose();
_storageService = null;
}
private int _isDisposed = 0;
public bool IsDisposed
{
get => ModUtils.Threading.GetBool(ref _isDisposed);
private set => ModUtils.Threading.SetBool(ref _isDisposed, value);
}
#endregion
private IStorageService _storageService;
public SettingsFileParserService(IStorageService storageService)
{
_storageService = storageService;
}
async Task<Result<ImmutableArray<IConfigInfo>>> IParserServiceOneToManyAsync<IConfigResourceInfo, IConfigInfo>
.TryParseResourcesAsync(IConfigResourceInfo src)
{
Guard.IsNotNull(src, nameof(src));
Guard.IsNotNull(src.OwnerPackage, nameof(src.OwnerPackage));
using var lck = await _operationLock.AcquireReaderLock();
IService.CheckDisposed(this);
if (src.FilePaths.IsDefaultOrEmpty)
{
return ReturnFail($"The config file list is empty.");
}
var parsedInfo = ImmutableArray.CreateBuilder<IConfigInfo>();
foreach ((ContentPath path, Result<XDocument> docLoadResult) res in await _storageService.LoadPackageXmlFilesAsync(src.FilePaths))
{
if (res.docLoadResult.IsFailed)
{
return ReturnFail($"Failed to load document for {src.OwnerPackage.Name}").WithErrors(res.docLoadResult.Errors);
}
var settingElements = res.docLoadResult.Value.GetChildElement("Configuration")
.GetChildElements("Settings").SelectMany(e => e.GetChildElements("Setting")).ToImmutableArray();
if (settingElements.IsDefaultOrEmpty)
{
continue;
}
var packageIdent = res.path.ContentPackage.ToIdentifier().ToString();
foreach (var element in settingElements)
{
var name = element.GetAttributeString("Name", string.Empty);
if (name.IsNullOrWhiteSpace())
{
return ReturnFail(
$"The internal name for a setting in the config file '{res.path.FullPath}' is empty!");
}
var newSetting = new ConfigInfo()
{
InternalName = name,
OwnerPackage = res.path.ContentPackage,
DataType = element.GetAttributeString("Type", string.Empty),
Element = element,
EditableStates = element.GetAttributeBool("AllowChangesWhileExecuting", true) ? RunState.Running :
element.GetAttributeBool("ReadOnly", false) ? RunState.LoadedNoExec :
RunState.Unloaded,
NetSync = element.GetAttributeEnum("NetSync", NetSync.None),
#if CLIENT
DisplayName = $"{packageIdent}.{name}.DisplayName",
Description = $"{packageIdent}.{name}.Description",
DisplayCategory = $"{packageIdent}.{name}.DisplayCategory",
ShowInMenus = element.GetAttributeBool("ShowInMenus", true),
Tooltip = $"{packageIdent}.{name}.Tooltip",
ImageIconPath = element.GetAttributeString("ImageIcon", string.Empty) is {} val && !val.IsNullOrWhiteSpace() ?
ContentPath.FromRaw(res.path.ContentPackage, val) : ContentPath.Empty
#endif
};
if (!IsInfoValid(newSetting))
{
return ReturnFail($"A setting was invalid. ContentPackage: {res.path.ContentPackage}");
}
parsedInfo.Add(newSetting);
}
}
return FluentResults.Result.Ok(parsedInfo.MoveToImmutable());
// Helpers
FluentResults.Result ReturnFail(string msg)
{
return FluentResults.Result.Fail($"{nameof(IParserServiceOneToManyAsync<IConfigResourceInfo, IConfigInfo>.TryParseResourcesAsync)}: {msg}");
}
bool IsInfoValid(ConfigInfo info)
{
return info.OwnerPackage != null
&& !info.InternalName.IsNullOrWhiteSpace()
&& !info.DataType.IsNullOrWhiteSpace()
&& !info.DataType.IsNullOrWhiteSpace()
&& info.Element != null
#if CLIENT
&& !info.DisplayName.IsNullOrWhiteSpace()
&& !info.Description.IsNullOrWhiteSpace()
&& !info.DisplayCategory.IsNullOrWhiteSpace()
&& !info.Tooltip.IsNullOrWhiteSpace()
#endif
;
}
}
async Task<Result<ImmutableArray<IConfigProfileInfo>>>
IParserServiceOneToManyAsync<IConfigResourceInfo, IConfigProfileInfo>
.TryParseResourcesAsync(IConfigResourceInfo src)
{
Guard.IsNotNull(src, nameof(src));
Guard.IsNotNull(src.OwnerPackage, nameof(src.OwnerPackage));
using var lck = await _operationLock.AcquireReaderLock();
IService.CheckDisposed(this);
if (src.FilePaths.IsDefaultOrEmpty)
{
return ReturnFail($"The config file list is empty.");
}
var parsedInfo = ImmutableArray.CreateBuilder<IConfigProfileInfo>();
foreach ((ContentPath path, Result<XDocument> docLoadResult) res in await _storageService
.LoadPackageXmlFilesAsync(src.FilePaths))
{
if (res.docLoadResult.IsFailed)
{
return ReturnFail($"Failed to load document for {src.OwnerPackage.Name}")
.WithErrors(res.docLoadResult.Errors);
}
var profileCollection = res.docLoadResult.Value.GetChildElement("Configuration")
.GetChildElement("Profiles");
if (profileCollection == null)
{
continue;
}
foreach (var profile in profileCollection.GetChildElements("Profile"))
{
var profileName = profile.GetAttributeString("Name", string.Empty);
Guard.IsNotNullOrWhiteSpace(profileName, nameof(profileName));
var settingValues = profile.GetChildElements("SettingValue").ToImmutableArray();
if (settingValues.IsDefaultOrEmpty)
{
ThrowHelper.ThrowArgumentNullException(nameof(settingValues));
}
var profileValuesBuilder = ImmutableArray.CreateBuilder<(string ConfigName, XElement Value)>();
foreach (var settingValue in settingValues)
{
var cfgName = settingValue.GetAttributeString("Name", string.Empty);
Guard.IsNotNullOrWhiteSpace(cfgName, nameof(cfgName));
profileValuesBuilder.Add((cfgName, settingValue));
}
parsedInfo.Add(new ConfigProfileInfo()
{
InternalName = profileName,
OwnerPackage = res.path.ContentPackage,
ProfileValues = profileValuesBuilder.MoveToImmutable()
});
}
}
return parsedInfo.MoveToImmutable();
FluentResults.Result ReturnFail(string msg)
{
return FluentResults.Result.Fail($"{nameof(IParserServiceOneToManyAsync<IConfigResourceInfo, IConfigProfileInfo>.TryParseResourcesAsync)}: {msg}");
}
}
}
@@ -6,27 +6,5 @@ namespace Barotrauma.LuaCs.Services.Safe;
public interface ILuaConfigService : ILuaService
{
// get values
bool TryGetConfigBool(string packageName, string configName, out bool value);
bool TryGetConfigInt(string packageName, string configName, out int value);
bool TryGetConfigFloat(string packageName, string configName, out float value);
bool TryGetConfigNumber(string packageName, string configName, out double value);
bool TryGetConfigString(string packageName, string configName, out string value);
bool TryGetConfigVector2(string packageName, string configName, out Vector2 value);
bool TryGetConfigVector3(string packageName, string configName, out Vector3 value);
bool TryGetConfigColor(string packageName, string configName, out Color value);
bool TryGetConfigList(string packageName, string configName, out IReadOnlyList<string> value);
// set values
void SetConfigBool(string packageName, string configName, bool value);
void SetConfigInt(string packageName, string configName, int value);
void SetConfigFloat(string packageName, string configName, float value);
void SetConfigNumber(string packageName, string configName, double value);
void SetConfigString(string packageName, string configName, string value);
void SetConfigVector2(string packageName, string configName, Vector2 value);
void SetConfigVector3(string packageName, string configName, Vector3 value);
void SetConfigColor(string packageName, string configName, Color value);
void SetConfigList(string packageName, string configName, string value);
// profiles
bool TryApplyProfileSettings(string packageName, string profileName);
}
@@ -15,33 +15,18 @@ namespace Barotrauma.LuaCs.Services;
public partial interface IConfigService : IReusableService, ILuaConfigService
{
void RegisterSettingTypeInitializer<T>(string typeIdentifier, Func<IConfigInfo, T> settingFactory)
where T : class, ISettingBase;
/// <summary>
/// Registers a type initializer from instancing config types by indicated type from config.
///
/// </summary>
/// <param name="initializer"></param>
/// <param name="replaceIfExists"></param>
/// <typeparam name="TData">The <see cref="Type"/> as parsed from the configuration info.</typeparam>
/// <typeparam name="TConfig">The resulting configuration instance.</typeparam>
void RegisterTypeInitializer<TData, TConfig>(Func<IConfigInfo, FluentResults.Result<TConfig>> initializer, bool replaceIfExists = false)
where TData : IEquatable<TData> where TConfig : IConfigBase;
// Config Files/Resources
/// <param name="configResources"></param>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="NullReferenceException"></exception>
/// <returns></returns>
Task<FluentResults.Result> LoadConfigsAsync(ImmutableArray<IConfigResourceInfo> configResources);
Task<FluentResults.Result> LoadConfigsProfilesAsync(ImmutableArray<IConfigResourceInfo> configProfileResources);
// Immediate Mode
FluentResults.Result<TConfig> AddConfig<TConfig>(IConfigInfo configInfo) where TConfig : IConfigBase;
// Utility
FluentResults.Result ApplyProfileSettings(ContentPackage package, string profileName);
FluentResults.Result DisposePackageData(ContentPackage package);
FluentResults.Result DisposeAllPackageData();
FluentResults.Result<IReadOnlyDictionary<(ContentPackage Package, string ConfigName), IConfigBase>> GetConfigsForPackage(ContentPackage package);
FluentResults.Result<IReadOnlyDictionary<(ContentPackage Package, string ConfigName), ImmutableArray<(string ConfigName, OneOf.OneOf<string, XElement> Value)>>>
GetProfilesForPackage(ContentPackage package);
IReadOnlyDictionary<(ContentPackage Package, string Name), IConfigBase> GetAllConfigs();
bool TryGetConfig<T>(ContentPackage package, string name, out T config) where T : IConfigBase;
Task<FluentResults.Result> SaveAllConfigs();
Task<FluentResults.Result> SaveConfigsForPackage(ContentPackage package);
Task<FluentResults.Result> SaveConfig((ContentPackage Package, string ConfigName) config);
bool TryGetConfig<T>(ContentPackage package, string internalName, out T instance) where T : ISettingBase;
}