OBT/1.2.0(Spring Update)
Sync with Upstream
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.Serialization;
|
||||
using Barotrauma.LuaCs;
|
||||
using Barotrauma.Steam;
|
||||
using OneOf;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
#region ModConfigurationInfo
|
||||
|
||||
public partial record ModConfigInfo : IModConfigInfo
|
||||
{
|
||||
public ContentPackage Package { get; init; }
|
||||
public ImmutableArray<IAssemblyResourceInfo> Assemblies { get; init; }
|
||||
public ImmutableArray<ILuaScriptResourceInfo> LuaScripts { get; init; }
|
||||
public ImmutableArray<IConfigResourceInfo> Configs { get; init; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DataContracts_Resources
|
||||
|
||||
public record BaseResourceInfo : IBaseResourceInfo
|
||||
{
|
||||
public Platform SupportedPlatforms { get; init; }
|
||||
public Target SupportedTargets { get; init; }
|
||||
public int LoadPriority { get; init; }
|
||||
public ImmutableArray<ContentPath> FilePaths { get; init; }
|
||||
public bool Optional { get; init; }
|
||||
public string InternalName { get; init; }
|
||||
public ContentPackage OwnerPackage { get; init; }
|
||||
public ImmutableArray<Identifier> RequiredPackages { get; init; }
|
||||
public ImmutableArray<Identifier> IncompatiblePackages { get; init; }
|
||||
}
|
||||
|
||||
public record AssemblyResourceInfo : BaseResourceInfo, IAssemblyResourceInfo
|
||||
{
|
||||
public string FriendlyName { get; init; }
|
||||
public bool IsScript { get; init; }
|
||||
public bool UseInternalAccessName { get; init; }
|
||||
public bool IsReferenceModeOnly { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Note: Config settings and settings-profiles are stored in the same files.
|
||||
/// </summary>
|
||||
public record ConfigResourceInfo : BaseResourceInfo, IConfigResourceInfo {}
|
||||
|
||||
public record LuaScriptsResourceInfo : BaseResourceInfo, ILuaScriptResourceInfo
|
||||
{
|
||||
public bool IsAutorun { get; init; }
|
||||
public bool RunUnrestricted { get; init; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DataContracts_ParsedInfo
|
||||
|
||||
public record ConfigInfo : IConfigInfo
|
||||
{
|
||||
public string InternalName { get; init; }
|
||||
public ContentPackage OwnerPackage { get; init; }
|
||||
public string DataType { get; init; }
|
||||
public XElement Element { get; init; }
|
||||
public RunState EditableStates { get; init; }
|
||||
public NetSync NetSync { get; init; }
|
||||
|
||||
#if CLIENT // IConfigDisplayInfo
|
||||
public string DisplayName { get; init; }
|
||||
public string Description { get; init; }
|
||||
public string DisplayCategory { get; init; }
|
||||
public bool ShowInMenus { get; init; }
|
||||
public string Tooltip { get; init; }
|
||||
public ContentPath ImageIconPath { get; init; }
|
||||
#endif
|
||||
}
|
||||
|
||||
public record ConfigProfileInfo : IConfigProfileInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Profile name.
|
||||
/// </summary>
|
||||
public string InternalName { get; init; }
|
||||
public ContentPackage OwnerPackage { get; init; }
|
||||
public IReadOnlyList<(string SettingName, XElement Element)> ProfileValues { get; init; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
[Flags]
|
||||
public enum Platform
|
||||
{
|
||||
Linux = 0x1,
|
||||
OSX = 0x2,
|
||||
Windows = 0x4,
|
||||
Any = Linux | OSX | Windows
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum Target
|
||||
{
|
||||
Client = 0x1,
|
||||
Server = 0x2,
|
||||
Any = Client | Server
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public interface IDependencyInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// List of dependency packages required by this resource.
|
||||
/// </summary>
|
||||
ImmutableArray<Identifier> RequiredPackages { get; }
|
||||
/// <summary>
|
||||
/// List of packages incompatible with this resource.
|
||||
/// </summary>
|
||||
ImmutableArray<Identifier> IncompatiblePackages { get; }
|
||||
}
|
||||
|
||||
public interface IPlatformInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Platforms that these localization files should be loaded for.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[XmlAttribute("Platform")]
|
||||
Platform SupportedPlatforms { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Targets that these localization files should be loaded for.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[XmlAttribute("Target")]
|
||||
Target SupportedTargets { get; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ResourceInfos contain metadata about a resource.
|
||||
/// </summary>
|
||||
public interface IResourceInfo : IPlatformInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// [Optional]
|
||||
/// Specifies the loading order for all assets of the same type (ie. styles, assemblies, etc.) from
|
||||
/// the same <see cref="ContentPackage"/>. Lower number is higher priority, see <see cref="System.Linq.Enumerable.OrderBy{TSource,TKey}(IEnumerable{TSource}, Func{TSource,TKey})"/>
|
||||
/// </summary>
|
||||
[XmlAttribute("LoadPriority")]
|
||||
int LoadPriority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Resource absolute file paths.
|
||||
/// </summary>
|
||||
[Required]
|
||||
ImmutableArray<ContentPath> FilePaths { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Marks this resource as optional (ie. Cross-CP content). Setting this to true will allow the dependency system to
|
||||
/// try and order the loading but not fail if it runs into circular dependency issues.
|
||||
/// </summary>
|
||||
[XmlAttribute("Optional")]
|
||||
bool Optional { get; }
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.LuaCs;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Parsed data from a configuration xml.
|
||||
/// </summary>
|
||||
public partial interface IConfigInfo : IDataInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the type initializer that will be used to instantiate the config var.
|
||||
/// </summary>
|
||||
string DataType { get; }
|
||||
/// <summary>
|
||||
/// The 'Setting' XML element.
|
||||
/// </summary>
|
||||
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.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; }
|
||||
/// <summary>
|
||||
/// Network synchronization rules for this config.
|
||||
/// </summary>
|
||||
NetSync NetSync { get; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public interface IConfigProfileInfo : IDataInfo
|
||||
{
|
||||
IReadOnlyList<(string SettingName, XElement Element)> ProfileValues { get; }
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Serves as a compound-key to refer to all resources and information that comes from a specific source.
|
||||
/// </summary>
|
||||
public interface IDataInfo : IEqualityComparer<IDataInfo>, IEquatable<IDataInfo>
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal name unique within the resources inside a package.
|
||||
/// </summary>
|
||||
[XmlAttribute("Name")]
|
||||
string InternalName { get; }
|
||||
/// <summary>
|
||||
/// The package this information belongs to.
|
||||
/// </summary>
|
||||
ContentPackage OwnerPackage { get; }
|
||||
|
||||
bool IEqualityComparer<IDataInfo>.Equals(IDataInfo x, IDataInfo y)
|
||||
{
|
||||
if (x is null || y is null)
|
||||
return false;
|
||||
if (x.OwnerPackage is null)
|
||||
throw new NullReferenceException($"ContentPackage not set for resource {x}!");
|
||||
if (y.OwnerPackage is null)
|
||||
throw new NullReferenceException($"ContentPackage not set for resource {y}!");
|
||||
if (x.InternalName.IsNullOrWhiteSpace())
|
||||
throw new NullReferenceException($"InternalName not set for resource {x}!");
|
||||
if (y.InternalName.IsNullOrWhiteSpace())
|
||||
throw new NullReferenceException($"InternalName not set for resource {y}!");
|
||||
return x.OwnerPackage == y.OwnerPackage && x.InternalName == y.InternalName;
|
||||
}
|
||||
|
||||
bool IEquatable<IDataInfo>.Equals(IDataInfo other)
|
||||
{
|
||||
return Equals(this, other);
|
||||
}
|
||||
|
||||
int IEqualityComparer<IDataInfo>.GetHashCode(IDataInfo obj)
|
||||
{
|
||||
if (obj.OwnerPackage is null)
|
||||
throw new NullReferenceException($"ContentPackage not set for resource {obj}!");
|
||||
if (obj.InternalName.IsNullOrWhiteSpace())
|
||||
throw new NullReferenceException($"InternalName is null for object {obj}!");
|
||||
return obj.InternalName.GetHashCode() + obj.OwnerPackage.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public partial interface IModConfigInfo : IAssembliesResourcesInfo,
|
||||
ILuaScriptsResourcesInfo, IConfigsResourcesInfo
|
||||
{
|
||||
// package info
|
||||
ContentPackage Package { get; }
|
||||
}
|
||||
|
||||
public record ResourceParserInfo(
|
||||
[NotNull] ContentPackage Owner,
|
||||
[NotNull] XElement Element,
|
||||
ImmutableArray<Identifier> Required,
|
||||
ImmutableArray<Identifier> Incompatible);
|
||||
@@ -0,0 +1,79 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
|
||||
public interface IBaseResourceInfo : IResourceInfo, IDataInfo, IDependencyInfo {}
|
||||
|
||||
public interface IConfigResourceInfo : IBaseResourceInfo {}
|
||||
|
||||
/// <summary>
|
||||
/// Represents loadable Lua files.
|
||||
/// </summary>
|
||||
public interface ILuaScriptResourceInfo : IBaseResourceInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Should this script be run automatically.
|
||||
/// </summary>
|
||||
[XmlAttribute("IsAutorun")]
|
||||
public bool IsAutorun { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that this lua resources needs to run outside sandbox/requires unrestricted access.
|
||||
/// </summary>
|
||||
[XmlAttribute("RunUnrestricted")]
|
||||
public bool RunUnrestricted { get; }
|
||||
}
|
||||
|
||||
public interface IAssemblyResourceInfo : IBaseResourceInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The friendly name of the assembly. Script files belonging to the same assembly should all have the same name.
|
||||
/// Legacy scripts will all be given the sanitized name of the Content Package they belong to.
|
||||
/// </summary>
|
||||
[XmlAttribute("FriendlyName")]
|
||||
public string FriendlyName { get; }
|
||||
/// <summary>
|
||||
/// Is this entry referring to a script file collection.
|
||||
/// </summary>
|
||||
[XmlAttribute("IsScript")]
|
||||
public bool IsScript { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <b>[Required(IsScript: true)] Whether the internal compiled assembly name should be named to enabled use of the
|
||||
/// <see cref="InternalsVisibleToAttribute"/> attribute.</b>
|
||||
/// </summary>
|
||||
[XmlAttribute("UseInternalAccessName")]
|
||||
public bool UseInternalAccessName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the following resources only be used for Compilation MetadataReference.
|
||||
/// NOTE: Affects the entire package's assembly resources, meant for internal use only.
|
||||
/// </summary>
|
||||
[XmlAttribute("IsReferenceModeOnly")]
|
||||
public bool IsReferenceModeOnly { get; }
|
||||
}
|
||||
|
||||
|
||||
#region Collections
|
||||
|
||||
public interface IAssembliesResourcesInfo
|
||||
{
|
||||
ImmutableArray<IAssemblyResourceInfo> Assemblies { get; }
|
||||
}
|
||||
|
||||
public interface ILuaScriptsResourcesInfo
|
||||
{
|
||||
ImmutableArray<ILuaScriptResourceInfo> LuaScripts { get; }
|
||||
}
|
||||
|
||||
public interface IConfigsResourcesInfo
|
||||
{
|
||||
ImmutableArray<IConfigResourceInfo> Configs { get; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Legacy data contract for the old run configuration system. Should be deprecated
|
||||
/// once no longer needed.
|
||||
/// </summary>
|
||||
public interface IRunConfig
|
||||
{
|
||||
bool UseNonPublicizedAssemblies { get; }
|
||||
bool AutoGenerated { get; }
|
||||
bool UseInternalAssemblyName { get; }
|
||||
string Client { get; }
|
||||
string Server { get; }
|
||||
|
||||
bool IsForced();
|
||||
bool IsStandard();
|
||||
bool IsForcedOrStandard();
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.LuaCs;
|
||||
using Barotrauma.Networking;
|
||||
using OneOf;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public partial 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);
|
||||
}
|
||||
|
||||
IConfigInfo GetConfigInfo();
|
||||
#if CLIENT
|
||||
IConfigDisplayInfo GetDisplayInfo();
|
||||
#endif
|
||||
bool IsDisposed { get; }
|
||||
Type GetValueType();
|
||||
string GetStringValue();
|
||||
string GetDefaultStringValue();
|
||||
bool TrySetSerializedValue(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
|
||||
{
|
||||
bool TrySetValueByIndex(int index);
|
||||
IReadOnlyList<T> Options { get; }
|
||||
IReadOnlyList<string> StringOptions { get; }
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.AccessControl;
|
||||
using Barotrauma.LuaCs;
|
||||
using Barotrauma.Networking;
|
||||
using FluentResults;
|
||||
using OneOf.Types;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
|
||||
// --- Storage Service
|
||||
// TODO: Configs should not be services, add new registration path for them.
|
||||
public interface IStorageServiceConfig : IService
|
||||
{
|
||||
string LocalModsDirectory { get; }
|
||||
string WorkshopModsDirectory { get; }
|
||||
string GameSettingsConfigPath { get; }
|
||||
#if CLIENT
|
||||
string TempDownloadsDirectory { get; }
|
||||
#endif
|
||||
string LocalDataSavePath { get; }
|
||||
string LocalDataPathRegex { get; }
|
||||
string LocalPackageDataPath { get; }
|
||||
}
|
||||
|
||||
public record StorageServiceConfig : IStorageServiceConfig
|
||||
{
|
||||
private static readonly string ExecutionLocation = Directory.GetCurrentDirectory().CleanUpPathCrossPlatform();
|
||||
|
||||
public string LocalModsDirectory { get; init; } = System.IO.Path.GetFullPath(ContentPackage.LocalModsDir).CleanUpPath();
|
||||
public string WorkshopModsDirectory { get; init; } = System.IO.Path.GetFullPath(ContentPackage.WorkshopModsDir).CleanUpPath();
|
||||
public string GameSettingsConfigPath { get; init; } = System.IO.Path.GetFullPath(
|
||||
string.IsNullOrEmpty(GameSettings.CurrentConfig.SavePath)
|
||||
? SaveUtil.DefaultSaveFolder
|
||||
: GameSettings.CurrentConfig.SavePath).CleanUpPath();
|
||||
#if CLIENT
|
||||
public string TempDownloadsDirectory { get; init; } = System.IO.Path.GetFullPath(ModReceiver.DownloadFolder).CleanUpPath();
|
||||
#endif
|
||||
public string LocalDataSavePath => Path.Combine(ExecutionLocation, "Data/Mods").CleanUpPathCrossPlatform();
|
||||
public string LocalDataPathRegex => "%ModDir%";
|
||||
public string RunLocation => ExecutionLocation;
|
||||
|
||||
public string LocalPackageDataPath => Path.Combine(LocalDataSavePath, LocalDataPathRegex);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// cannot be disposed.
|
||||
}
|
||||
|
||||
public bool IsDisposed => false;
|
||||
}
|
||||
|
||||
// --- Config Service
|
||||
public interface IConfigServiceConfig : IService
|
||||
{
|
||||
string LocalConfigPathPartial { get; }
|
||||
string FileNamePattern { get; }
|
||||
}
|
||||
|
||||
public record ConfigServiceConfig : IConfigServiceConfig
|
||||
{
|
||||
public string LocalConfigPathPartial => $"/Config/{FileNamePattern}.xml";
|
||||
public string FileNamePattern => "<ConfigName>";
|
||||
public void Dispose()
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
public bool IsDisposed => false;
|
||||
}
|
||||
|
||||
|
||||
// --- Lua Scripts Service
|
||||
public interface ILuaScriptServicesConfig : IService
|
||||
{
|
||||
bool SafeLuaIOEnabled { get; }
|
||||
bool UseCaching { get; }
|
||||
}
|
||||
|
||||
public record LuaScriptServicesConfig : ILuaScriptServicesConfig
|
||||
{
|
||||
public bool SafeLuaIOEnabled => true;
|
||||
public bool UseCaching => true;
|
||||
public void Dispose()
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
public bool IsDisposed => false;
|
||||
}
|
||||
|
||||
// --- Package Management Service
|
||||
public interface IPackageManagementServiceConfig : IService
|
||||
{
|
||||
bool IsCsEnabled { get; }
|
||||
}
|
||||
|
||||
public class PackageManagementServiceConfig : IPackageManagementServiceConfig
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
public bool IsDisposed => false;
|
||||
public bool IsCsEnabled => true;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using OneOf;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public abstract class SettingBase : ISettingBase
|
||||
{
|
||||
protected SettingBase(IConfigInfo configInfo)
|
||||
{
|
||||
Guard.IsNotNull(configInfo, nameof(configInfo));
|
||||
ConfigInfo = configInfo;
|
||||
}
|
||||
|
||||
protected IConfigInfo ConfigInfo { get; private set; }
|
||||
|
||||
public string InternalName => ConfigInfo.InternalName;
|
||||
public ContentPackage OwnerPackage => ConfigInfo.OwnerPackage;
|
||||
|
||||
public IConfigInfo GetConfigInfo() => ConfigInfo;
|
||||
#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;
|
||||
public virtual bool IsDisposed
|
||||
{
|
||||
get => ModUtils.Threading.GetBool(ref _isDisposed);
|
||||
private set => ModUtils.Threading.SetBool(ref _isDisposed, value);
|
||||
}
|
||||
|
||||
protected abstract void OnDispose();
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (!ModUtils.Threading.CheckIfClearAndSetBool(ref _isDisposed))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnDispose();
|
||||
ConfigInfo = null;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// -- Must be implemented
|
||||
|
||||
public abstract Type GetValueType();
|
||||
public abstract string GetStringValue();
|
||||
public abstract string GetDefaultStringValue();
|
||||
public abstract bool TrySetSerializedValue(OneOf<string, XElement> value);
|
||||
|
||||
public abstract event Action<ISettingBase> OnValueChanged;
|
||||
public abstract OneOf<string, XElement> GetSerializableValue();
|
||||
#if CLIENT
|
||||
public virtual void AddDisplayComponent(GUILayoutGroup layoutGroup, Vector2 relativeSize, Action<string> onSerializedValue)
|
||||
{
|
||||
new GUITextBox(new RectTransform(relativeSize, layoutGroup.RectTransform), font: GUIStyle.SmallFont)
|
||||
{
|
||||
Text = GetStringValue(),
|
||||
OnTextChangedDelegate = (box, txt) =>
|
||||
{
|
||||
onSerializedValue?.Invoke(txt);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
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 Microsoft.Xna.Framework;
|
||||
using OneOf;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public partial class SettingEntry<T> : SettingBase, ISettingBase<T>, INetworkSyncVar 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));
|
||||
DefaultValue = Value;
|
||||
}
|
||||
catch (Exception e) when (e is InvalidCastException or ArgumentNullException)
|
||||
{
|
||||
Value = default(T);
|
||||
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 || value.Equals(Value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#if CLIENT
|
||||
if (SyncType is NetSync.ServerAuthority && NetworkingService is not null
|
||||
&& GameMain.IsMultiplayer
|
||||
&& GameMain.Client is not null
|
||||
&& !GameMain.Client.HasPermission(this.WritePermissions))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!TrySetValueInternal(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
OnValueChanged?.Invoke(this);
|
||||
#if CLIENT
|
||||
if (GameMain.IsMultiplayer && SyncType is NetSync.ClientOneWay or NetSync.TwoWay)
|
||||
{
|
||||
NetworkingService?.SendNetVar(this);
|
||||
}
|
||||
#elif SERVER
|
||||
if (SyncType is NetSync.TwoWay or NetSync.ServerAuthority)
|
||||
{
|
||||
NetworkingService?.SendNetVar(this);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TrySetValueInternal(T value)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ValueChangePredicate != null && !ValueChangePredicate(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Value = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// handles internal networking rules after reading the net message (to avoid synchro issues).
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
private bool TrySetValueNetwork(T value)
|
||||
{
|
||||
if (NetworkingService is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#if CLIENT
|
||||
if (SyncType is NetSync.None or NetSync.ClientOneWay)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (SyncType is NetSync.None or NetSync.ServerAuthority)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
if (!TrySetValueInternal(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
if (SyncType is NetSync.TwoWay)
|
||||
{
|
||||
NetworkingService?.SendNetVar(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
OnValueChanged?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnDispose()
|
||||
{
|
||||
ValueChangePredicate = null;
|
||||
NetworkingService?.DeregisterNetVar(this);
|
||||
}
|
||||
|
||||
public override Type GetValueType() => typeof(T);
|
||||
public override string GetStringValue() => Value?.ToString() ?? string.Empty;
|
||||
public override string GetDefaultStringValue() => DefaultValue?.ToString() ?? string.Empty;
|
||||
|
||||
public override bool TrySetSerializedValue(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 event Action<ISettingBase> OnValueChanged;
|
||||
|
||||
public override OneOf<string, XElement> GetSerializableValue() => Value.ToString();
|
||||
|
||||
// -- Networking
|
||||
protected IEntityNetworkingService NetworkingService;
|
||||
public Guid InstanceId => NetworkingService?.GetNetworkIdForInstance(this) ?? Guid.Empty;
|
||||
public void SetNetworkOwner(IEntityNetworkingService networkingService)
|
||||
{
|
||||
NetworkingService = networkingService;
|
||||
}
|
||||
|
||||
public NetSync SyncType => ConfigInfo?.NetSync ?? NetSync.None;
|
||||
// needs to be added IConfigInfo
|
||||
public ClientPermissions WritePermissions => ClientPermissions.ManageSettings;
|
||||
|
||||
public void ReadNetMessage(IReadMessage message)
|
||||
{
|
||||
if (SyncType == NetSync.None || NetworkingService is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (typeof(T).IsEnum)
|
||||
{
|
||||
TrySetValueInternal((T)(object)message.ReadInt32());
|
||||
}
|
||||
|
||||
// No...there's no better way to do this...
|
||||
var typeCode = Type.GetTypeCode(typeof(T));
|
||||
switch (typeCode)
|
||||
{
|
||||
case TypeCode.Boolean:
|
||||
TrySetValueNetwork((T)Convert.ChangeType(message.ReadBoolean(), typeCode));
|
||||
return;
|
||||
case TypeCode.Byte:
|
||||
TrySetValueNetwork((T)Convert.ChangeType(message.ReadByte(), typeCode));
|
||||
return;
|
||||
// SByte not supported by interface
|
||||
case TypeCode.SByte:
|
||||
TrySetValueNetwork((T)Convert.ChangeType(message.ReadInt16(), typeCode));
|
||||
return;
|
||||
case TypeCode.Int16:
|
||||
TrySetValueNetwork((T)Convert.ChangeType(message.ReadInt16(), typeCode));
|
||||
return;
|
||||
case TypeCode.Char:
|
||||
case TypeCode.UInt16:
|
||||
TrySetValueNetwork((T)Convert.ChangeType(message.ReadUInt16(), typeCode));
|
||||
return;
|
||||
case TypeCode.Int32:
|
||||
TrySetValueNetwork((T)Convert.ChangeType(message.ReadInt32(), typeCode));
|
||||
return;
|
||||
case TypeCode.UInt32:
|
||||
TrySetValueNetwork((T)Convert.ChangeType(message.ReadUInt32(), typeCode));
|
||||
return;
|
||||
case TypeCode.Int64:
|
||||
TrySetValueNetwork((T)Convert.ChangeType(message.ReadInt64(), typeCode));
|
||||
return;
|
||||
case TypeCode.UInt64:
|
||||
TrySetValueNetwork((T)Convert.ChangeType(message.ReadUInt64(), typeCode));
|
||||
return;
|
||||
case TypeCode.Single:
|
||||
TrySetValueNetwork((T)Convert.ChangeType(message.ReadSingle(), typeCode));
|
||||
return;
|
||||
case TypeCode.Double:
|
||||
TrySetValueNetwork((T)Convert.ChangeType(message.ReadDouble(), typeCode));
|
||||
return;
|
||||
case TypeCode.String:
|
||||
TrySetValueNetwork((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
|
||||
}
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
public override void AddDisplayComponent(GUILayoutGroup layoutGroup, Vector2 relativeSize, Action<string> onSerializedValue)
|
||||
{
|
||||
switch (Type.GetTypeCode(typeof(T)))
|
||||
{
|
||||
case TypeCode.Boolean:
|
||||
new GUITickBox(new RectTransform(relativeSize, layoutGroup.RectTransform), "")
|
||||
{
|
||||
Selected = (bool)Convert.ChangeType(this.Value, TypeCode.Boolean),
|
||||
OnSelected = (box) =>
|
||||
{
|
||||
onSerializedValue?.Invoke(box.Selected.ToString());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
break;
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.Char:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.UInt32:
|
||||
case TypeCode.Int64:
|
||||
case TypeCode.UInt64:
|
||||
new GUINumberInput(new RectTransform(relativeSize, layoutGroup.RectTransform), NumberType.Int)
|
||||
{
|
||||
IntValue = (int)Convert.ChangeType(this.Value, TypeCode.Int32)!,
|
||||
OnValueChanged = (num) =>
|
||||
{
|
||||
onSerializedValue?.Invoke(num.IntValue.ToString());
|
||||
}
|
||||
};
|
||||
break;
|
||||
case TypeCode.Single:
|
||||
case TypeCode.Double:
|
||||
new GUINumberInput(new RectTransform(relativeSize, layoutGroup.RectTransform), NumberType.Float)
|
||||
{
|
||||
FloatValue = (float)Convert.ChangeType(this.Value, TypeCode.Single)!,
|
||||
OnValueChanged = (num) =>
|
||||
{
|
||||
onSerializedValue?.Invoke(num.FloatValue.ToString());
|
||||
}
|
||||
};
|
||||
break;
|
||||
case TypeCode.String:
|
||||
default:
|
||||
base.AddDisplayComponent(layoutGroup, relativeSize, onSerializedValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public class SettingList<T> : SettingEntry<T>, ISettingList<T> where T : IEquatable<T>, IConvertible
|
||||
{
|
||||
public class LFactory : ISettingBase.IFactory<ISettingList<T>>
|
||||
{
|
||||
public ISettingList<T> CreateInstance(IConfigInfo configInfo, Func<OneOf<string, XElement, object>, bool> valueChangePredicate)
|
||||
{
|
||||
Guard.IsNotNull(configInfo, nameof(configInfo));
|
||||
return new SettingList<T>(configInfo, valueChangePredicate);
|
||||
}
|
||||
}
|
||||
|
||||
public SettingList(IConfigInfo configInfo, Func<OneOf<string, XElement, object>, bool> valueChangePredicate) : base(configInfo, valueChangePredicate)
|
||||
{
|
||||
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;
|
||||
|
||||
var valuesElements = ConfigInfo.Element.GetChildElement("Values")?.GetChildElements("Value")?.ToImmutableArray();
|
||||
|
||||
Guard.IsNotNull(valuesElements, this.InternalName);
|
||||
if (valuesElements.Value.IsEmpty)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullException($"{this.InternalName}: Could not find any values in list!");
|
||||
}
|
||||
|
||||
foreach (var element in valuesElements.Value)
|
||||
{
|
||||
if (!TryConvert(element, out var v1))
|
||||
{
|
||||
ThrowHelper.ThrowArgumentException($"{this.InternalName}: Error while parsing list values");
|
||||
}
|
||||
_valuesList.Add(v1);
|
||||
}
|
||||
|
||||
if (TryConvert(ConfigInfo.Element, out var v) && _valuesList.Contains(v))
|
||||
{
|
||||
Value = v;
|
||||
DefaultValue = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
Value = _valuesList[0];
|
||||
DefaultValue = _valuesList[0];
|
||||
}
|
||||
|
||||
|
||||
bool TryConvert(XElement element, out T value)
|
||||
{
|
||||
try
|
||||
{
|
||||
value = (T)Convert.ChangeType(element.GetAttributeString("Value", null), typeof(T));
|
||||
return true;
|
||||
}
|
||||
catch (Exception e) when (e is InvalidCastException or ArgumentNullException)
|
||||
{
|
||||
value = default(T);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<T> _valuesList = new();
|
||||
|
||||
public override bool TrySetValue(T value)
|
||||
{
|
||||
if (!_valuesList.Contains(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.TrySetValue(value);
|
||||
}
|
||||
|
||||
public bool TrySetValueByIndex(int index)
|
||||
{
|
||||
if (_valuesList.Count <= index)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.TrySetValue(_valuesList[index]);
|
||||
}
|
||||
|
||||
public IReadOnlyList<T> Options => _valuesList.AsReadOnly();
|
||||
|
||||
public IReadOnlyList<string> StringOptions => _valuesList.Select(e => e.ToString()).ToImmutableArray();
|
||||
|
||||
#if CLIENT
|
||||
public override void AddDisplayComponent(GUILayoutGroup layoutGroup, Vector2 relativeSize, Action<string> onSerializedValue)
|
||||
{
|
||||
GUIUtil.Dropdown(layoutGroup, (T val) => GetLocalizedString(val.ToString(), val.ToString()), null, Options, Value, (T val) =>
|
||||
{
|
||||
onSerializedValue?.Invoke(val.ToString());
|
||||
}, new Vector2(relativeSize.X, 1f));
|
||||
|
||||
string GetLocalizedString(string identifier, string defaultValue)
|
||||
{
|
||||
var lstr = TextManager.Get($"{XmlConvert.EncodeLocalName(OwnerPackage.Name)}.{InternalName}.{identifier}.DisplayName");
|
||||
return lstr.IsNullOrWhiteSpace() ? defaultValue : lstr.Value;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Microsoft.Xna.Framework;
|
||||
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);
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
public override void AddDisplayComponent(GUILayoutGroup layoutGroup, Vector2 relativeSize, Action<string> onSerializedValue)
|
||||
{
|
||||
GUIUtil.Slider(layoutGroup, new Vector2(MinValue, MaxValue), IncrementalSteps, labelFunc: val =>
|
||||
{
|
||||
return val.ToString("G4", CultureInfo.InvariantCulture);
|
||||
}, Value, setter: val =>
|
||||
{
|
||||
onSerializedValue?.Invoke(val.ToString());
|
||||
}, TextManager.Get(this.GetDisplayInfo().Tooltip), relativeSize);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
public override void AddDisplayComponent(GUILayoutGroup layoutGroup, Vector2 relativeSize, Action<string> onSerializedValue)
|
||||
{
|
||||
GUIUtil.Slider(layoutGroup, new Vector2(MinValue, MaxValue), IncrementalSteps, labelFunc: val =>
|
||||
{
|
||||
return ((int)val).ToString();
|
||||
}, Value, setter: val =>
|
||||
{
|
||||
onSerializedValue?.Invoke(((int)val).ToString());
|
||||
}, TextManager.Get(this.GetDisplayInfo().Tooltip), relativeSize);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.LuaCs;
|
||||
using OneOf;
|
||||
|
||||
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)
|
||||
{
|
||||
RegisterSettingEntry<bool>(configService, "bool", valueChangePredicate);
|
||||
RegisterSettingEntry<byte>(configService, "byte", valueChangePredicate);
|
||||
RegisterSettingEntry<sbyte>(configService, "sbyte", valueChangePredicate);
|
||||
RegisterSettingEntry<short>(configService, "short", valueChangePredicate);
|
||||
RegisterSettingEntry<ushort>(configService, "ushort", valueChangePredicate);
|
||||
RegisterSettingEntry<int>(configService, "int", valueChangePredicate);
|
||||
RegisterSettingEntry<uint>(configService, "uint", valueChangePredicate);
|
||||
RegisterSettingEntry<long>(configService, "long", valueChangePredicate);
|
||||
RegisterSettingEntry<ulong>(configService, "ulong", valueChangePredicate);
|
||||
RegisterSettingEntry<string>(configService, "string", valueChangePredicate);
|
||||
RegisterSettingEntry<float>(configService, "float", valueChangePredicate);
|
||||
RegisterSettingEntry<float>(configService, "single", valueChangePredicate);
|
||||
RegisterSettingEntry<double>(configService, "double", valueChangePredicate);
|
||||
|
||||
// ISettingRangeBase<T>
|
||||
configService.RegisterSettingTypeInitializer("rangeInt", cfgInfo =>
|
||||
{
|
||||
return new SettingRangeInt.RangeFactory().CreateInstance(cfgInfo.Info, (val) =>
|
||||
IsValueChangeAllowed(cfgInfo.Info, val, valueChangePredicate));
|
||||
});
|
||||
|
||||
configService.RegisterSettingTypeInitializer("rangeFloat", cfgInfo =>
|
||||
{
|
||||
return new SettingRangeFloat.RangeFactory().CreateInstance(cfgInfo.Info, (val) =>
|
||||
IsValueChangeAllowed(cfgInfo.Info, val, valueChangePredicate));
|
||||
});
|
||||
|
||||
#if CLIENT
|
||||
configService.RegisterSettingTypeInitializer("control" , cfgInfo =>
|
||||
{
|
||||
return new SettingControl.Factory().CreateInstance(cfgInfo.Info, val =>
|
||||
IsValueChangeAllowed(cfgInfo.Info, val, valueChangePredicate));
|
||||
});
|
||||
#endif
|
||||
|
||||
RegisterSettingList<bool>(configService, "listBool", valueChangePredicate);
|
||||
RegisterSettingList<byte>(configService, "listByte", valueChangePredicate);
|
||||
RegisterSettingList<sbyte>(configService, "listSbyte", valueChangePredicate);
|
||||
RegisterSettingList<short>(configService, "listShort", valueChangePredicate);
|
||||
RegisterSettingList<ushort>(configService, "listUshort", valueChangePredicate);
|
||||
RegisterSettingList<int>(configService, "listInt", valueChangePredicate);
|
||||
RegisterSettingList<uint>(configService, "listUint", valueChangePredicate);
|
||||
RegisterSettingList<long>(configService, "listLong", valueChangePredicate);
|
||||
RegisterSettingList<ulong>(configService, "listUlong", valueChangePredicate);
|
||||
RegisterSettingList<string>(configService, "listString", valueChangePredicate);
|
||||
RegisterSettingList<float>(configService, "listFloat", valueChangePredicate);
|
||||
RegisterSettingList<float>(configService, "listSingle", valueChangePredicate);
|
||||
RegisterSettingList<double>(configService, "listDouble", valueChangePredicate);
|
||||
}
|
||||
|
||||
private void RegisterSettingList<T>(IConfigService configService, string typeName, Func<OneOf<string, XElement, object>, bool> valueChangePredicate) where T : IEquatable<T>, IConvertible
|
||||
{
|
||||
configService.RegisterSettingTypeInitializer(typeName, cfgInfo =>
|
||||
{
|
||||
return new SettingList<T>.LFactory().CreateInstance(cfgInfo.Info, (val) =>
|
||||
IsValueChangeAllowed(cfgInfo.Info, val, valueChangePredicate));
|
||||
});
|
||||
}
|
||||
|
||||
private void RegisterSettingEntry<T>(IConfigService configService, string typeName, Func<OneOf<string, XElement, object>, bool> valueChangePredicate) where T : IEquatable<T>, IConvertible
|
||||
{
|
||||
configService.RegisterSettingTypeInitializer(typeName, cfgInfo =>
|
||||
{
|
||||
return new SettingEntry<T>.Factory().CreateInstance(cfgInfo.Info, (val) =>
|
||||
IsValueChangeAllowed(cfgInfo.Info, val, valueChangePredicate));
|
||||
});
|
||||
}
|
||||
|
||||
private bool IsValueChangeAllowed(IConfigInfo info, OneOf<string, XElement, object> newValue,
|
||||
Func<OneOf<string, XElement, object>, bool> valueChangePredicate)
|
||||
{
|
||||
#if CLIENT
|
||||
return !info.Element.GetAttributeBool("ReadOnly", false)
|
||||
|| info.EditableStates < _infoProvider.CurrentRunState
|
||||
|| valueChangePredicate is null
|
||||
|| valueChangePredicate.Invoke(newValue);
|
||||
#else
|
||||
// Server has absolute authority.
|
||||
return !info.Element.GetAttributeBool("ReadOnly", false);
|
||||
#endif
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user