- Config Services almost ready.

- Refactored and flattened namespaces.
This commit is contained in:
MapleWheels
2026-02-05 19:47:47 -05:00
committed by Maplewheels
parent 863ee23583
commit e75208507d
101 changed files with 350 additions and 1276 deletions
@@ -6,7 +6,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml.Linq;
using Barotrauma.LuaCs.Services;
using Barotrauma.LuaCs;
using Barotrauma.Steam;
using OneOf;
@@ -1,6 +1,6 @@
using System;
using System.Xml.Linq;
using Barotrauma.LuaCs.Services;
using Barotrauma.LuaCs;
using Barotrauma.Networking;
namespace Barotrauma.LuaCs.Data;
@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Xml.Linq;
using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs;
using Barotrauma.Networking;
namespace Barotrauma.LuaCs.Data;
public interface ISettingBase : IDataInfo, IEquatable<ISettingBase>, IDisposable
{
/// <summary>
/// Settings production factory. Should be implemented by all types and registered with the Dependency Injector.
/// </summary>
/// <typeparam name="T">An interface type derived from <see cref="ISettingBase"/>.</typeparam>
public interface IFactory<out T> where T : ISettingBase
{
/// <summary>
/// Creates an instance of the given <see cref="ISettingBase"/> type.
/// </summary>
/// <param name="configInfo">Configuration information.</param>
/// <param name="valueChangePredicate">Called before a new value is assigned. Returns a boolean whether to allow
/// the value to be changed to the one given.</param>
/// <returns></returns>
T CreateInstance([NotNull]IConfigInfo configInfo, Func<OneOf<string, XElement, object>, bool> valueChangePredicate);
}
#if CLIENT
IConfigDisplayInfo GetDisplayInfo();
#endif
Type GetValueType();
string GetStringValue();
string GetDefaultStringValue();
bool TrySetValue(OneOf.OneOf<string, XElement> value);
event Action<ISettingBase> OnValueChanged;
OneOf.OneOf<string, XElement> GetSerializableValue();
}
/// <summary>
/// Creates a setting representing a value of the given <see cref="Type"/>. Must be a compatible listed type. <br/>
/// </summary>
/// <typeparam name="T">
/// <b>Compatible Types:</b><br/>
/// Any primitive type:<br/>
/// - <see cref="byte"/><br/>
/// - <see cref="sbyte"/><br/>
/// - <see cref="ushort"/><br/>
/// - <see cref="short"/><br/>
/// - <see cref="int"/><br/>
/// - <see cref="uint"/><br/>
/// - <see cref="long"/><br/>
/// - <see cref="ulong"/><br/>
/// - <see cref="float"/><br/>
/// - <see cref="double"/><br/>
/// Extension types and Enums: <br/>
/// - <see cref="string"/><br/>
/// - <see cref="Enum"/><br/>
/// </typeparam>
public interface ISettingBase<T> : ISettingBase where T : IEquatable<T>, IConvertible
{
[NotNull]
T Value { get; }
[NotNull]
T DefaultValue { get; }
bool TrySetValue(T value);
}
/// <summary>
/// Creates a setting representing a value of the given <see cref="Type"/> with a minimum and maximum value.
/// Can only be either an <see cref="int"/> or a <see cref="float"/>.
/// </summary>
/// <remarks>The type selection is limited by the Undertow implementation of the GUI Slider.</remarks>
/// <typeparam name="T">The value type, either <see cref="int"/> or <see cref="float"/></typeparam>
public interface ISettingRangeBase<T> : ISettingBase<T> where T : IEquatable<T>, IConvertible
{
T MinValue { get; }
T MaxValue { get; }
int IncrementalSteps { get; }
}
/// <summary>
/// Creates a setting representing a value of the given <see cref="Type"/> with a distinct list of selectable values.
/// Must be a type compatible with <see cref="ISettingBase{T}"/>.
/// </summary>
/// <typeparam name="T">The value type. See <see cref="ISettingBase{T}"/></typeparam>
public interface ISettingList<T> : ISettingBase<T> where T : IEquatable<T>, IConvertible
{
IReadOnlyList<T> Options { get; }
IReadOnlyList<string> StringOptions { get; }
}
@@ -7,7 +7,7 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.AccessControl;
using Barotrauma.LuaCs.Services;
using Barotrauma.LuaCs;
using Barotrauma.Networking;
using FluentResults;
using OneOf.Types;
@@ -0,0 +1,60 @@
using System;
using System.Xml.Linq;
using Barotrauma.LuaCs.Data;
using OneOf;
namespace Barotrauma.LuaCs.Data;
public abstract class SettingBase : ISettingBase
{
protected SettingBase(IConfigInfo configInfo)
{
ConfigInfo = configInfo;
}
protected IConfigInfo ConfigInfo { get; private set; }
public string InternalName => ConfigInfo.InternalName;
public ContentPackage OwnerPackage => ConfigInfo.OwnerPackage;
#if CLIENT
public IConfigDisplayInfo GetDisplayInfo() => ConfigInfo;
#endif
public virtual bool Equals(ISettingBase other)
{
return other is not null && (
ReferenceEquals(this, other) || !IsDisposed &&
OwnerPackage == other.OwnerPackage &&
InternalName.Equals(other.InternalName));
}
private int _isDisposed = 0;
protected virtual bool IsDisposed
{
get => ModUtils.Threading.GetBool(ref _isDisposed);
private set => ModUtils.Threading.SetBool(ref _isDisposed, value);
}
public virtual void Dispose()
{
if (!ModUtils.Threading.CheckIfClearAndSetBool(ref _isDisposed))
{
return;
}
ConfigInfo = null;
OnValueChanged = null;
GC.SuppressFinalize(this);
}
// -- Must be implemented
public abstract Type GetValueType();
public abstract string GetStringValue();
public abstract string GetDefaultStringValue();
public abstract bool TrySetValue(OneOf<string, XElement> value);
public event Action<ISettingBase> OnValueChanged;
public abstract OneOf<string, XElement> GetSerializableValue();
}
@@ -0,0 +1,274 @@
using System;
using System.Runtime.CompilerServices;
using System.Xml.Linq;
using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs;
using Barotrauma.Networking;
using Microsoft.Toolkit.Diagnostics;
using OneOf;
namespace Barotrauma.LuaCs.Data;
public class SettingEntry<T> : SettingBase, ISettingBase<T>, INetworkSyncEntity where T : IEquatable<T>, IConvertible
{
public class Factory : ISettingBase.IFactory<ISettingBase<T>>
{
public ISettingBase<T> CreateInstance(IConfigInfo configInfo, Func<OneOf<string, XElement, object>, bool> valueChangePredicate)
{
Guard.IsNotNull(configInfo, nameof(configInfo));
return new SettingEntry<T>(configInfo, valueChangePredicate);
}
}
public SettingEntry(IConfigInfo configInfo,
Func<OneOf<string, XElement, object>, bool> valueChangePredicate)
: base(configInfo)
{
if (!(
typeof(T).IsEnum ||
typeof(T).IsPrimitive ||
typeof(T) == typeof(string)))
{
ThrowHelper.ThrowArgumentException($"{nameof(ISettingBase<T>)}: The type of {nameof(T)} is not an allowed type.");
}
ValueChangePredicate = valueChangePredicate;
try
{
Value = (T)Convert.ChangeType(ConfigInfo.Element.GetAttributeString("Value", null), typeof(T));
}
catch (Exception e) when (e is InvalidCastException or ArgumentNullException)
{
Value = default(T);
}
try
{
DefaultValue = (T)Convert.ChangeType(ConfigInfo.Element.GetAttributeString("Value", null), typeof(T));
}
catch (Exception e) when (e is InvalidCastException or ArgumentNullException)
{
DefaultValue = default(T);
}
}
protected Func<OneOf<string, XElement, object>, bool> ValueChangePredicate;
public T Value { get; protected set; }
public T DefaultValue { get; protected set; }
public virtual bool TrySetValue(T value)
{
if (value is null)
{
return false;
}
if (ValueChangePredicate != null && !ValueChangePredicate(value))
{
return false;
}
Value = value;
return true;
}
public override Type GetValueType() => typeof(T);
public override string GetStringValue() => Value.ToString();
public override string GetDefaultStringValue() => DefaultValue.ToString();
public override bool TrySetValue(OneOf<string, XElement> value)
{
bool isFailed = false;
var typeConvertedValue = value.Match<T>(
(string val) =>
{
try
{
return (T)Convert.ChangeType(val, typeof(T));
}
catch (Exception e)
{
// ignored
isFailed = true;
return default(T);
}
},
(XElement val) =>
{
try
{
return (T)Convert.ChangeType(val.GetAttributeString("Value", null), typeof(T));
}
catch (Exception e)
{
isFailed = true;
return default(T);
}
});
return isFailed || TrySetValue(typeConvertedValue);
}
public override OneOf<string, XElement> GetSerializableValue() => Value.ToString();
// -- Networking
protected IEntityNetworkingService NetworkingService;
public ulong InstanceId => NetworkingService?.GetNetworkIdForInstance(this) ?? 0ul;
public void SetNetworkOwner(IEntityNetworkingService networkingService)
{
NetworkingService = networkingService;
if (NetworkingService is null)
{
return;
}
NetworkingService.RegisterNetVar(this);
}
public NetSync SyncType => ConfigInfo.NetSync;
// needs to be added IConfigInfo
public ClientPermissions WritePermissions => throw new NotImplementedException();
public void ReadNetMessage(IReadMessage message)
{
if (SyncType == NetSync.None || NetworkingService is null)
{
return;
}
try
{
if (typeof(T).IsEnum)
{
TrySetValue((T)(object)message.ReadInt32());
}
// No...there's no better way to do this...
var typeCode = Type.GetTypeCode(typeof(T));
switch (typeCode)
{
case TypeCode.Boolean:
TrySetValue((T)Convert.ChangeType(message.ReadBoolean(), typeCode));
return;
case TypeCode.Byte:
TrySetValue((T)Convert.ChangeType(message.ReadByte(), typeCode));
return;
// SByte not supported by interface
case TypeCode.SByte:
TrySetValue((T)Convert.ChangeType(message.ReadInt16(), typeCode));
return;
case TypeCode.Int16:
TrySetValue((T)Convert.ChangeType(message.ReadInt16(), typeCode));
return;
case TypeCode.Char:
case TypeCode.UInt16:
TrySetValue((T)Convert.ChangeType(message.ReadUInt16(), typeCode));
return;
case TypeCode.Int32:
TrySetValue((T)Convert.ChangeType(message.ReadInt32(), typeCode));
return;
case TypeCode.UInt32:
TrySetValue((T)Convert.ChangeType(message.ReadUInt32(), typeCode));
return;
case TypeCode.Int64:
TrySetValue((T)Convert.ChangeType(message.ReadInt64(), typeCode));
return;
case TypeCode.UInt64:
TrySetValue((T)Convert.ChangeType(message.ReadUInt64(), typeCode));
return;
case TypeCode.Single:
TrySetValue((T)Convert.ChangeType(message.ReadSingle(), typeCode));
return;
case TypeCode.Double:
TrySetValue((T)Convert.ChangeType(message.ReadDouble(), typeCode));
return;
case TypeCode.String:
TrySetValue((T)Convert.ChangeType(message.ReadString(), typeCode));
return;
case TypeCode.Decimal:
default:
ThrowHelper.ThrowNotSupportedException($"{nameof(SettingEntry<T>)}: The type {typeof(T).Name} is not supported.");
break;
}
}
catch (Exception e)
{
// Suppress unless we're testing.
#if DEBUG
throw;
#endif
}
}
public void WriteNetMessage(IWriteMessage message)
{
if (SyncType == NetSync.None || NetworkingService is null)
{
return;
}
try
{
if (typeof(T).IsEnum)
{
message.WriteInt32((int)((IConvertible)Value));
}
// No...there's no better way to do this...
var typeCode = Type.GetTypeCode(typeof(T));
switch (typeCode)
{
case TypeCode.Boolean:
message.WriteBoolean((bool)Convert.ChangeType(Value, typeCode)!);
return;
case TypeCode.Byte:
message.WriteByte((byte)Convert.ChangeType(Value, typeCode)!);
return;
// SByte not supported by interface
case TypeCode.SByte:
message.WriteInt16((short)Convert.ChangeType(Value, typeCode)!);
return;
case TypeCode.Int16:
message.WriteInt16((short)Convert.ChangeType(Value, typeCode)!);
return;
case TypeCode.Char:
case TypeCode.UInt16:
message.WriteUInt16((ushort)Convert.ChangeType(Value, typeCode)!);
return;
case TypeCode.Int32:
message.WriteInt32((int)Convert.ChangeType(Value, typeCode)!);
return;
case TypeCode.UInt32:
message.WriteUInt32((uint)Convert.ChangeType(Value, typeCode)!);
return;
case TypeCode.Int64:
message.WriteInt64((long)Convert.ChangeType(Value, typeCode)!);
return;
case TypeCode.UInt64:
message.WriteUInt64((ulong)Convert.ChangeType(Value, typeCode)!);
return;
case TypeCode.Single:
message.WriteSingle((float)Convert.ChangeType(Value, typeCode)!);
return;
case TypeCode.Double:
message.WriteDouble((double)Convert.ChangeType(Value, typeCode)!);
return;
case TypeCode.String:
message.WriteString((string)Convert.ChangeType(Value, typeCode)!);
return;
case TypeCode.Decimal:
default:
ThrowHelper.ThrowNotSupportedException($"{nameof(SettingEntry<T>)}: The type {typeof(T).Name} is not supported.");
break;
}
}
catch (Exception e)
{
// Suppress unless we're testing.
#if DEBUG
throw;
#endif
}
}
}
@@ -0,0 +1,76 @@
using System;
using System.Xml.Linq;
using Barotrauma.LuaCs.Data;
using Microsoft.Toolkit.Diagnostics;
using OneOf;
namespace Barotrauma.LuaCs.Data;
public abstract class SettingRangeBase<T> : SettingEntry<T>, ISettingRangeBase<T> where T : IEquatable<T>, IConvertible
{
public SettingRangeBase(IConfigInfo configInfo, Func<OneOf<string, XElement, object>, bool> valueChangePredicate) : base(configInfo, valueChangePredicate)
{
}
public T MinValue { get; protected init; }
public T MaxValue { get; protected init; }
public int IncrementalSteps { get; protected init; }
}
public class SettingRangeFloat : SettingRangeBase<float>
{
public class RangeFactory : ISettingBase.IFactory<SettingRangeFloat>
{
public SettingRangeFloat CreateInstance(IConfigInfo configInfo, Func<OneOf<string, XElement, object>, bool> valueChangePredicate)
{
Guard.IsNotNull(configInfo, nameof(configInfo));
return new SettingRangeFloat(configInfo, valueChangePredicate);
}
}
public SettingRangeFloat(IConfigInfo configInfo, Func<OneOf<string, XElement, object>, bool> valueChangePredicate) : base(configInfo, valueChangePredicate)
{
// funny values in case they forget to set them in the config.
MinValue = configInfo.Element.GetAttributeFloat("Min", float.MinValue);
MaxValue = configInfo.Element.GetAttributeFloat("Max", float.MaxValue);
IncrementalSteps = configInfo.Element.GetAttributeInt("Steps", 3);
}
public override bool TrySetValue(float value)
{
if (value > MaxValue || value < MinValue)
{
return false;
}
return base.TrySetValue(value);
}
}
public class SettingRangeInt : SettingRangeBase<int>
{
public class RangeFactory : ISettingBase.IFactory<SettingRangeInt>
{
public SettingRangeInt CreateInstance(IConfigInfo configInfo, Func<OneOf<string, XElement, object>, bool> valueChangePredicate)
{
Guard.IsNotNull(configInfo, nameof(configInfo));
return new SettingRangeInt(configInfo, valueChangePredicate);
}
}
public SettingRangeInt(IConfigInfo configInfo, Func<OneOf<string, XElement, object>, bool> valueChangePredicate) : base(configInfo, valueChangePredicate)
{
// funny values in case they forget to set them in the config.
MinValue = configInfo.Element.GetAttributeInt("Min", int.MinValue);
MaxValue = configInfo.Element.GetAttributeInt("Max", int.MaxValue);
IncrementalSteps = configInfo.Element.GetAttributeInt("Steps", 3);
}
public override bool TrySetValue(int value)
{
if (value > MaxValue || value < MinValue)
{
return false;
}
return base.TrySetValue(value);
}
}
@@ -0,0 +1,67 @@
using System;
using System.Xml.Linq;
using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs;
namespace Barotrauma.LuaCs.Data;
public interface ISettingsRegistrationProvider : IService
{
void RegisterTypeProviders(IConfigService configService, Func<OneOf<string, XElement, object>, bool> valueChangePredicate);
}
public class SettingsEntryRegistrar : ISettingsRegistrationProvider
{
private ILuaCsInfoProvider _infoProvider;
public SettingsEntryRegistrar(ILuaCsInfoProvider infoProvider)
{
_infoProvider = infoProvider;
}
public void RegisterTypeProviders(IConfigService configService, Func<OneOf<string, XElement, object>, bool> valueChangePredicate)
{
// ISettingBase<T>
RegisterSettingEntry<bool>(configService, "bool");
RegisterSettingEntry<byte>(configService, "byte");
RegisterSettingEntry<sbyte>(configService, "sbyte");
RegisterSettingEntry<short>(configService, "short");
RegisterSettingEntry<ushort>(configService, "ushort");
RegisterSettingEntry<int>(configService, "int");
RegisterSettingEntry<uint>(configService, "uint");
RegisterSettingEntry<long>(configService, "long");
RegisterSettingEntry<ulong>(configService, "ulong");
RegisterSettingEntry<string>(configService, "string");
// ISettingRangeBase<T>
// ISettingList
}
private void RegisterSettingEntry<T>(IConfigService configService, string typeName) where T : IEquatable<T>, IConvertible
{
configService.RegisterSettingTypeInitializer(typeName, cfgInfo =>
{
return new SettingEntry<bool>.Factory().CreateInstance(cfgInfo.Info, (val) =>
{
return !cfgInfo.Info.Element.GetAttributeBool("ReadOnly", false)
&& cfgInfo.Info.EditableStates.HasFlag(_infoProvider?.CurrentRunState ?? RunState.Running);
});
});
}
public void Dispose()
{
if (!ModUtils.Threading.CheckIfClearAndSetBool(ref _isDisposed))
{
return;
}
_infoProvider.Dispose();
_infoProvider = null;
}
private int _isDisposed;
public bool IsDisposed
{
get => ModUtils.Threading.GetBool(ref _isDisposed);
private set => ModUtils.Threading.SetBool(ref _isDisposed, value);
}
}