- 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

View File

@@ -2,9 +2,9 @@
namespace Barotrauma.LuaCs.Configuration;
public interface IConfigControl : IConfigBase
public interface ISettingControl : ISettingBase
{
event Action<IConfigControl> OnDown;
event Action<ISettingControl> OnDown;
KeyOrMouse Value { get; }
bool IsAssignable(KeyOrMouse value);
bool TrySetValue(KeyOrMouse value);

View File

@@ -7,11 +7,11 @@ public partial interface IConfigInfo : IConfigDisplayInfo { }
public interface IConfigDisplayInfo
{
/// <summary>
/// User-friendly name or Localization Token.
/// Localization Token for display name.
/// </summary>
string DisplayName { get; }
/// <summary>
/// User-friendly description or Localization Token.
/// Localization Token for description.
/// </summary>
string Description { get; }
/// <summary>
@@ -29,5 +29,5 @@ public interface IConfigDisplayInfo
/// <summary>
/// Icon for display in menus, if available.
/// </summary>
string ImageIconPath { get; }
ContentPath ImageIconPath { get; }
}

View File

@@ -21,8 +21,20 @@ namespace Barotrauma
}
}
public void CheckCsEnabled()
/// <summary>
///
/// </summary>
/// <returns>Returns whether the IsCsEnabled has been changed to true/enabled. Returns false if already enabled.</returns>
public bool CheckCsEnabled()
{
// fast exit if enabled or unavailable.
if (this.IsCsEnabled?.Value ?? true )
{
return false;
}
bool isCsValueChanged = false;
var csharpMods = PackageManagementService.GetLoadedAssemblyPackages();
StringBuilder sb = new StringBuilder();
@@ -38,7 +50,7 @@ namespace Barotrauma
if (GameMain.Client == null || GameMain.Client.IsServerOwner)
{
new GUIMessageBox("", $"You have CSharp mods enabled but don't have the CSharp Scripting enabled, those mods might not work, go to the Main Menu, click on LuaCs Settings and check Enable CSharp Scripting.\n\n{sb}");
return;
return false;
}
GUIMessageBox msg = new GUIMessageBox(
@@ -49,6 +61,7 @@ namespace Barotrauma
msg.Buttons[0].OnClicked = (GUIButton button, object obj) =>
{
this.IsCsEnabled.TrySetValue(true);
isCsValueChanged = true;
return true;
};
@@ -57,6 +70,8 @@ namespace Barotrauma
this.IsCsEnabled.TrySetValue(false);
return true;
};
return isCsValueChanged;
}
/// <summary>
@@ -85,7 +100,10 @@ namespace Barotrauma
case SpriteEditorScreen:
case SubEditorScreen:
case TestScreen: // notes: TestScreen is a Linux edge case editor screen and is deprecated.
CheckCsEnabled();
if (CheckCsEnabled() && this.CurrentRunState >= RunState.Running)
{
SetRunState(RunState.LoadedNoExec);
}
SetRunState(RunState.Running);
break;
default:

View File

@@ -19,7 +19,7 @@ public partial class ConfigService
throw new NotImplementedException();
}
public Result<IConfigControl> AddConfigControl(IConfigInfo configInfo)
public Result<ISettingControl> AddConfigControl(IConfigInfo configInfo)
{
throw new NotImplementedException();
}

View File

@@ -13,5 +13,5 @@ public partial interface IConfigService
ImmutableArray<IDisplayableConfigBase> GetDisplayableConfigs();
ImmutableArray<IDisplayableConfigBase> GetDisplayableConfigsForPackage(ContentPackage package);
FluentResults.Result<IConfigControl> AddConfigControl(IConfigInfo configInfo);
FluentResults.Result<ISettingControl> AddConfigControl(IConfigInfo configInfo);
}

View File

@@ -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();
}
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -3,7 +3,7 @@ using Barotrauma.LuaCs.Services;
namespace Barotrauma.LuaCs.Configuration;
public interface IConfigEnum : IConfigBase, INetworkSyncEntity
public interface ISettingEnum : ISettingBase, INetworkSyncEntity
{
}

View File

@@ -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;
}

View File

@@ -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; }

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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; }

View File

@@ -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; }
}

View File

@@ -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

View File

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

View File

@@ -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) =>
{

View File

@@ -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;
}
}

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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;
}

View File

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

View File

@@ -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);
}

View File

@@ -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;
}