IT BUILDS!!!
- Removed LocalizationServices and other sus things. - Rewrote AssemblyLoader [In Progress] SafeStorageService [In Progress] LuaScriptLoader
This commit is contained in:
@@ -0,0 +1,142 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Barotrauma.LuaCs.Data;
|
||||||
|
|
||||||
|
namespace Barotrauma.LuaCs.Configuration;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base type of all menu displayable types.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDisplayableConfigBase : IDataInfo, IConfigDisplayInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the current config is editable.
|
||||||
|
/// </summary>
|
||||||
|
bool IsEditable { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// Used to indicate the implemented interface and targeted display logic.
|
||||||
|
/// </summary>
|
||||||
|
static virtual DisplayType DisplayOption => DisplayType.Undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IDisplayableConfigBase<out TDisplay, in TValue> : IDisplayableConfigBase
|
||||||
|
{
|
||||||
|
void SetValue(TValue value);
|
||||||
|
TDisplay GetDisplayValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IDisplayableConfigBool : IDisplayableConfigBase<bool, bool>
|
||||||
|
{
|
||||||
|
static DisplayType IDisplayableConfigBase.DisplayOption => DisplayType.Boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IDisplayableConfigText : IDisplayableConfigBase<string, string>
|
||||||
|
{
|
||||||
|
static DisplayType IDisplayableConfigBase.DisplayOption => DisplayType.Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IDisplayableConfigInt : IDisplayableConfigBase<int, int>
|
||||||
|
{
|
||||||
|
static DisplayType IDisplayableConfigBase.DisplayOption => DisplayType.Integer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IDisplayableConfigFloat : IDisplayableConfigBase<float, float>
|
||||||
|
{
|
||||||
|
static DisplayType IDisplayableConfigBase.DisplayOption => DisplayType.Float;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IDisplayableConfigSliderInt : IDisplayableConfigBase<(int Min, int Max, int Value, int Steps), int>
|
||||||
|
{
|
||||||
|
static DisplayType IDisplayableConfigBase.DisplayOption => DisplayType.SliderInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IDisplayableConfigSliderFloat : IDisplayableConfigBase<(float Min, float Max, float Value, int Steps), float>
|
||||||
|
{
|
||||||
|
static DisplayType IDisplayableConfigBase.DisplayOption => DisplayType.SliderFloat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IDisplayableConfigDropdown : IDisplayableConfigBase<List<string>, string>
|
||||||
|
{
|
||||||
|
static DisplayType IDisplayableConfigBase.DisplayOption => DisplayType.Dropdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows completely custom-designed UI for this configuration component.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDisplayableConfigCustom : IDisplayableConfigBase
|
||||||
|
{
|
||||||
|
static DisplayType IDisplayableConfigBase.DisplayOption => DisplayType.Custom;
|
||||||
|
/// <summary>
|
||||||
|
/// Draw your menu settings option.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="layoutGroup">Parent layout component.</param>
|
||||||
|
void DrawComponent(GUILayoutGroup layoutGroup);
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the config element is set to be disposed to allow for cleanup.
|
||||||
|
/// </summary>
|
||||||
|
void DisposeGUI();
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the UI indicates to save the current value as permanent.
|
||||||
|
/// </summary>
|
||||||
|
void OnValueSaved();
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the UI indicates to discard the currently displayed value and revert to the last saved value.
|
||||||
|
/// </summary>
|
||||||
|
void OnValueDiscarded();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates the intended display and feedback logic to be used by the <see cref="SettingsMenu"/>.
|
||||||
|
/// <br/><b>[Important]</b>
|
||||||
|
/// <br/>The type must implement the indicated interface for the selected option, or it will not be displayed.
|
||||||
|
/// </summary>
|
||||||
|
public enum DisplayType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Will not be displayed in menus.
|
||||||
|
/// </summary>
|
||||||
|
Undefined,
|
||||||
|
/// <summary>
|
||||||
|
/// Will be shown as a checkbox.
|
||||||
|
/// <br/><b>[Requires(<see cref="IDisplayableConfigBool"/>)]</b>
|
||||||
|
/// </summary>
|
||||||
|
Boolean,
|
||||||
|
/// <summary>
|
||||||
|
/// Shown as an editable text input.
|
||||||
|
/// <br/><b>[Requires(<see cref="IDisplayableConfigText"/>)]</b>
|
||||||
|
/// </summary>
|
||||||
|
Text,
|
||||||
|
/// <summary>
|
||||||
|
/// Shown as number input (no decimal input).
|
||||||
|
/// <br/><b>[Requires(<see cref="IDisplayableConfigInt"/>)]</b>
|
||||||
|
/// </summary>
|
||||||
|
Integer,
|
||||||
|
/// <summary>
|
||||||
|
/// Shown as a number input.
|
||||||
|
/// <br/><b>[Requires(<see cref="IDisplayableConfigFloat"/>)]</b>
|
||||||
|
/// </summary>
|
||||||
|
Float,
|
||||||
|
/// <summary>
|
||||||
|
/// Shown as a slider, values parsed as integers.
|
||||||
|
/// <br/><b>[Requires(<see cref="IDisplayableConfigSliderInt"/>)]</b>
|
||||||
|
/// </summary>
|
||||||
|
SliderInt,
|
||||||
|
/// <summary>
|
||||||
|
/// Shown as a slider, values parsed as single-precision decimal numbers.
|
||||||
|
/// <br/><b>[Requires(<see cref="IDisplayableConfigSliderFloat"/>)]</b>
|
||||||
|
/// </summary>
|
||||||
|
SliderFloat,
|
||||||
|
/// <summary>
|
||||||
|
/// Shown as a <see cref="GUIDropDown"/> menu, values parsed as strings.
|
||||||
|
/// <br/><b>[Requires(<see cref="IDisplayableConfigDropdown"/>)]</b>
|
||||||
|
/// </summary>
|
||||||
|
Dropdown,
|
||||||
|
/// <summary>
|
||||||
|
/// UI Display is implemented by inheritor and actioned by a call to <see cref="IDisplayableConfigCustom.DrawComponent"/>.
|
||||||
|
/// <br/><b>[Requires(<see cref="IDisplayableConfigCustom"/>)]</b>
|
||||||
|
/// </summary>
|
||||||
|
Custom
|
||||||
|
}
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
using System.Collections.Immutable;
|
|
||||||
using System.Globalization;
|
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Data;
|
|
||||||
|
|
||||||
public partial record ModConfigInfo : IModConfigInfo
|
|
||||||
{
|
|
||||||
public ImmutableArray<IStylesResourceInfo> Styles { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public record StylesResourceInfo : IStylesResourceInfo
|
|
||||||
{
|
|
||||||
public Platform SupportedPlatforms { get; init; }
|
|
||||||
public Target SupportedTargets { get; init; }
|
|
||||||
public int LoadPriority { get; init; }
|
|
||||||
public ImmutableArray<string> FilePaths { get; init; }
|
|
||||||
public bool Optional { get; init; }
|
|
||||||
public ImmutableArray<CultureInfo> SupportedCultures { get; init; }
|
|
||||||
public string InternalName { get; init; }
|
|
||||||
public ContentPackage OwnerPackage { get; init; }
|
|
||||||
public string FallbackPackageName { get; init; }
|
|
||||||
public ImmutableArray<IPackageDependency> Dependencies { get; init; }
|
|
||||||
}
|
|
||||||
@@ -2,8 +2,22 @@ using Barotrauma.LuaCs.Configuration;
|
|||||||
|
|
||||||
namespace Barotrauma.LuaCs.Data;
|
namespace Barotrauma.LuaCs.Data;
|
||||||
|
|
||||||
public partial interface IConfigInfo
|
public partial interface IConfigInfo : IConfigDisplayInfo { }
|
||||||
|
|
||||||
|
public interface IConfigDisplayInfo
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// User-friendly name or Localization Token.
|
||||||
|
/// </summary>
|
||||||
|
string DisplayName { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// User-friendly description or Localization Token.
|
||||||
|
/// </summary>
|
||||||
|
string Description { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// The menu category to display under. Used for filtering.
|
||||||
|
/// </summary>
|
||||||
|
string DisplayCategory { get; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Should this config be displayed in end-user menus.
|
/// Should this config be displayed in end-user menus.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
using System.Collections.Immutable;
|
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Data;
|
|
||||||
|
|
||||||
public partial interface IModConfigInfo : IStylesResourcesInfo { }
|
|
||||||
|
|
||||||
public interface IStylesResourceInfo : IResourceInfo, IResourceCultureInfo, IDataInfo, IPackageDependenciesInfo { }
|
|
||||||
|
|
||||||
public interface IStylesResourcesInfo
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Collection of loadable styles data.
|
|
||||||
/// </summary>
|
|
||||||
ImmutableArray<IStylesResourceInfo> Styles { get; }
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Barotrauma.LuaCs.Data;
|
|
||||||
|
|
||||||
public interface IStylesInfo
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -23,9 +23,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private partial bool ShouldRunCs() => IsCsEnabled.Value;
|
private partial bool ShouldRunCs() => IsCsEnabled.Value;
|
||||||
|
|
||||||
public IStylesManagementService StylesManagementService => _servicesProvider.TryGetService<IStylesManagementService>(out var svc)
|
|
||||||
? svc : throw new NullReferenceException("Networking Manager service not found!");
|
|
||||||
|
|
||||||
public void CheckCsEnabled()
|
public void CheckCsEnabled()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using Barotrauma.LuaCs.Configuration;
|
||||||
|
using Barotrauma.LuaCs.Data;
|
||||||
|
using Barotrauma.Networking;
|
||||||
|
using FluentResults;
|
||||||
|
|
||||||
|
namespace Barotrauma.LuaCs.Services;
|
||||||
|
|
||||||
|
public partial class ConfigService
|
||||||
|
{
|
||||||
|
public ImmutableArray<IDisplayableConfigBase> GetDisplayableConfigs()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableArray<IDisplayableConfigBase> GetDisplayableConfigsForPackage(ContentPackage package)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<IConfigControl> AddConfigControl(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
using Barotrauma.LuaCs.Configuration;
|
using Barotrauma.LuaCs.Configuration;
|
||||||
|
using Barotrauma.LuaCs.Data;
|
||||||
using Barotrauma.LuaCs.Services;
|
using Barotrauma.LuaCs.Services;
|
||||||
using Barotrauma.Networking;
|
using Barotrauma.Networking;
|
||||||
|
|
||||||
@@ -8,28 +10,8 @@ namespace Barotrauma.LuaCs.Services;
|
|||||||
|
|
||||||
public partial interface IConfigService
|
public partial interface IConfigService
|
||||||
{
|
{
|
||||||
/*
|
ImmutableArray<IDisplayableConfigBase> GetDisplayableConfigs();
|
||||||
* Immediate mode
|
ImmutableArray<IDisplayableConfigBase> GetDisplayableConfigsForPackage(ContentPackage package);
|
||||||
*/
|
|
||||||
FluentResults.Result<IConfigEntry<T>> AddConfigEntry<T>(IDisplayableData data,
|
|
||||||
T defaultValue,
|
|
||||||
NetSync syncMode = NetSync.None,
|
|
||||||
ClientPermissions permissions = ClientPermissions.None,
|
|
||||||
Func<T, bool> valueChangePredicate = null,
|
|
||||||
Action<IConfigEntry<T>> onValueChanged = null) where T : IConvertible, IEquatable<T>;
|
|
||||||
|
|
||||||
FluentResults.Result<IConfigList> AddConfigList(IDisplayableData data,
|
FluentResults.Result<IConfigControl> AddConfigControl(IConfigInfo configInfo);
|
||||||
int defaultIndex, IReadOnlyList<string> values,
|
|
||||||
NetSync syncMode = NetSync.None,
|
|
||||||
ClientPermissions permissions = ClientPermissions.None,
|
|
||||||
Func<IConfigList, int, bool> valueChangePredicate = null,
|
|
||||||
Action<IConfigList, int> onValueChanged = null);
|
|
||||||
|
|
||||||
FluentResults.Result<IConfigRangeEntry<T>> AddConfigRangeEntry<T>(IDisplayableData data,
|
|
||||||
T defaultValue, T minValue, T maxValue,
|
|
||||||
Func<IConfigRangeEntry<T>, int> getStepCount,
|
|
||||||
NetSync syncMode = NetSync.None,
|
|
||||||
ClientPermissions permissions = ClientPermissions.None,
|
|
||||||
Func<T, bool> valueChangePredicate = null,
|
|
||||||
Action<IConfigEntry<T>> onValueChanged = null) where T : IConvertible, IEquatable<T>;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
using System.Collections.Immutable;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Barotrauma.LuaCs.Data;
|
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Services;
|
|
||||||
|
|
||||||
public interface IStylesManagementService : IReusableService
|
|
||||||
{
|
|
||||||
Task<FluentResults.Result> LoadStylesAsync(ImmutableArray<IStylesResourceInfo> styles);
|
|
||||||
FluentResults.Result<IStylesService> GetStylesService(ContentPackage package);
|
|
||||||
Task<FluentResults.Result> DisposeAllStyles();
|
|
||||||
Task<FluentResults.Result> DisposeStylesForPackage(ContentPackage package);
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Services;
|
|
||||||
|
|
||||||
// TODO: Rework interface to support resource infos.
|
|
||||||
/// <summary>
|
|
||||||
/// Loads XML Style assets from the given content package.
|
|
||||||
/// </summary>
|
|
||||||
public interface IStylesService : IService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to load the styles file for the given <see cref="ContentPackage"/> and path into a new <see cref="UIStyleProcessor"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="package"></param>
|
|
||||||
/// <param name="path"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<FluentResults.Result> LoadStylesFileAsync(ContentPackage package, ContentPath path);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unloads all styles assets and <see cref="UIStyleProcessor"/> instances.
|
|
||||||
/// </summary>
|
|
||||||
FluentResults.Result UnloadAllStyles();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to the get the <see cref="GUIFont"/> asset by xml asset name, returns null on failure.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="fontName">XML Name of the asset.</param>
|
|
||||||
/// <returns>The asset or null if none are found.</returns>
|
|
||||||
GUIFont GetFont(string fontName);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to the get the <see cref="GUISprite"/> asset by xml asset name, returns null on failure.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="spriteName">XML Name of the asset.</param>
|
|
||||||
/// <returns>The asset or null if none are found.</returns>
|
|
||||||
GUISprite GetSprite(string spriteName);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to the get the <see cref="GUISpriteSheet"/> asset by xml asset name, returns null on failure.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="spriteSheetName">XML Name of the asset.</param>
|
|
||||||
/// <returns>The asset or null if none are found.</returns>
|
|
||||||
GUISpriteSheet GetSpriteSheet(string spriteSheetName);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to the get the <see cref="GUICursor"/> asset by xml asset name, returns null on failure.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cursorName">XML Name of the asset.</param>
|
|
||||||
/// <returns>The asset or null if none are found.</returns>
|
|
||||||
GUICursor GetCursor(string cursorName);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to the get the <see cref="GUIColor"/> asset by xml asset name, returns null on failure.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="colorName">XML Name of the asset.</param>
|
|
||||||
/// <returns>The asset or null if none are found.</returns>
|
|
||||||
GUIColor GetColor(string colorName);
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
using Barotrauma.LuaCs.Services;
|
using Barotrauma.LuaCs.Services;
|
||||||
using Barotrauma.Networking;
|
using Barotrauma.Networking;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Services;
|
namespace Barotrauma.LuaCs.Services;
|
||||||
|
|
||||||
partial class NetworkingService : INetworkingService
|
partial class NetworkingService : INetworkingService
|
||||||
{
|
{
|
||||||
private Dictionary<ushort, Queue<IReadMessage>> receiveQueue = new Dictionary<ushort, Queue<IReadMessage>>();
|
private ConcurrentDictionary<ushort, ConcurrentQueue<IReadMessage>> receiveQueue = new();
|
||||||
|
|
||||||
public void SendSyncMessage()
|
public void SendSyncMessage()
|
||||||
{
|
{
|
||||||
@@ -99,7 +100,7 @@ partial class NetworkingService : INetworkingService
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!receiveQueue.ContainsKey(id)) { receiveQueue[id] = new Queue<IReadMessage>(); }
|
if (!receiveQueue.ContainsKey(id)) { receiveQueue[id] = new ConcurrentQueue<IReadMessage>(); }
|
||||||
receiveQueue[id].Enqueue(netMessage);
|
receiveQueue[id].Enqueue(netMessage);
|
||||||
|
|
||||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Immutable;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Barotrauma.LuaCs.Data;
|
|
||||||
using Barotrauma.LuaCs.Services.Processing;
|
|
||||||
using FluentResults;
|
|
||||||
// ReSharper disable UseCollectionExpression
|
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Services;
|
|
||||||
|
|
||||||
public partial class PackageManagementService : IPackageManagementService
|
|
||||||
{
|
|
||||||
public PackageManagementService(
|
|
||||||
IConverterServiceAsync<ContentPackage, IModConfigInfo> modConfigParserService,
|
|
||||||
IProcessorService<IReadOnlyList<IAssemblyResourceInfo>, IAssembliesResourcesInfo> assemblyInfoConverter,
|
|
||||||
IProcessorService<IReadOnlyList<IConfigResourceInfo>, IConfigsResourcesInfo> configsInfoConverter,
|
|
||||||
IProcessorService<IReadOnlyList<IConfigProfileResourceInfo>, IConfigProfilesResourcesInfo> configProfilesConverter,
|
|
||||||
IProcessorService<IReadOnlyList<ILocalizationResourceInfo>, ILocalizationsResourcesInfo> localizationsConverter,
|
|
||||||
IProcessorService<IReadOnlyList<ILuaScriptResourceInfo>, ILuaScriptsResourcesInfo> luaScriptsConverter,
|
|
||||||
IPackageInfoLookupService packageInfoLookupService, Func<IReadOnlyList<IStylesResourceInfo>, IStylesResourcesInfo> stylesInfoConverter)
|
|
||||||
{
|
|
||||||
_stylesInfoConverter = stylesInfoConverter;
|
|
||||||
_modConfigParserService = modConfigParserService;
|
|
||||||
_assemblyInfoConverter = assemblyInfoConverter;
|
|
||||||
_configsInfoConverter = configsInfoConverter;
|
|
||||||
_configProfilesConverter = configProfilesConverter;
|
|
||||||
_localizationsConverter = localizationsConverter;
|
|
||||||
_luaScriptsConverter = luaScriptsConverter;
|
|
||||||
_packageInfoLookupService = packageInfoLookupService;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly Func<IReadOnlyList<IStylesResourceInfo>, IStylesResourcesInfo> _stylesInfoConverter;
|
|
||||||
|
|
||||||
public ImmutableArray<IStylesResourceInfo> Styles => _modInfos.IsEmpty ? ImmutableArray<IStylesResourceInfo>.Empty
|
|
||||||
: _modInfos.SelectMany(kvp => kvp.Value.Styles).ToImmutableArray();
|
|
||||||
|
|
||||||
public Result<IStylesResourcesInfo> GetStylesInfos(ContentPackage package, bool onlySupportedResources = true)
|
|
||||||
{
|
|
||||||
((IService)this).CheckDisposed();
|
|
||||||
if (package is null)
|
|
||||||
return FluentResults.Result.Fail($"{nameof(GetStylesInfos)}: ContentPackage is null.");
|
|
||||||
if (_modInfos.TryGetValue(package, out var result))
|
|
||||||
return FluentResults.Result.Ok<IStylesResourcesInfo>(_stylesInfoConverter(onlySupportedResources?
|
|
||||||
result.Styles.Where(r =>
|
|
||||||
(r.SupportedPlatforms & ModUtils.Environment.CurrentPlatform) > 0
|
|
||||||
&& (r.SupportedTargets & ModUtils.Environment.CurrentTarget) > 0).ToImmutableArray()
|
|
||||||
: result.Styles
|
|
||||||
));
|
|
||||||
return FluentResults.Result.Fail(
|
|
||||||
$"{nameof(GetStylesInfos)}: ContentPackage {package.Name} is not registered.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result<IStylesResourcesInfo> GetStylesInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true)
|
|
||||||
{
|
|
||||||
((IService)this).CheckDisposed();
|
|
||||||
if (packages is null || packages.Count == 0)
|
|
||||||
return FluentResults.Result.Fail($"{nameof(GetStylesInfos)}: ContentPackage list is null or empty.");
|
|
||||||
var builder = ImmutableArray.CreateBuilder<IStylesResourceInfo>();
|
|
||||||
foreach (var package in packages)
|
|
||||||
{
|
|
||||||
if (_modInfos.TryGetValue(package, out var result) && result.Styles is { IsEmpty: false })
|
|
||||||
{
|
|
||||||
builder.AddRange(onlySupportedResources?
|
|
||||||
result.Styles.Where(r =>
|
|
||||||
(r.SupportedPlatforms & ModUtils.Environment.CurrentPlatform) > 0
|
|
||||||
&& (r.SupportedTargets & ModUtils.Environment.CurrentTarget) > 0).ToImmutableArray()
|
|
||||||
: result.Styles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return FluentResults.Result.Ok(_stylesInfoConverter(builder.MoveToImmutable()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Result<IStylesResourcesInfo>> GetStylesInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true)
|
|
||||||
{
|
|
||||||
return await Task.Run(() => GetStylesInfos(packages, onlySupportedResources));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-10
@@ -14,26 +14,17 @@ public partial class ModConfigService
|
|||||||
private partial async Task<Result<IModConfigInfo>> GetModConfigInfoAsync(ContentPackage package, XElement root)
|
private partial async Task<Result<IModConfigInfo>> GetModConfigInfoAsync(ContentPackage package, XElement root)
|
||||||
{
|
{
|
||||||
var asm = root.GetChildElements("Assembly").ToImmutableArray();
|
var asm = root.GetChildElements("Assembly").ToImmutableArray();
|
||||||
var loc = root.GetChildElements("Localization").ToImmutableArray();
|
|
||||||
var cfg = root.GetChildElements("Config").ToImmutableArray();
|
var cfg = root.GetChildElements("Config").ToImmutableArray();
|
||||||
var lua = root.GetChildElements("Lua").ToImmutableArray();
|
var lua = root.GetChildElements("Lua").ToImmutableArray();
|
||||||
var stl = root.GetChildElements("Style").ToImmutableArray();
|
|
||||||
|
|
||||||
return FluentResults.Result.Ok<IModConfigInfo>(new ModConfigInfo()
|
return FluentResults.Result.Ok<IModConfigInfo>(new ModConfigInfo()
|
||||||
{
|
{
|
||||||
Package = package,
|
Package = package,
|
||||||
PackageName = package.Name,
|
PackageName = package.Name,
|
||||||
Assemblies = asm.Any() ? GetAssemblies(package, asm) : ImmutableArray<IAssemblyResourceInfo>.Empty,
|
Assemblies = asm.Any() ? GetAssemblies(package, asm) : ImmutableArray<IAssemblyResourceInfo>.Empty,
|
||||||
Localizations = loc.Any() ? GetLocalizations(package, loc) : ImmutableArray<ILocalizationResourceInfo>.Empty,
|
|
||||||
Configs = cfg.Any() ? GetConfigs(package, cfg) : ImmutableArray<IConfigResourceInfo>.Empty,
|
Configs = cfg.Any() ? GetConfigs(package, cfg) : ImmutableArray<IConfigResourceInfo>.Empty,
|
||||||
ConfigProfiles = cfg.Any() ? GetConfigProfiles(package, cfg) : ImmutableArray<IConfigProfileResourceInfo>.Empty,
|
ConfigProfiles = cfg.Any() ? GetConfigProfiles(package, cfg) : ImmutableArray<IConfigProfileResourceInfo>.Empty,
|
||||||
LuaScripts = lua.Any() ? GetLuaScripts(package, lua) : ImmutableArray<ILuaScriptResourceInfo>.Empty,
|
LuaScripts = lua.Any() ? GetLuaScripts(package, lua) : ImmutableArray<ILuaScriptResourceInfo>.Empty
|
||||||
Styles = stl.Any() ? GetStyles(package, stl) : ImmutableArray<IStylesResourceInfo>.Empty
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImmutableArray<IStylesResourceInfo> GetStyles(ContentPackage src, IEnumerable<XElement> elements)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
-6
@@ -1,6 +0,0 @@
|
|||||||
namespace Barotrauma.LuaCs.Services.Processing;
|
|
||||||
|
|
||||||
public class StylesManagementService : IStylesManagementService
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Xml.Linq;
|
|
||||||
using FluentResults;
|
|
||||||
using FluentResults.LuaCs;
|
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Services;
|
|
||||||
|
|
||||||
// TODO: Complete rewrite
|
|
||||||
public class StylesService : IStylesService
|
|
||||||
{
|
|
||||||
private readonly ConcurrentDictionary<string, UIStyleProcessor> _loadedProcessors = new();
|
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly ILoggerService _loggerService;
|
|
||||||
|
|
||||||
public StylesService(IStorageService storageService, ILoggerService loggerService)
|
|
||||||
{
|
|
||||||
_storageService = storageService;
|
|
||||||
_loggerService = loggerService;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public async Task<FluentResults.Result> LoadStylesFileAsync(ContentPackage package, ContentPath path)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public FluentResults.Result UnloadAllStyles()
|
|
||||||
{
|
|
||||||
if (NoProcessorsLoaded)
|
|
||||||
return FluentResults.Result.Fail(new Error($"{nameof(StylesService)}.{nameof(UnloadAllStyles)}: No processors have been loaded.")
|
|
||||||
.WithMetadata(MetadataType.ExceptionObject, this));
|
|
||||||
|
|
||||||
foreach (var processor in _loadedProcessors)
|
|
||||||
{
|
|
||||||
processor.Value.UnloadFile();
|
|
||||||
}
|
|
||||||
_loadedProcessors.Clear();
|
|
||||||
return FluentResults.Result.Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
public GUIFont GetFont(string fontName)
|
|
||||||
{
|
|
||||||
if (NoProcessorsLoaded)
|
|
||||||
return null;
|
|
||||||
foreach (var processor in _loadedProcessors.Values)
|
|
||||||
{
|
|
||||||
if (processor.Fonts.TryGetValue(fontName, out var asset))
|
|
||||||
return asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GUISprite GetSprite(string spriteName)
|
|
||||||
{
|
|
||||||
if (NoProcessorsLoaded)
|
|
||||||
return null;
|
|
||||||
foreach (var processor in _loadedProcessors.Values)
|
|
||||||
{
|
|
||||||
if (processor.Sprites.TryGetValue(spriteName, out var asset))
|
|
||||||
return asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GUISpriteSheet GetSpriteSheet(string spriteSheetName)
|
|
||||||
{
|
|
||||||
if (NoProcessorsLoaded)
|
|
||||||
return null;
|
|
||||||
foreach (var processor in _loadedProcessors.Values)
|
|
||||||
{
|
|
||||||
if (processor.SpriteSheets.TryGetValue(spriteSheetName, out var asset))
|
|
||||||
return asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GUICursor GetCursor(string cursorName)
|
|
||||||
{
|
|
||||||
if (NoProcessorsLoaded)
|
|
||||||
return null;
|
|
||||||
foreach (var processor in _loadedProcessors.Values)
|
|
||||||
{
|
|
||||||
if (processor.Cursors.TryGetValue(cursorName, out var asset))
|
|
||||||
return asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GUIColor GetColor(string colorName)
|
|
||||||
{
|
|
||||||
if (NoProcessorsLoaded)
|
|
||||||
return null;
|
|
||||||
foreach (var processor in _loadedProcessors.Values)
|
|
||||||
{
|
|
||||||
if (processor.Colors.TryGetValue(colorName, out var asset))
|
|
||||||
return asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool NoProcessorsLoaded => _loadedProcessors.IsEmpty;
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
UnloadAllStyles();
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsDisposed { get; private set; }
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Xml.Linq;
|
|
||||||
using Barotrauma.Extensions;
|
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Services;
|
|
||||||
|
|
||||||
public class UIStyleProcessor : HashlessFile
|
|
||||||
{
|
|
||||||
private readonly UIStyleFile _fake;
|
|
||||||
public readonly Dictionary<string, GUIFont> Fonts = new();
|
|
||||||
public readonly Dictionary<string, GUISprite> Sprites = new();
|
|
||||||
public readonly Dictionary<string, GUISpriteSheet> SpriteSheets = new();
|
|
||||||
public readonly Dictionary<string, GUICursor> Cursors = new();
|
|
||||||
public readonly Dictionary<string, GUIColor> Colors = new();
|
|
||||||
|
|
||||||
public UIStyleProcessor(ContentPackage contentPackage, ContentPath path) : base(contentPackage, path)
|
|
||||||
{
|
|
||||||
_fake = new UIStyleFile(contentPackage, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void LoadFile()
|
|
||||||
{
|
|
||||||
var element = XMLExtensions.TryLoadXml(path: Path)?.Root?.FromPackage(ContentPackage);
|
|
||||||
if (element is null)
|
|
||||||
throw new InvalidDataException($"UIStyleProcessor: Failed to load UI style file: {Path}");
|
|
||||||
|
|
||||||
var styleElement = element.Name.LocalName.ToLowerInvariant() == "style" ? element : element.GetChildElement("style");
|
|
||||||
if (styleElement is null)
|
|
||||||
throw new InvalidDataException($"UIStyleProcessor: no 'style' XmlElement found in file: {Path}");
|
|
||||||
|
|
||||||
var childElements = styleElement.GetChildElements("Font");
|
|
||||||
if (childElements is not null)
|
|
||||||
AddToList<GUIFont, GUIFontPrefab>(Fonts, childElements, _fake);
|
|
||||||
|
|
||||||
childElements = styleElement.GetChildElements("Sprite");
|
|
||||||
if (childElements is not null)
|
|
||||||
AddToList<GUISprite, GUISpritePrefab>(Sprites, childElements, _fake);
|
|
||||||
|
|
||||||
childElements = styleElement.GetChildElements("Spritesheet");
|
|
||||||
if (childElements is not null)
|
|
||||||
AddToList<GUISpriteSheet, GUISpriteSheetPrefab>(SpriteSheets, childElements, _fake);
|
|
||||||
|
|
||||||
childElements = styleElement.GetChildElements("Cursor");
|
|
||||||
if (childElements is not null)
|
|
||||||
AddToList<GUICursor, GUICursorPrefab>(Cursors, childElements, _fake);
|
|
||||||
|
|
||||||
childElements = styleElement.GetChildElements("Color");
|
|
||||||
if (childElements is not null)
|
|
||||||
AddToList<GUIColor, GUIColorPrefab>(Colors, childElements, _fake);
|
|
||||||
|
|
||||||
|
|
||||||
void AddToList<T1, T2>(Dictionary<string, T1> dict, IEnumerable<ContentXElement> ele, UIStyleFile file) where T1 : GUISelector<T2> where T2 : GUIPrefab
|
|
||||||
{
|
|
||||||
foreach (ContentXElement prefabElement in ele)
|
|
||||||
{
|
|
||||||
string name = prefabElement.GetAttributeString("name", string.Empty);
|
|
||||||
if (name != string.Empty)
|
|
||||||
{
|
|
||||||
var prefab = (T2)Activator.CreateInstance(typeof(T2), new object[]{ prefabElement, file })!;
|
|
||||||
if (!dict.ContainsKey(name))
|
|
||||||
dict[name] = (T1)Activator.CreateInstance(typeof(T1), new object[] { name })!;
|
|
||||||
dict[name].Prefabs.Add(prefab, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UnloadFile()
|
|
||||||
{
|
|
||||||
Fonts.Values.ForEach(p => p.Prefabs.RemoveByFile(_fake));
|
|
||||||
Sprites.Values.ForEach(p => p.Prefabs.RemoveByFile(_fake));
|
|
||||||
SpriteSheets.Values.ForEach(p => p.Prefabs.RemoveByFile(_fake));
|
|
||||||
Cursors.Values.ForEach(p => p.Prefabs.RemoveByFile(_fake));
|
|
||||||
Colors.Values.ForEach(p => p.Prefabs.RemoveByFile(_fake));
|
|
||||||
|
|
||||||
Fonts.Clear();
|
|
||||||
Sprites.Clear();
|
|
||||||
SpriteSheets.Clear();
|
|
||||||
Cursors.Clear();
|
|
||||||
Colors.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Sort()
|
|
||||||
{
|
|
||||||
Fonts.Values.ForEach(p => p.Prefabs.Sort());
|
|
||||||
Sprites.Values.ForEach(p => p.Prefabs.Sort());
|
|
||||||
SpriteSheets.Values.ForEach(p => p.Prefabs.Sort());
|
|
||||||
Cursors.Values.ForEach(p => p.Prefabs.Sort());
|
|
||||||
Colors.Values.ForEach(p => p.Prefabs.Sort());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,6 +12,7 @@ using System.Globalization;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
using Barotrauma.LuaCs.Events;
|
||||||
using Barotrauma.Sounds;
|
using Barotrauma.Sounds;
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
@@ -1532,7 +1533,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
Select(enableAutoSave: true);
|
Select(enableAutoSave: true);
|
||||||
|
|
||||||
GameMain.LuaCs.CheckInitialize();
|
GameMain.LuaCs.EventService.PublishEvent<IEventScreenSelected>(sub => sub.OnScreenSelected(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Select(bool enableAutoSave = true)
|
public void Select(bool enableAutoSave = true)
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ public partial class PackageManagementService
|
|||||||
IProcessorService<IReadOnlyList<IAssemblyResourceInfo>, IAssembliesResourcesInfo> assemblyInfoConverter,
|
IProcessorService<IReadOnlyList<IAssemblyResourceInfo>, IAssembliesResourcesInfo> assemblyInfoConverter,
|
||||||
IProcessorService<IReadOnlyList<IConfigResourceInfo>, IConfigsResourcesInfo> configsInfoConverter,
|
IProcessorService<IReadOnlyList<IConfigResourceInfo>, IConfigsResourcesInfo> configsInfoConverter,
|
||||||
IProcessorService<IReadOnlyList<IConfigProfileResourceInfo>, IConfigProfilesResourcesInfo> configProfilesConverter,
|
IProcessorService<IReadOnlyList<IConfigProfileResourceInfo>, IConfigProfilesResourcesInfo> configProfilesConverter,
|
||||||
IProcessorService<IReadOnlyList<ILocalizationResourceInfo>, ILocalizationsResourcesInfo> localizationsConverter,
|
|
||||||
IProcessorService<IReadOnlyList<ILuaScriptResourceInfo>, ILuaScriptsResourcesInfo> luaScriptsConverter,
|
IProcessorService<IReadOnlyList<ILuaScriptResourceInfo>, ILuaScriptsResourcesInfo> luaScriptsConverter,
|
||||||
IPackageInfoLookupService packageInfoLookupService)
|
IPackageInfoLookupService packageInfoLookupService)
|
||||||
{
|
{
|
||||||
@@ -19,7 +18,6 @@ public partial class PackageManagementService
|
|||||||
_assemblyInfoConverter = assemblyInfoConverter;
|
_assemblyInfoConverter = assemblyInfoConverter;
|
||||||
_configsInfoConverter = configsInfoConverter;
|
_configsInfoConverter = configsInfoConverter;
|
||||||
_configProfilesConverter = configProfilesConverter;
|
_configProfilesConverter = configProfilesConverter;
|
||||||
_localizationsConverter = localizationsConverter;
|
|
||||||
_luaScriptsConverter = luaScriptsConverter;
|
_luaScriptsConverter = luaScriptsConverter;
|
||||||
_packageInfoLookupService = packageInfoLookupService;
|
_packageInfoLookupService = packageInfoLookupService;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ public partial class ModConfigService
|
|||||||
private partial async Task<Result<IModConfigInfo>> GetModConfigInfoAsync(ContentPackage package, XElement root)
|
private partial async Task<Result<IModConfigInfo>> GetModConfigInfoAsync(ContentPackage package, XElement root)
|
||||||
{
|
{
|
||||||
var asm = root.GetChildElements("Assembly").ToImmutableArray();
|
var asm = root.GetChildElements("Assembly").ToImmutableArray();
|
||||||
var loc = root.GetChildElements("Localization").ToImmutableArray();
|
|
||||||
var cfg = root.GetChildElements("Config").ToImmutableArray();
|
var cfg = root.GetChildElements("Config").ToImmutableArray();
|
||||||
var lua = root.GetChildElements("Lua").ToImmutableArray();
|
var lua = root.GetChildElements("Lua").ToImmutableArray();
|
||||||
|
|
||||||
@@ -24,7 +23,6 @@ public partial class ModConfigService
|
|||||||
Package = package,
|
Package = package,
|
||||||
PackageName = package.Name,
|
PackageName = package.Name,
|
||||||
Assemblies = asm.Any() ? GetAssemblies(package, asm) : ImmutableArray<IAssemblyResourceInfo>.Empty,
|
Assemblies = asm.Any() ? GetAssemblies(package, asm) : ImmutableArray<IAssemblyResourceInfo>.Empty,
|
||||||
Localizations = loc.Any() ? GetLocalizations(package, loc) : ImmutableArray<ILocalizationResourceInfo>.Empty,
|
|
||||||
Configs = cfg.Any() ? GetConfigs(package, cfg) : ImmutableArray<IConfigResourceInfo>.Empty,
|
Configs = cfg.Any() ? GetConfigs(package, cfg) : ImmutableArray<IConfigResourceInfo>.Empty,
|
||||||
ConfigProfiles = cfg.Any() ? GetConfigProfiles(package, cfg) : ImmutableArray<IConfigProfileResourceInfo>.Empty,
|
ConfigProfiles = cfg.Any() ? GetConfigProfiles(package, cfg) : ImmutableArray<IConfigProfileResourceInfo>.Empty,
|
||||||
LuaScripts = lua.Any() ? GetLuaScripts(package, lua) : ImmutableArray<ILuaScriptResourceInfo>.Empty
|
LuaScripts = lua.Any() ? GetLuaScripts(package, lua) : ImmutableArray<ILuaScriptResourceInfo>.Empty
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- For: DataType="typeof(Barotrauma.LuaCs.Data.IModConfig)"-->
|
||||||
|
<schema
|
||||||
|
xmlns="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns:xs="http://www.w3.org/2001/XMLSchema-datatypes"
|
||||||
|
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
|
||||||
|
targetNamespace="Barotrauma.LuaCs.Data"
|
||||||
|
vc:minVersion="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<!-- ATTRIBUTES -->
|
||||||
|
<!--Attribute "Folder" restrictions-->
|
||||||
|
<attribute name="Folder">
|
||||||
|
<simpleType>
|
||||||
|
<restriction base="string"/>
|
||||||
|
</simpleType>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<!--Attribute "File" restrictions-->
|
||||||
|
<attribute name="File">
|
||||||
|
<simpleType>
|
||||||
|
<restriction base="string"/>
|
||||||
|
</simpleType>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<!--Attribute "Platform" restrictions-->
|
||||||
|
<attribute name="Platform">
|
||||||
|
<simpleType>
|
||||||
|
<restriction base="string">
|
||||||
|
<pattern value="(Windows|Linux|OSX)"/>
|
||||||
|
</restriction>
|
||||||
|
</simpleType>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<!--Attribute "Target" restrictions-->
|
||||||
|
<attribute name="Target">
|
||||||
|
<simpleType>
|
||||||
|
<restriction base="string">
|
||||||
|
<pattern value="(Client|Server|Any)"/>
|
||||||
|
</restriction>
|
||||||
|
</simpleType>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<!--Attribute "Culture" restrictions-->
|
||||||
|
<attribute name="Culture">
|
||||||
|
<simpleType>
|
||||||
|
<restriction base="string">
|
||||||
|
<pattern value="^[a-z]{2,3}(?:-[A-Z]{2,3}(?:-[a-zA-Z]{4})?)?$"/>
|
||||||
|
</restriction>
|
||||||
|
</simpleType>
|
||||||
|
</attribute>
|
||||||
|
</schema>
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
@@ -1,20 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Xml.Linq;
|
||||||
using Barotrauma.LuaCs.Data;
|
using Barotrauma.LuaCs.Data;
|
||||||
|
using Barotrauma.LuaCs.Services;
|
||||||
using Barotrauma.Networking;
|
using Barotrauma.Networking;
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Configuration;
|
namespace Barotrauma.LuaCs.Configuration;
|
||||||
|
|
||||||
public partial interface IConfigBase : IVarId
|
public partial interface IConfigBase : IDataInfo, IEquatable<IConfigBase>, IDisposable
|
||||||
{
|
{
|
||||||
bool IsInitialized { get; }
|
|
||||||
string GetValue();
|
|
||||||
bool TrySetValue(string value);
|
|
||||||
bool IsAssignable(string value);
|
|
||||||
Type GetValueType();
|
Type GetValueType();
|
||||||
void Initialize(IVarId id, string defaultValue);
|
string GetValue();
|
||||||
}
|
bool TrySetValue(OneOf.OneOf<string, XElement> value);
|
||||||
|
bool IsAssignable(OneOf.OneOf<string, XElement> value);
|
||||||
public interface IVarId : IDataInfo
|
event Action<IConfigBase> OnValueChanged;
|
||||||
{
|
OneOf.OneOf<string, XElement> GetSerializableValue();
|
||||||
Guid InstanceId { get; }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ using Barotrauma.LuaCs.Services;
|
|||||||
|
|
||||||
namespace Barotrauma.LuaCs.Configuration;
|
namespace Barotrauma.LuaCs.Configuration;
|
||||||
|
|
||||||
public interface IConfigEntry<T> : IConfigBase, INetVar where T : IConvertible, IEquatable<T>
|
public interface IConfigEntry<T> : IConfigBase, INetworkSyncEntity where T : IEquatable<T>
|
||||||
{
|
{
|
||||||
T Value { get; }
|
T Value { get; }
|
||||||
bool TrySetValue(T value);
|
bool TrySetValue(T value);
|
||||||
bool IsAssignable(T value);
|
bool IsAssignable(T value);
|
||||||
void Initialize(IVarId id, T defaultValue);
|
new event Action<IConfigEntry<T>> OnValueChanged;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
using Barotrauma.LuaCs.Services;
|
||||||
|
|
||||||
|
namespace Barotrauma.LuaCs.Configuration;
|
||||||
|
|
||||||
|
public interface IConfigEnum : IConfigBase, INetworkSyncEntity
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Barotrauma.LuaCs.Services;
|
using Barotrauma.LuaCs.Services;
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Configuration;
|
namespace Barotrauma.LuaCs.Configuration;
|
||||||
|
|
||||||
public interface IConfigList : IConfigBase, INetVar
|
public interface IConfigList<T> : IConfigEntry<T>, INetworkSyncEntity where T : IEquatable<T>
|
||||||
{
|
{
|
||||||
|
IReadOnlyList<T> Options { get; }
|
||||||
|
new event Action<IConfigList<T>> OnValueChanged;
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-17
@@ -5,7 +5,10 @@ using System.Globalization;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using Barotrauma.LuaCs.Services;
|
||||||
using Barotrauma.Steam;
|
using Barotrauma.Steam;
|
||||||
|
using OneOf;
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Data;
|
namespace Barotrauma.LuaCs.Data;
|
||||||
|
|
||||||
@@ -16,7 +19,6 @@ public partial record ModConfigInfo : IModConfigInfo
|
|||||||
public ContentPackage Package { get; init; }
|
public ContentPackage Package { get; init; }
|
||||||
public string PackageName { get; init; }
|
public string PackageName { get; init; }
|
||||||
public ImmutableArray<IAssemblyResourceInfo> Assemblies { get; init; }
|
public ImmutableArray<IAssemblyResourceInfo> Assemblies { get; init; }
|
||||||
public ImmutableArray<ILocalizationResourceInfo> Localizations { get; init; }
|
|
||||||
public ImmutableArray<ILuaScriptResourceInfo> LuaScripts { get; init; }
|
public ImmutableArray<ILuaScriptResourceInfo> LuaScripts { get; init; }
|
||||||
public ImmutableArray<IConfigResourceInfo> Configs { get; init; }
|
public ImmutableArray<IConfigResourceInfo> Configs { get; init; }
|
||||||
public ImmutableArray<IConfigProfileResourceInfo> ConfigProfiles { get; init; }
|
public ImmutableArray<IConfigProfileResourceInfo> ConfigProfiles { get; init; }
|
||||||
@@ -24,10 +26,9 @@ public partial record ModConfigInfo : IModConfigInfo
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region DataContracts
|
#region DataContracts_Resources
|
||||||
|
|
||||||
public record AssemblyResourcesInfo(ImmutableArray<IAssemblyResourceInfo> Assemblies) : IAssembliesResourcesInfo;
|
public record AssemblyResourcesInfo(ImmutableArray<IAssemblyResourceInfo> Assemblies) : IAssembliesResourcesInfo;
|
||||||
public record LocalizationResourcesInfo(ImmutableArray<ILocalizationResourceInfo> Localizations) : ILocalizationsResourcesInfo;
|
|
||||||
public record LuaScriptsResourcesInfo(ImmutableArray<ILuaScriptResourceInfo> LuaScripts) : ILuaScriptsResourcesInfo;
|
public record LuaScriptsResourcesInfo(ImmutableArray<ILuaScriptResourceInfo> LuaScripts) : ILuaScriptsResourcesInfo;
|
||||||
public record ConfigResourcesInfo(ImmutableArray<IConfigResourceInfo> Configs) : IConfigsResourcesInfo;
|
public record ConfigResourcesInfo(ImmutableArray<IConfigResourceInfo> Configs) : IConfigsResourcesInfo;
|
||||||
public record ConfigProfilesResourcesInfo(ImmutableArray<IConfigProfileResourceInfo> ConfigProfiles) : IConfigProfilesResourcesInfo;
|
public record ConfigProfilesResourcesInfo(ImmutableArray<IConfigProfileResourceInfo> ConfigProfiles) : IConfigProfilesResourcesInfo;
|
||||||
@@ -161,20 +162,7 @@ public record ConfigProfileResourceInfo : IConfigProfileResourceInfo
|
|||||||
public ContentPackage OwnerPackage { get; init; }
|
public ContentPackage OwnerPackage { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public record LocalizationResourceInfo : ILocalizationResourceInfo
|
public readonly struct LuaScriptsResourceInfo : ILuaScriptResourceInfo
|
||||||
{
|
|
||||||
public string InternalName { get; init; }
|
|
||||||
public ContentPackage OwnerPackage { get; init; }
|
|
||||||
public Platform SupportedPlatforms { get; init; }
|
|
||||||
public Target SupportedTargets { get; init; }
|
|
||||||
public int LoadPriority { get; init; }
|
|
||||||
public ImmutableArray<string> FilePaths { get; init; }
|
|
||||||
public ImmutableArray<CultureInfo> SupportedCultures { get; init; }
|
|
||||||
public ImmutableArray<IPackageDependency> Dependencies { get; init; }
|
|
||||||
public bool Optional { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly struct LuaScriptScriptResourceInfo : ILuaScriptResourceInfo
|
|
||||||
{
|
{
|
||||||
public ContentPackage OwnerPackage { get; init; }
|
public ContentPackage OwnerPackage { get; init; }
|
||||||
public Platform SupportedPlatforms { get; init; }
|
public Platform SupportedPlatforms { get; init; }
|
||||||
@@ -189,3 +177,34 @@ public readonly struct LuaScriptScriptResourceInfo : ILuaScriptResourceInfo
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region DataContracts_ParsedInfo
|
||||||
|
|
||||||
|
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 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 string ImageIconPath { get; init; }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
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; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
@@ -1,51 +1,43 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Xml.Linq;
|
||||||
using Barotrauma.LuaCs.Services;
|
using Barotrauma.LuaCs.Services;
|
||||||
using Barotrauma.Networking;
|
using Barotrauma.Networking;
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Data;
|
namespace Barotrauma.LuaCs.Data;
|
||||||
|
|
||||||
// TODO: Finish
|
/// <summary>
|
||||||
|
/// Parsed data from a configuration xml.
|
||||||
|
/// </summary>
|
||||||
public partial interface IConfigInfo : IDataInfo
|
public partial interface IConfigInfo : IDataInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies the data type this should be initialized to (ie. string, int, vector, etc.)
|
/// Specifies the type initializer that will be used to instantiate the config var.
|
||||||
/// Custom types can be registered by mods.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Type DataType { get; }
|
Type DataType { get; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// String version of the default value.
|
/// The default value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string DefaultValue { get; }
|
OneOf.OneOf<string, XElement> DefaultValue { get; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The value the last time this config was saved, if found in /data/.
|
/// 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>
|
/// </summary>
|
||||||
string StoredValue { get; }
|
OneOf.OneOf<string, XElement> Value { get; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Custom data storage for other type-specific information needed. IE. Used to store the min,
|
/// In what <see cref="RunState"/>(s) is this config editable. Will be editable in the selected state, and lower value states.
|
||||||
/// max and step values for the <b>IConfigRangeEntry(T)</b>.
|
/// <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.Running"/>]</b>: Can be changed at the Main Menu and while a lobby is active.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string CustomParameters { get; }
|
RunState EditableStates { get; }
|
||||||
/// <summary>
|
|
||||||
/// <b>[Multiplayer]</b><br/>
|
|
||||||
/// What permissions do clients require to change this setting.
|
|
||||||
/// </summary>
|
|
||||||
ClientPermissions RequiredPermissions { get; }
|
|
||||||
/// <summary>
|
|
||||||
/// In what <see cref="RunState"/>s is this config editable.
|
|
||||||
/// <br/>
|
|
||||||
/// Note: Setting this to value lower than 'Configuration` will render this config read-only.
|
|
||||||
/// </summary>
|
|
||||||
RunState CanEditStates { get; }
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Network synchronization rules for this config.
|
/// Network synchronization rules for this config.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
NetSync NetSync { get; }
|
NetSync NetSync { get; }
|
||||||
/// <summary>
|
|
||||||
/// User friendly name or Localization Token.
|
|
||||||
/// </summary>
|
|
||||||
string DisplayName { get; }
|
|
||||||
/// <summary>
|
|
||||||
/// User friendly description or Localization Token.
|
|
||||||
/// </summary>
|
|
||||||
string Description { get; }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
namespace Barotrauma.LuaCs.Data;
|
using System.Collections.Generic;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
public interface IConfigProfileInfo
|
namespace Barotrauma.LuaCs.Data;
|
||||||
|
|
||||||
|
public interface IConfigProfileInfo : IDataInfo
|
||||||
{
|
{
|
||||||
|
IReadOnlyList<(string ConfigName, OneOf.OneOf<string, XElement> Value)> ProfileValues { get; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Data;
|
|
||||||
|
|
||||||
public interface ILocalizationInfo : IDataInfo
|
|
||||||
{
|
|
||||||
string Symbol { get; }
|
|
||||||
IReadOnlyDictionary<CultureInfo, RawLString> LocalizedValues { get; }
|
|
||||||
RawLString GetLocalizedString(CultureInfo locale);
|
|
||||||
RawLString GetLocalizedString(string cultureCode);
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
namespace Barotrauma.LuaCs.Data;
|
namespace Barotrauma.LuaCs.Data;
|
||||||
|
|
||||||
public partial interface IModConfigInfo : IAssembliesResourcesInfo,
|
public partial interface IModConfigInfo : IAssembliesResourcesInfo,
|
||||||
ILocalizationsResourcesInfo, ILuaScriptsResourcesInfo, IConfigsResourcesInfo,
|
ILuaScriptsResourcesInfo, IConfigsResourcesInfo,
|
||||||
IConfigProfilesResourcesInfo
|
IConfigProfilesResourcesInfo
|
||||||
{
|
{
|
||||||
// package info
|
// package info
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ namespace Barotrauma.LuaCs.Data;
|
|||||||
|
|
||||||
public interface IConfigResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IDataInfo { }
|
public interface IConfigResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IDataInfo { }
|
||||||
public interface IConfigProfileResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IDataInfo { }
|
public interface IConfigProfileResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IDataInfo { }
|
||||||
public interface ILocalizationResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IDataInfo { }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents loadable Lua files.
|
/// Represents loadable Lua files.
|
||||||
@@ -40,11 +39,6 @@ public interface IAssembliesResourcesInfo
|
|||||||
ImmutableArray<IAssemblyResourceInfo> Assemblies { get; }
|
ImmutableArray<IAssemblyResourceInfo> Assemblies { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ILocalizationsResourcesInfo
|
|
||||||
{
|
|
||||||
ImmutableArray<ILocalizationResourceInfo> Localizations { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ILuaScriptsResourcesInfo
|
public interface ILuaScriptsResourcesInfo
|
||||||
{
|
{
|
||||||
ImmutableArray<ILuaScriptResourceInfo> LuaScripts { get; }
|
ImmutableArray<ILuaScriptResourceInfo> LuaScripts { get; }
|
||||||
|
|||||||
@@ -0,0 +1,206 @@
|
|||||||
|
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.Networking;
|
||||||
|
using FluentResults;
|
||||||
|
|
||||||
|
namespace Barotrauma.LuaCs.Data;
|
||||||
|
|
||||||
|
|
||||||
|
// --- Storage Service
|
||||||
|
public interface IStorageServiceConfig
|
||||||
|
{
|
||||||
|
string LocalModsDirectory { get; }
|
||||||
|
string WorkshopModsDirectory { get; }
|
||||||
|
string GameSettingsConfigPath { get; }
|
||||||
|
#if CLIENT
|
||||||
|
string TempDownloadsDirectory { get; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//ReadOnlyCollection<string> SafeIOReadDirectories { get; }
|
||||||
|
//ReadOnlyCollection<string> SafeIOWriteDirectories { get; }
|
||||||
|
IEnumerable<string> GlobalIOReadWhitelist();
|
||||||
|
IEnumerable<string> GlobalIOWriteWhitelist();
|
||||||
|
|
||||||
|
bool IOReadWhiteListContains(string filePath);
|
||||||
|
bool IOWriteWhiteListContains(string filePath);
|
||||||
|
|
||||||
|
string LocalDataSavePath { get; }
|
||||||
|
string LocalDataPathRegex { get; }
|
||||||
|
string LocalPackageDataPath { get; }
|
||||||
|
public string RunLocation { get; }
|
||||||
|
bool GlobalSafeIOEnabled { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface IStorageServiceConfigUpdate
|
||||||
|
{
|
||||||
|
public FluentResults.Result SetSafeReadFilePaths(string[] filePaths);
|
||||||
|
public FluentResults.Result SetSafeWriteFilePaths(string[] filePaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record StorageServiceConfig : IStorageServiceConfig, IStorageServiceConfigUpdate
|
||||||
|
{
|
||||||
|
private static readonly string ExecutionLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location.CleanUpPath());
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
private readonly AsyncReaderWriterLock _safeIOReadLock = new();
|
||||||
|
private readonly AsyncReaderWriterLock _safeIOWriteLock = new();
|
||||||
|
private readonly ConcurrentDictionary<string,byte> _safeIOReadFilePaths = new();
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<string,byte> _safeIOWriteFilePaths = new();
|
||||||
|
|
||||||
|
public IEnumerable<string> GlobalIOReadWhitelist()
|
||||||
|
{
|
||||||
|
using var lck = _safeIOReadLock.AcquireReaderLock().GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
if (_safeIOReadFilePaths.Count == 0)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var path in _safeIOReadFilePaths)
|
||||||
|
{
|
||||||
|
yield return path.Key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<string> GlobalIOWriteWhitelist()
|
||||||
|
{
|
||||||
|
using var lck = _safeIOWriteLock.AcquireReaderLock().GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
if (_safeIOWriteFilePaths.Count == 0)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var path in _safeIOWriteFilePaths)
|
||||||
|
{
|
||||||
|
yield return path.Key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IOReadWhiteListContains(string filePath)
|
||||||
|
{
|
||||||
|
if (filePath.IsNullOrWhiteSpace())
|
||||||
|
return false;
|
||||||
|
return _safeIOReadFilePaths.ContainsKey(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IOWriteWhiteListContains(string filePath)
|
||||||
|
{
|
||||||
|
if (filePath.IsNullOrWhiteSpace())
|
||||||
|
return false;
|
||||||
|
return _safeIOWriteFilePaths.ContainsKey(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string LocalDataSavePath => Path.Combine(ExecutionLocation, "/Data/Mods/");
|
||||||
|
|
||||||
|
public string LocalDataPathRegex => "<PACKAGENAME>";
|
||||||
|
|
||||||
|
public string RunLocation => ExecutionLocation;
|
||||||
|
public bool GlobalSafeIOEnabled => false;
|
||||||
|
|
||||||
|
public string LocalPackageDataPath
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ContainsIllegalPaths(LocalDataSavePath) ? $"/Data/Mods/{LocalDataPathRegex}"
|
||||||
|
: Path.Combine(LocalDataSavePath, LocalDataPathRegex);
|
||||||
|
|
||||||
|
bool ContainsIllegalPaths(string path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public FluentResults.Result SetSafeReadFilePaths(string[] filePaths)
|
||||||
|
{
|
||||||
|
using var lck = _safeIOReadLock.AcquireWriterLock().GetAwaiter().GetResult();
|
||||||
|
return SetSafeDirectory(_safeIOReadFilePaths, filePaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FluentResults.Result SetSafeWriteFilePaths(string[] filePaths)
|
||||||
|
{
|
||||||
|
using var lck = _safeIOWriteLock.AcquireWriterLock().GetAwaiter().GetResult();
|
||||||
|
return SetSafeDirectory(_safeIOWriteFilePaths, filePaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FluentResults.Result SetSafeDirectory(ConcurrentDictionary<string,byte> target, string[] filePaths)
|
||||||
|
{
|
||||||
|
if (filePaths is null || filePaths.Length < 1)
|
||||||
|
{
|
||||||
|
target.Clear();
|
||||||
|
return FluentResults.Result.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
FluentResults.Result result = new();
|
||||||
|
|
||||||
|
target.Clear();
|
||||||
|
foreach (string path in filePaths)
|
||||||
|
{
|
||||||
|
if (path.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
result = result.WithError($"ServicesConfigData: A supplied whitelist path was null.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var path2 = Path.GetFullPath(path);
|
||||||
|
target.TryAdd(path2, 0);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
result = result.WithError(
|
||||||
|
new ExceptionalError(e).WithMetadata(FluentResults.LuaCs.MetadataType.ExceptionObject, this));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.WithSuccess($"Whitelist updated.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Config Service
|
||||||
|
public interface IConfigServiceConfig
|
||||||
|
{
|
||||||
|
string LocalConfigPathPartial { get; }
|
||||||
|
string FileNamePattern { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public record ConfigServiceConfig : IConfigServiceConfig
|
||||||
|
{
|
||||||
|
public string LocalConfigPathPartial => $"/Config/{FileNamePattern}.xml";
|
||||||
|
public string FileNamePattern => "<ConfigName>";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Lua Scripts Service
|
||||||
|
public interface ILuaScriptServicesConfig
|
||||||
|
{
|
||||||
|
bool SafeLuaIOEnabled { get; }
|
||||||
|
bool UseCaching { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public record LuaScriptServicesConfig : ILuaScriptServicesConfig
|
||||||
|
{
|
||||||
|
public bool SafeLuaIOEnabled => true;
|
||||||
|
public bool UseCaching => true;
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Barotrauma.LuaCs.Configuration;
|
||||||
using Barotrauma.LuaCs.Services;
|
using Barotrauma.LuaCs.Services;
|
||||||
using Barotrauma.Networking;
|
using Barotrauma.Networking;
|
||||||
using Dynamitey;
|
using Dynamitey;
|
||||||
@@ -60,6 +61,11 @@ internal interface IEventReloadAllPackages : IEvent<IEventReloadAllPackages>
|
|||||||
void OnReloadAllPackages();
|
void OnReloadAllPackages();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal interface IEventConfigVarInstanced : IEvent<IEventConfigVarInstanced>
|
||||||
|
{
|
||||||
|
void OnConfigCreated(IConfigBase config);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region GameEvents
|
#region GameEvents
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.IO;
|
|
||||||
using MoonSharp.Interpreter;
|
|
||||||
using MoonSharp.Interpreter.Loaders;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma
|
|
||||||
{
|
|
||||||
class LuaScriptLoader : ScriptLoaderBase
|
|
||||||
{
|
|
||||||
|
|
||||||
public override object LoadFile(string file, Table globalContext)
|
|
||||||
{
|
|
||||||
if (!LuaCsFile.IsPathAllowedLuaException(file, false)) return null;
|
|
||||||
|
|
||||||
return File.ReadAllText(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool ScriptFileExists(string file)
|
|
||||||
{
|
|
||||||
if (!LuaCsFile.IsPathAllowedLuaException(file, false)) return false;
|
|
||||||
|
|
||||||
return File.Exists(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -44,15 +44,11 @@ namespace Barotrauma
|
|||||||
_servicesProvider.RegisterServiceType<PerformanceCounterService, PerformanceCounterService>(ServiceLifetime.Singleton);
|
_servicesProvider.RegisterServiceType<PerformanceCounterService, PerformanceCounterService>(ServiceLifetime.Singleton);
|
||||||
_servicesProvider.RegisterServiceType<IStorageService, StorageService>(ServiceLifetime.Transient);
|
_servicesProvider.RegisterServiceType<IStorageService, StorageService>(ServiceLifetime.Transient);
|
||||||
_servicesProvider.RegisterServiceType<IEventService, EventService>(ServiceLifetime.Singleton);
|
_servicesProvider.RegisterServiceType<IEventService, EventService>(ServiceLifetime.Singleton);
|
||||||
#if CLIENT
|
|
||||||
_servicesProvider.RegisterServiceType<IStylesService, StylesService>(ServiceLifetime.Transient);
|
|
||||||
_servicesProvider.RegisterServiceType<IStylesManagementService, StylesManagementService>(ServiceLifetime.Singleton);
|
|
||||||
#endif
|
|
||||||
_servicesProvider.RegisterServiceType<IPackageManagementService, PackageManagementService>(ServiceLifetime.Singleton);
|
_servicesProvider.RegisterServiceType<IPackageManagementService, PackageManagementService>(ServiceLifetime.Singleton);
|
||||||
_servicesProvider.RegisterServiceType<IPluginManagementService, PluginManagementService>(ServiceLifetime.Singleton);
|
_servicesProvider.RegisterServiceType<IPluginManagementService, PluginManagementService>(ServiceLifetime.Singleton);
|
||||||
_servicesProvider.RegisterServiceType<ILuaScriptManagementService, LuaScriptManagementService>(ServiceLifetime.Singleton);
|
_servicesProvider.RegisterServiceType<ILuaScriptManagementService, LuaScriptManagementService>(ServiceLifetime.Singleton);
|
||||||
_servicesProvider.RegisterServiceType<LuaGame, LuaGame>(ServiceLifetime.Singleton);
|
_servicesProvider.RegisterServiceType<LuaGame, LuaGame>(ServiceLifetime.Singleton);
|
||||||
// TODO: ILocalizationService
|
|
||||||
// TODO: IConfigService
|
// TODO: IConfigService
|
||||||
// TODO: INetworkingService
|
// TODO: INetworkingService
|
||||||
// TODO: [Resource Converter/Parser Services]
|
// TODO: [Resource Converter/Parser Services]
|
||||||
@@ -61,13 +57,12 @@ namespace Barotrauma
|
|||||||
_servicesProvider.RegisterServiceType<IProcessorService<IReadOnlyList<IAssemblyResourceInfo>, IAssembliesResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient);
|
_servicesProvider.RegisterServiceType<IProcessorService<IReadOnlyList<IAssemblyResourceInfo>, IAssembliesResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient);
|
||||||
_servicesProvider.RegisterServiceType<IProcessorService<IReadOnlyList<IConfigResourceInfo>, IConfigsResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient);
|
_servicesProvider.RegisterServiceType<IProcessorService<IReadOnlyList<IConfigResourceInfo>, IConfigsResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient);
|
||||||
_servicesProvider.RegisterServiceType<IProcessorService<IReadOnlyList<IConfigProfileResourceInfo>, IConfigProfilesResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient);
|
_servicesProvider.RegisterServiceType<IProcessorService<IReadOnlyList<IConfigProfileResourceInfo>, IConfigProfilesResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient);
|
||||||
_servicesProvider.RegisterServiceType<IProcessorService<IReadOnlyList<ILocalizationResourceInfo>, ILocalizationsResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient);
|
|
||||||
_servicesProvider.RegisterServiceType<IProcessorService<IReadOnlyList<ILuaScriptResourceInfo>, ILuaScriptsResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient);
|
_servicesProvider.RegisterServiceType<IProcessorService<IReadOnlyList<ILuaScriptResourceInfo>, ILuaScriptsResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient);
|
||||||
|
|
||||||
|
// Loaders and Processors (yes the naming is reversed, oops).
|
||||||
_servicesProvider.RegisterServiceType<IConverterService<ContentPackage, IModConfigInfo>, ModConfigService>(ServiceLifetime.Transient);
|
_servicesProvider.RegisterServiceType<IConverterService<ContentPackage, IModConfigInfo>, ModConfigService>(ServiceLifetime.Transient);
|
||||||
_servicesProvider.RegisterServiceType<IConverterServiceAsync<ContentPackage, IModConfigInfo>, ModConfigService>(ServiceLifetime.Transient);
|
_servicesProvider.RegisterServiceType<IConverterServiceAsync<ContentPackage, IModConfigInfo>, ModConfigService>(ServiceLifetime.Transient);
|
||||||
|
_servicesProvider.RegisterServiceType<IConfigIOService, ConfigIOService>(ServiceLifetime.Transient);
|
||||||
|
|
||||||
|
|
||||||
_servicesProvider.Compile();
|
_servicesProvider.Compile();
|
||||||
}
|
}
|
||||||
@@ -121,8 +116,6 @@ namespace Barotrauma
|
|||||||
? svc : throw new NullReferenceException("Plugin Manager service not found!");
|
? svc : throw new NullReferenceException("Plugin Manager service not found!");
|
||||||
public ILuaScriptManagementService LuaScriptManagementService => _servicesProvider.TryGetService<ILuaScriptManagementService>(out var svc)
|
public ILuaScriptManagementService LuaScriptManagementService => _servicesProvider.TryGetService<ILuaScriptManagementService>(out var svc)
|
||||||
? svc : throw new NullReferenceException("Lua Script Manager service not found!");
|
? svc : throw new NullReferenceException("Lua Script Manager service not found!");
|
||||||
public ILocalizationService LocalizationService => _servicesProvider.TryGetService<ILocalizationService>(out var svc)
|
|
||||||
? svc : throw new NullReferenceException("Localization Manager service not found!");
|
|
||||||
public INetworkingService NetworkingService => _servicesProvider.TryGetService<INetworkingService>(out var svc)
|
public INetworkingService NetworkingService => _servicesProvider.TryGetService<INetworkingService>(out var svc)
|
||||||
? svc : throw new NullReferenceException("Networking Manager service not found!");
|
? svc : throw new NullReferenceException("Networking Manager service not found!");
|
||||||
public IEventService EventService => _servicesProvider.TryGetService<IEventService>(out var svc)
|
public IEventService EventService => _servicesProvider.TryGetService<IEventService>(out var svc)
|
||||||
@@ -180,6 +173,11 @@ namespace Barotrauma
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IConfigEntry<bool> RestrictMessageSize { get; private set; }
|
public IConfigEntry<bool> RestrictMessageSize { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The local save path for all local data storage for mods.
|
||||||
|
/// </summary>
|
||||||
|
public IConfigEntry<string> LocalDataSavePath { get; private set; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* == Ops Vars
|
* == Ops Vars
|
||||||
*/
|
*/
|
||||||
@@ -287,11 +285,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
PluginManagementService.Dispose();
|
PluginManagementService.Dispose();
|
||||||
LuaScriptManagementService.Dispose();
|
LuaScriptManagementService.Dispose();
|
||||||
#if CLIENT
|
|
||||||
StylesManagementService.Dispose();
|
|
||||||
#endif
|
|
||||||
ConfigService.Dispose();
|
ConfigService.Dispose();
|
||||||
LocalizationService.Dispose();
|
|
||||||
PackageManagementService.Dispose();
|
PackageManagementService.Dispose();
|
||||||
// TODO: Add all missing services.
|
// TODO: Add all missing services.
|
||||||
//NetworkingService.Dispose();
|
//NetworkingService.Dispose();
|
||||||
@@ -372,12 +366,7 @@ namespace Barotrauma
|
|||||||
while (_toUnload.TryDequeue(out var cp))
|
while (_toUnload.TryDequeue(out var cp))
|
||||||
{
|
{
|
||||||
LuaScriptManagementService.DisposePackageResources(cp);
|
LuaScriptManagementService.DisposePackageResources(cp);
|
||||||
ConfigService.DisposeConfigsProfiles(cp);
|
ConfigService.DisposePackageData(cp);
|
||||||
ConfigService.DisposeConfigs(cp);
|
|
||||||
#if CLIENT
|
|
||||||
StylesManagementService.DisposeStylesForPackage(cp);
|
|
||||||
#endif
|
|
||||||
LocalizationService.DisposePackage(cp);
|
|
||||||
PackageManagementService.DisposePackageInfos(cp);
|
PackageManagementService.DisposePackageInfos(cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,23 +504,22 @@ namespace Barotrauma
|
|||||||
|
|
||||||
void LoadLuaCsConfig()
|
void LoadLuaCsConfig()
|
||||||
{
|
{
|
||||||
IsCsEnabled = ConfigService.GetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "IsCsEnabled")
|
IsCsEnabled = ConfigService.TryGetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "IsCsEnabled", out var val1) ? val1
|
||||||
?? throw new NullReferenceException($"{nameof(IsCsEnabled)} cannot be loaded.");
|
: throw new NullReferenceException($"{nameof(IsCsEnabled)} cannot be loaded.");
|
||||||
TreatForcedModsAsNormal = ConfigService.GetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "TreatForcedModsAsNormal")
|
TreatForcedModsAsNormal = ConfigService.TryGetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "TreatForcedModsAsNormal", out var val2) ? val2
|
||||||
?? throw new NullReferenceException($"{nameof(TreatForcedModsAsNormal)} cannot be loaded.");
|
: throw new NullReferenceException($"{nameof(TreatForcedModsAsNormal)} cannot be loaded.");
|
||||||
DisableErrorGUIOverlay = ConfigService.GetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "DisableErrorGUIOverlay")
|
DisableErrorGUIOverlay = ConfigService.TryGetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "DisableErrorGUIOverlay", out var val3) ? val3
|
||||||
?? throw new NullReferenceException($"{nameof(DisableErrorGUIOverlay)} cannot be loaded.");
|
: throw new NullReferenceException($"{nameof(DisableErrorGUIOverlay)} cannot be loaded.");
|
||||||
HideUserNamesInLogs = ConfigService.GetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "HideUserNamesInLogs")
|
HideUserNamesInLogs = ConfigService.TryGetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "HideUserNamesInLogs", out var val4) ? val4
|
||||||
?? throw new NullReferenceException($"{nameof(HideUserNamesInLogs)} cannot be loaded.");
|
: throw new NullReferenceException($"{nameof(HideUserNamesInLogs)} cannot be loaded.");
|
||||||
LuaForBarotraumaSteamId = ConfigService.GetConfig<IConfigEntry<ulong>>(ContentPackageManager.VanillaCorePackage, "LuaForBarotraumaSteamId")
|
LuaForBarotraumaSteamId = ConfigService.TryGetConfig<IConfigEntry<ulong>>(ContentPackageManager.VanillaCorePackage, "LuaForBarotraumaSteamId", out var val5) ? val5
|
||||||
?? throw new NullReferenceException($"{nameof(LuaForBarotraumaSteamId)} cannot be loaded.");
|
: throw new NullReferenceException($"{nameof(LuaForBarotraumaSteamId)} cannot be loaded.");
|
||||||
CsForBarotraumaSteamId = ConfigService.GetConfig<IConfigEntry<ulong>>(ContentPackageManager.VanillaCorePackage, "CsForBarotraumaSteamId")
|
CsForBarotraumaSteamId = ConfigService.TryGetConfig<IConfigEntry<ulong>>(ContentPackageManager.VanillaCorePackage, "CsForBarotraumaSteamId", out var val6) ? val6
|
||||||
?? throw new NullReferenceException($"{nameof(CsForBarotraumaSteamId)} cannot be loaded.");
|
: throw new NullReferenceException($"{nameof(CsForBarotraumaSteamId)} cannot be loaded.");
|
||||||
RestrictMessageSize = ConfigService.GetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "RestrictMessageSize")
|
RestrictMessageSize = ConfigService.TryGetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "RestrictMessageSize", out var val7) ? val7
|
||||||
?? throw new NullReferenceException($"{nameof(RestrictMessageSize)} cannot be loaded.");
|
: throw new NullReferenceException($"{nameof(RestrictMessageSize)} cannot be loaded.");
|
||||||
ReloadPackagesOnLobbyStart = ConfigService.GetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "ReloadPackagesOnLobbyStart")
|
ReloadPackagesOnLobbyStart = ConfigService.TryGetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "ReloadPackagesOnLobbyStart", out var val8) ? val8
|
||||||
?? throw new NullReferenceException($"{nameof(ReloadPackagesOnLobbyStart)} cannot be loaded.");
|
: throw new NullReferenceException($"{nameof(ReloadPackagesOnLobbyStart)} cannot be loaded.");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisposeLuaCsConfig()
|
void DisposeLuaCsConfig()
|
||||||
@@ -548,27 +536,14 @@ namespace Barotrauma
|
|||||||
|
|
||||||
async Task LoadStaticAssetsAsync(IReadOnlyList<ContentPackage> packages)
|
async Task LoadStaticAssetsAsync(IReadOnlyList<ContentPackage> packages)
|
||||||
{
|
{
|
||||||
var locRes = ImmutableArray<ILocalizationResourceInfo>.Empty;
|
|
||||||
var cfgRes = ImmutableArray<IConfigResourceInfo>.Empty;
|
var cfgRes = ImmutableArray<IConfigResourceInfo>.Empty;
|
||||||
var cfpRes = ImmutableArray<IConfigProfileResourceInfo>.Empty;
|
var cfpRes = ImmutableArray<IConfigProfileResourceInfo>.Empty;
|
||||||
var luaRes = ImmutableArray<ILuaScriptResourceInfo>.Empty;
|
var luaRes = ImmutableArray<ILuaScriptResourceInfo>.Empty;
|
||||||
|
|
||||||
#if CLIENT
|
|
||||||
var styleRes = ImmutableArray<IStylesResourceInfo>.Empty;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
var tasksBuilder = ImmutableArray.CreateBuilder<Task>();
|
var tasksBuilder = ImmutableArray.CreateBuilder<Task>();
|
||||||
|
|
||||||
//---- get resource infos
|
//---- get resource infos
|
||||||
tasksBuilder.AddRange(new Func<Task>(async () =>
|
tasksBuilder.AddRange(
|
||||||
{
|
|
||||||
var res = await PackageManagementService.GetLocalizationsInfosAsync(packages);
|
|
||||||
if (res.IsSuccess)
|
|
||||||
locRes = res.Value.Localizations;
|
|
||||||
if (res.Errors.Any())
|
|
||||||
ThreadPool.QueueUserWorkItem(state => Logger.LogResults((FluentResults.Result)state),
|
|
||||||
res.ToResult());
|
|
||||||
})(),
|
|
||||||
new Func<Task>(async () =>
|
new Func<Task>(async () =>
|
||||||
{
|
{
|
||||||
var res = await PackageManagementService.GetConfigsInfosAsync(packages);
|
var res = await PackageManagementService.GetConfigsInfosAsync(packages);
|
||||||
@@ -597,17 +572,6 @@ namespace Barotrauma
|
|||||||
res.ToResult());
|
res.ToResult());
|
||||||
})());
|
})());
|
||||||
|
|
||||||
#if CLIENT
|
|
||||||
tasksBuilder.Add(new Func<Task>(async () =>
|
|
||||||
{
|
|
||||||
var res = await PackageManagementService.GetStylesInfosAsync(packages);
|
|
||||||
if (res.IsSuccess)
|
|
||||||
styleRes = res.Value.Styles;
|
|
||||||
if (res.Errors.Any())
|
|
||||||
ThreadPool.QueueUserWorkItem(state => Logger.LogResults((FluentResults.Result)state),
|
|
||||||
res.ToResult());
|
|
||||||
})());
|
|
||||||
#endif
|
|
||||||
await Task.WhenAll(tasksBuilder.MoveToImmutable());
|
await Task.WhenAll(tasksBuilder.MoveToImmutable());
|
||||||
tasksBuilder.Clear();
|
tasksBuilder.Clear();
|
||||||
|
|
||||||
@@ -628,23 +592,6 @@ namespace Barotrauma
|
|||||||
Logger.LogResults(res);
|
Logger.LogResults(res);
|
||||||
})());
|
})());
|
||||||
|
|
||||||
#if CLIENT
|
|
||||||
tasksBuilder.Add(new Func<Task>(async () =>
|
|
||||||
{
|
|
||||||
var res = await StylesManagementService.LoadStylesAsync(styleRes);
|
|
||||||
if (res.Errors.Any())
|
|
||||||
Logger.LogResults(res);
|
|
||||||
})());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// load localizations first
|
|
||||||
if (!locRes.IsDefaultOrEmpty)
|
|
||||||
{
|
|
||||||
var res = await LocalizationService.LoadLocalizations(locRes);
|
|
||||||
if (res.Errors.Any())
|
|
||||||
Logger.LogResults(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.WhenAll(tasksBuilder.MoveToImmutable());
|
await Task.WhenAll(tasksBuilder.MoveToImmutable());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -706,22 +653,18 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
//lua
|
//lua
|
||||||
var luaRes = PackageManagementService.GetLuaScriptsInfos(PackageManagementService
|
var luaRes = PackageManagementService.LuaScripts
|
||||||
.GetAllLoadedPackages()
|
.Select(ls => ls.OwnerPackage)
|
||||||
|
.Where(p => p is not null)
|
||||||
.Where(ContentPackageManager.EnabledPackages.All.Contains)
|
.Where(ContentPackageManager.EnabledPackages.All.Contains)
|
||||||
.ToList());
|
.ToImmutableArray();
|
||||||
if (luaRes.IsFailed)
|
if (luaRes.IsDefaultOrEmpty)
|
||||||
{
|
{
|
||||||
Logger.LogError($"{nameof(RunScripts)}: Failed to get enabled lua script resources!");
|
Logger.LogError($"{nameof(RunScripts)}: Failed to get enabled lua script resources!");
|
||||||
Logger.LogResults(luaRes.ToResult());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (luaRes.Errors.Any())
|
LuaScriptManagementService.ExecuteLoadedScriptsForPackages(luaRes);
|
||||||
Logger.LogResults(luaRes.ToResult());
|
|
||||||
|
|
||||||
|
|
||||||
LuaScriptManagementService.ExecuteLoadedScripts(luaRes.Value.LuaScripts);
|
|
||||||
|
|
||||||
if (CurrentRunState < RunState.Running)
|
if (CurrentRunState < RunState.Running)
|
||||||
_runState = RunState.Running;
|
_runState = RunState.Running;
|
||||||
@@ -748,10 +691,6 @@ namespace Barotrauma
|
|||||||
PluginManagementService.Reset();
|
PluginManagementService.Reset();
|
||||||
LuaScriptManagementService.Reset();
|
LuaScriptManagementService.Reset();
|
||||||
ConfigService.Reset();
|
ConfigService.Reset();
|
||||||
#if CLIENT
|
|
||||||
StylesManagementService.Reset();
|
|
||||||
#endif
|
|
||||||
LocalizationService.Reset();
|
|
||||||
|
|
||||||
if (CurrentRunState >= RunState.Configuration)
|
if (CurrentRunState >= RunState.Configuration)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -567,8 +567,17 @@ namespace FluentResults.LuaCs
|
|||||||
public static class MetadataType
|
public static class MetadataType
|
||||||
{
|
{
|
||||||
public static string ExceptionDetails = nameof(ExceptionDetails);
|
public static string ExceptionDetails = nameof(ExceptionDetails);
|
||||||
|
/// <summary>
|
||||||
|
/// The object that threw the exception.
|
||||||
|
/// </summary>
|
||||||
public static string ExceptionObject = nameof(ExceptionObject);
|
public static string ExceptionObject = nameof(ExceptionObject);
|
||||||
|
/// <summary>
|
||||||
|
/// The parameter-object responsible for the exception thrown (not the exception thrower).
|
||||||
|
/// </summary>
|
||||||
public static string RootObject = nameof(RootObject);
|
public static string RootObject = nameof(RootObject);
|
||||||
|
/// <summary>
|
||||||
|
/// Additional exception sources.
|
||||||
|
/// </summary>
|
||||||
public static string Sources = nameof(Sources);
|
public static string Sources = nameof(Sources);
|
||||||
public static string StackTrace = nameof(StackTrace);
|
public static string StackTrace = nameof(StackTrace);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Barotrauma.LuaCs.Configuration;
|
|
||||||
using Barotrauma.LuaCs.Data;
|
|
||||||
using Barotrauma.LuaCs.Services;
|
|
||||||
using Barotrauma.Networking;
|
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Services;
|
|
||||||
|
|
||||||
public interface INetVar : IVarId
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Synchronization type
|
|
||||||
/// </summary>
|
|
||||||
NetSync SyncType { get; }
|
|
||||||
/// <summary>
|
|
||||||
/// Permissions needed by clients to send net-events or receive net messages.
|
|
||||||
/// </summary>
|
|
||||||
ClientPermissions WritePermissions { get; }
|
|
||||||
|
|
||||||
void ReadNetMessage(INetReadMessage message);
|
|
||||||
void WriteNetMessage(INetWriteMessage message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum NetSync
|
|
||||||
{
|
|
||||||
None, TwoWay, ServerAuthority, ClientOneWay
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using Barotrauma.LuaCs.Configuration;
|
||||||
|
using Barotrauma.LuaCs.Data;
|
||||||
|
using Barotrauma.LuaCs.Services;
|
||||||
|
using Barotrauma.Networking;
|
||||||
|
|
||||||
|
namespace Barotrauma.LuaCs.Services;
|
||||||
|
|
||||||
|
public interface INetworkSyncEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Network-synchronized object ID. Used for networking send/receive message events.
|
||||||
|
/// </summary>
|
||||||
|
Guid InstanceId { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Synchronization type. See <see cref="NetSync"/> for more information.
|
||||||
|
/// </summary>
|
||||||
|
NetSync SyncType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Permissions needed by clients to send net-events and/or receive net messages.
|
||||||
|
/// </summary>
|
||||||
|
ClientPermissions WritePermissions { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when an incoming net message has data for this network object, typically from the same entity on another
|
||||||
|
/// machine.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">Wrapper for the internal type: <see cref="IReadMessage"/></param>
|
||||||
|
void ReadNetMessage(INetReadMessage message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when a network send-event involving this entity is triggered. Any data expected to be read by the recipient
|
||||||
|
/// network object on the other instance(s) should be written to the packet.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">Wrapper for the internal type: <see cref="IWriteMessage"/></param>
|
||||||
|
void WriteNetMessage(INetWriteMessage message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the networking send/receive relationship for network object. Objects implementing this interface are
|
||||||
|
/// expected to adhere to the contract or de-sync may occur.
|
||||||
|
/// </summary>
|
||||||
|
public enum NetSync
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No network synchronization.
|
||||||
|
/// </summary>
|
||||||
|
None,
|
||||||
|
/// <summary>
|
||||||
|
/// Both the client and the server have 'send' and 'receive' permissions (limited by <see cref="ClientPermissions"/>). Can also be used to allow two-way communication
|
||||||
|
/// with the server.
|
||||||
|
/// </summary>
|
||||||
|
TwoWay,
|
||||||
|
/// <summary>
|
||||||
|
/// Only the host/server has the authority to change this value.
|
||||||
|
/// </summary>
|
||||||
|
ServerAuthority,
|
||||||
|
/// <summary>
|
||||||
|
/// Only clients (with the required by <see cref="ClientPermissions"/>) may change the value and all value changes are communicated to the server/host.
|
||||||
|
/// <br/><br/><b>[Important] The host/server will not send the value to other connected clients.</b><br/>
|
||||||
|
/// Intended to allow clients to send one-way messages to the server.
|
||||||
|
/// </summary>
|
||||||
|
ClientOneWay
|
||||||
|
}
|
||||||
@@ -2,5 +2,9 @@
|
|||||||
|
|
||||||
public interface ILuaCsUtility : ILuaCsShim
|
public interface ILuaCsUtility : ILuaCsShim
|
||||||
{
|
{
|
||||||
|
public bool CanReadFromPath(string file);
|
||||||
|
public bool CanWriteToPath(string file);
|
||||||
|
internal bool IsPathAllowedException(string path, bool write = true,
|
||||||
|
LuaCsMessageOrigin origin = LuaCsMessageOrigin.Unknown);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,340 @@
|
|||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
using Barotrauma.LuaCs.Configuration;
|
||||||
|
using Barotrauma.LuaCs.Data;
|
||||||
|
using FluentResults;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
using Vector2 = Microsoft.Xna.Framework.Vector2;
|
||||||
|
using Vector3 = Microsoft.Xna.Framework.Vector3;
|
||||||
|
using Vector4 = Microsoft.Xna.Framework.Vector4;
|
||||||
|
|
||||||
|
namespace Barotrauma.LuaCs.Services;
|
||||||
|
|
||||||
|
public class ConfigInitializers : IService
|
||||||
|
{
|
||||||
|
// parameterless .ctor
|
||||||
|
public ConfigInitializers()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// stateless service
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateless service
|
||||||
|
public bool IsDisposed => false;
|
||||||
|
|
||||||
|
private Result<IConfigEntry<T>> CreateConfigEntry<T>(IConfigInfo configInfo,
|
||||||
|
Action<ConfigEntry<T>, INetReadMessage> readHandler,
|
||||||
|
Action<ConfigEntry<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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigList<T>> CreateConfigList<T>(IConfigInfo configInfo,
|
||||||
|
Action<IConfigList<T>, INetReadMessage> readHandler, Action<IConfigList<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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterTypeInitializers(IConfigService configService)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region InitializerWrappers_NetworkInjected
|
||||||
|
|
||||||
|
private void AssignValueConditional<T>(T val, IConfigEntry<T> inst) where T : IEquatable<T>
|
||||||
|
{
|
||||||
|
#if SERVER
|
||||||
|
if (inst.SyncType is NetSync.None or NetSync.ServerAuthority)
|
||||||
|
throw new InvalidOperationException($"[Server] Tried to assign a net value to a type that does not support sync: {inst.SyncType}. Name: {inst.InternalName}, Package: {inst.OwnerPackage.Name}");
|
||||||
|
inst.TrySetValue(val);
|
||||||
|
#else
|
||||||
|
if (inst.SyncType is NetSync.None or NetSync.ClientOneWay)
|
||||||
|
throw new InvalidOperationException($"[Client] Tried to assign a net value to a type that does not support sync: {inst.SyncType}. Name: {inst.InternalName}, Package: {inst.OwnerPackage.Name}");
|
||||||
|
inst.TrySetValue(val);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<bool>> CreateConfigBool(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<bool>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional(readMsg.ReadBoolean(), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteBoolean(inst.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<sbyte>> CreateConfigSbyte(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<sbyte>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional((sbyte)readMsg.ReadInt16(), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteInt16((short)inst.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<byte>> CreateConfigByte(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<byte>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional((byte)readMsg.ReadUInt16(), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteUInt16((byte)inst.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<short>> CreateConfigShort(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<short>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional(readMsg.ReadInt16(), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteInt16(inst.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<ushort>> CreateConfigUShort(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<ushort>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional(readMsg.ReadUInt16(), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteUInt16(inst.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<int>> CreateConfigInt32(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<int>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional(readMsg.ReadInt32(), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteInt32(inst.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<uint>> CreateConfigUInt32(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<uint>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional(readMsg.ReadUInt32(), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteUInt32(inst.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<long>> CreateConfigInt64(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<long>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional(readMsg.ReadInt64(), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteInt64(inst.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<ulong>> CreateConfigUInt64(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<ulong>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional(readMsg.ReadUInt64(), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteUInt64(inst.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<float>> CreateConfigFloat32(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<float>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional(readMsg.ReadSingle(), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteSingle(inst.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<double>> CreateConfigFloat64(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<double>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional(readMsg.ReadDouble(), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteDouble(inst.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<decimal>> CreateConfigFloat128(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<decimal>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
var decimalArr = new int[4];
|
||||||
|
decimalArr[0] = readMsg.ReadInt32();
|
||||||
|
decimalArr[1] = readMsg.ReadInt32();
|
||||||
|
decimalArr[2] = readMsg.ReadInt32();
|
||||||
|
decimalArr[3] = readMsg.ReadInt32();
|
||||||
|
AssignValueConditional(new decimal(decimalArr), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
var decimalArr = Decimal.GetBits(inst.Value);
|
||||||
|
writeMsg.WriteInt32(decimalArr[0]);
|
||||||
|
writeMsg.WriteInt32(decimalArr[1]);
|
||||||
|
writeMsg.WriteInt32(decimalArr[2]);
|
||||||
|
writeMsg.WriteInt32(decimalArr[3]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<char>> CreateConfigChar(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<char>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional((char)readMsg.ReadUInt16(), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteUInt16(inst.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<string>> CreateConfigString(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<string>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional(readMsg.ReadString(), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteString(inst.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<Color>> CreateConfigColor(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<Color>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional(readMsg.ReadColorR8G8B8A8(), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteColorR8G8B8A8(inst.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<Vector2>> CreateConfigVector2(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<Vector2>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional(new Vector2(readMsg.ReadSingle(), readMsg.ReadSingle()), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteSingle(inst.Value.X);
|
||||||
|
writeMsg.WriteSingle(inst.Value.Y);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<Vector3>> CreateConfigVector3(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<Vector3>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional(new Vector3(readMsg.ReadSingle(), readMsg.ReadSingle(), readMsg.ReadSingle()), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteSingle(inst.Value.X);
|
||||||
|
writeMsg.WriteSingle(inst.Value.Y);
|
||||||
|
writeMsg.WriteSingle(inst.Value.Z);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<IConfigEntry<Vector4>> CreateConfigVector4(IConfigInfo configInfo)
|
||||||
|
{
|
||||||
|
return CreateConfigEntry<Vector4>(configInfo, (inst, readMsg) =>
|
||||||
|
{
|
||||||
|
AssignValueConditional(new Vector4(
|
||||||
|
readMsg.ReadSingle(),
|
||||||
|
readMsg.ReadSingle(),
|
||||||
|
readMsg.ReadSingle(),
|
||||||
|
readMsg.ReadSingle()), inst);
|
||||||
|
|
||||||
|
}, (inst, writeMsg) =>
|
||||||
|
{
|
||||||
|
writeMsg.WriteSingle(inst.Value.X);
|
||||||
|
writeMsg.WriteSingle(inst.Value.Y);
|
||||||
|
writeMsg.WriteSingle(inst.Value.Z);
|
||||||
|
writeMsg.WriteSingle(inst.Value.W);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -1,6 +1,690 @@
|
|||||||
namespace Barotrauma.LuaCs.Services;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Frozen;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
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;
|
||||||
|
|
||||||
public class ConfigService : IConfigService
|
namespace Barotrauma.LuaCs.Services;
|
||||||
|
|
||||||
|
public partial class ConfigService : IConfigService
|
||||||
{
|
{
|
||||||
|
//--- Internals
|
||||||
|
public ConfigService(IConverterServiceAsync<IConfigProfileResourceInfo, IReadOnlyList<IConfigProfileInfo>> configProfileResourceConverter,
|
||||||
|
IConverterServiceAsync<IConfigResourceInfo, IReadOnlyList<IConfigInfo>> configResourceConverter, IEventService eventService, System.Lazy<IStorageService> storageService)
|
||||||
|
{
|
||||||
|
_configProfileResourceConverter = configProfileResourceConverter;
|
||||||
|
_configResourceConverter = configResourceConverter;
|
||||||
|
_eventService = eventService;
|
||||||
|
_storageService = storageService;
|
||||||
|
this._base = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// data, states
|
||||||
|
private readonly IService _base;
|
||||||
|
private int _isDisposed = 0;
|
||||||
|
private readonly ConcurrentDictionary<Type, Func<IConfigInfo, FluentResults.Result<IConfigBase>>> _configTypeInitializers = new();
|
||||||
|
private readonly ConcurrentDictionary<(ContentPackage Package, string ConfigName), IConfigBase> _configs = new();
|
||||||
|
private readonly ConcurrentDictionary<ContentPackage, ConcurrentBag<(ContentPackage Package, string ConfigName)>> _packageConfigReverseLookup = new();
|
||||||
|
private readonly ConcurrentDictionary<(ContentPackage Package, string ProfileName), ImmutableArray<(string ConfigName, OneOf.OneOf<string, XElement> Value)>> _configProfiles = new();
|
||||||
|
private readonly ConcurrentDictionary<ContentPackage, ConcurrentBag<(ContentPackage Package, string ProfileName)>> _packageProfilesReverseLookup = new();
|
||||||
|
private readonly ConcurrentDictionary<string, ContentPackage> _packageNameMap= new();
|
||||||
|
|
||||||
|
private readonly AsyncReaderWriterLock _disposeOpsLock = new();
|
||||||
|
|
||||||
|
// extern services
|
||||||
|
private readonly IConverterServiceAsync<IConfigResourceInfo, IReadOnlyList<IConfigInfo>> _configResourceConverter;
|
||||||
|
private readonly IConverterServiceAsync<IConfigProfileResourceInfo, IReadOnlyList<IConfigProfileInfo>> _configProfileResourceConverter;
|
||||||
|
private readonly IEventService _eventService;
|
||||||
|
private readonly System.Lazy<IStorageService> _storageService;
|
||||||
|
|
||||||
|
//--- GC
|
||||||
|
public bool IsDisposed => ModUtils.Threading.GetBool(ref _isDisposed);
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// stop all ops
|
||||||
|
using var lck = _disposeOpsLock.AcquireWriterLock().GetAwaiter().GetResult();
|
||||||
|
// set flag
|
||||||
|
ModUtils.Threading.SetBool(ref _isDisposed, true);
|
||||||
|
|
||||||
|
_configTypeInitializers.Clear();
|
||||||
|
if (!_configs.IsEmpty)
|
||||||
|
{
|
||||||
|
foreach (var config in _configs)
|
||||||
|
{
|
||||||
|
if (config.Value is IDisposable disposable)
|
||||||
|
disposable.Dispose();
|
||||||
|
config.Value.OnValueChanged -= this.SaveConfigEvent;
|
||||||
|
}
|
||||||
|
_configs.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
_configProfiles.Clear();
|
||||||
|
_packageConfigReverseLookup.Clear();
|
||||||
|
_packageNameMap.Clear();
|
||||||
|
_packageProfilesReverseLookup.Clear();
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FluentResults.Result Reset()
|
||||||
|
{
|
||||||
|
using var lck = _disposeOpsLock.AcquireWriterLock().GetAwaiter().GetResult();
|
||||||
|
_base.CheckDisposed();
|
||||||
|
|
||||||
|
_configTypeInitializers.Clear();
|
||||||
|
_configs.Clear();
|
||||||
|
_configProfiles.Clear();
|
||||||
|
_packageConfigReverseLookup.Clear();
|
||||||
|
_packageNameMap.Clear();
|
||||||
|
_packageProfilesReverseLookup.Clear();
|
||||||
|
|
||||||
|
return FluentResults.Result.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
//--- API contracts
|
||||||
|
// Notes:
|
||||||
|
// -- Lua Interface uses strong types due to lua limitations. May be required to move API to an adapter class
|
||||||
|
// to allow testing abstraction.
|
||||||
|
// -- Lua interface should not propagate errors.
|
||||||
|
#region LuaInterface
|
||||||
|
|
||||||
|
private bool TryGetConfigValue<T>(string packageName, string configName, out T value) where T : IEquatable<T>
|
||||||
|
{
|
||||||
|
value = default;
|
||||||
|
using var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult();
|
||||||
|
if (ModUtils.Threading.GetBool(ref _isDisposed))
|
||||||
|
return false;
|
||||||
|
if (!_packageNameMap.TryGetValue(packageName, out var package))
|
||||||
|
return false;
|
||||||
|
if (!_configs.TryGetValue((package, configName), out var config))
|
||||||
|
return false;
|
||||||
|
if (config is not IConfigEntry<T> entry)
|
||||||
|
return false;
|
||||||
|
value = entry.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetConfigBool(string packageName, string configName, out bool value)
|
||||||
|
{
|
||||||
|
return TryGetConfigValue(packageName, configName, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetConfigInt(string packageName, string configName, out int value)
|
||||||
|
{
|
||||||
|
return TryGetConfigValue(packageName, configName, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetConfigFloat(string packageName, string configName, out float value)
|
||||||
|
{
|
||||||
|
return TryGetConfigValue(packageName, configName, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetConfigNumber(string packageName, string configName, out double value)
|
||||||
|
{
|
||||||
|
return TryGetConfigValue(packageName, configName, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetConfigString(string packageName, string configName, out string value)
|
||||||
|
{
|
||||||
|
return TryGetConfigValue(packageName, configName, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetConfigVector2(string packageName, string configName, out Vector2 value)
|
||||||
|
{
|
||||||
|
return TryGetConfigValue(packageName, configName, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetConfigVector3(string packageName, string configName, out Vector3 value)
|
||||||
|
{
|
||||||
|
return TryGetConfigValue(packageName, configName, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetConfigColor(string packageName, string configName, out Color value)
|
||||||
|
{
|
||||||
|
return TryGetConfigValue(packageName, configName, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetConfigList(string packageName, string configName, out IReadOnlyList<string> value)
|
||||||
|
{
|
||||||
|
value = null;
|
||||||
|
using var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult();
|
||||||
|
if (ModUtils.Threading.GetBool(ref _isDisposed))
|
||||||
|
return false;
|
||||||
|
if (!_packageNameMap.TryGetValue(packageName, out var package))
|
||||||
|
return false;
|
||||||
|
if (!_configs.TryGetValue((package, configName), out var config))
|
||||||
|
return false;
|
||||||
|
if (config is not IConfigList<string> entry)
|
||||||
|
return false;
|
||||||
|
value = entry.Options;
|
||||||
|
return value is not null && value.Count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetConfigValue<T>(string packageName, string configName, T value) where T : IEquatable<T>
|
||||||
|
{
|
||||||
|
using var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult();
|
||||||
|
if (ModUtils.Threading.GetBool(ref _isDisposed))
|
||||||
|
return;
|
||||||
|
if (!_packageNameMap.TryGetValue(packageName, out var package))
|
||||||
|
return;
|
||||||
|
if (!_configs.TryGetValue((package, configName), out var config))
|
||||||
|
return;
|
||||||
|
if (config is not IConfigEntry<T> entry)
|
||||||
|
return;
|
||||||
|
entry.TrySetValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConfigBool(string packageName, string configName, bool value)
|
||||||
|
{
|
||||||
|
SetConfigValue(packageName, configName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConfigInt(string packageName, string configName, int value)
|
||||||
|
{
|
||||||
|
SetConfigValue(packageName, configName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConfigFloat(string packageName, string configName, float value)
|
||||||
|
{
|
||||||
|
SetConfigValue(packageName, configName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConfigNumber(string packageName, string configName, double value)
|
||||||
|
{
|
||||||
|
SetConfigValue(packageName, configName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConfigString(string packageName, string configName, string value)
|
||||||
|
{
|
||||||
|
SetConfigValue(packageName, configName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConfigVector2(string packageName, string configName, Vector2 value)
|
||||||
|
{
|
||||||
|
SetConfigValue(packageName, configName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConfigVector3(string packageName, string configName, Vector3 value)
|
||||||
|
{
|
||||||
|
SetConfigValue(packageName, configName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConfigColor(string packageName, string configName, Color value)
|
||||||
|
{
|
||||||
|
SetConfigValue(packageName, configName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConfigList(string packageName, string configName, string value)
|
||||||
|
{
|
||||||
|
using var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult();
|
||||||
|
if (ModUtils.Threading.GetBool(ref _isDisposed))
|
||||||
|
return;
|
||||||
|
if (!_packageNameMap.TryGetValue(packageName, out var package))
|
||||||
|
return;
|
||||||
|
if (!_configs.TryGetValue((package, configName), out var config))
|
||||||
|
return;
|
||||||
|
if (config is not IConfigList<string> entry)
|
||||||
|
return;
|
||||||
|
entry.TrySetValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryApplyProfileSettings(string packageName, string profileName)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (ModUtils.Threading.GetBool(ref _isDisposed))
|
||||||
|
return false;
|
||||||
|
if (packageName.IsNullOrWhiteSpace() || profileName.IsNullOrWhiteSpace())
|
||||||
|
return false;
|
||||||
|
ContentPackage package = null;
|
||||||
|
using (var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult())
|
||||||
|
{
|
||||||
|
if (!_packageNameMap.TryGetValue(packageName, out package) || package == null)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// exit semaphore before invocation. Note: Race condition, may require copy implementation.
|
||||||
|
return this.ApplyProfileSettings(package, profileName).IsSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public void RegisterTypeInitializer<TData, TConfig>(Func<IConfigInfo, FluentResults.Result<TConfig>> initializer, bool replaceIfExists = false)
|
||||||
|
where TData : IEquatable<TData> where TConfig : IConfigBase
|
||||||
|
{
|
||||||
|
using var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult();
|
||||||
|
_base.CheckDisposed();
|
||||||
|
|
||||||
|
Type dataType = typeof(TData);
|
||||||
|
if (_configTypeInitializers.ContainsKey(dataType) && !replaceIfExists)
|
||||||
|
return;
|
||||||
|
_configTypeInitializers[dataType] = (info =>
|
||||||
|
{
|
||||||
|
var res = initializer(info);
|
||||||
|
if (res.IsFailed)
|
||||||
|
return FluentResults.Result.Fail($"Failed to initialize config type {dataType.Name}").WithErrors(res.Errors);
|
||||||
|
return res.Value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddConfigInstance((ContentPackage Package, string ConfigName) key, IConfigBase instance)
|
||||||
|
{
|
||||||
|
_configs[key] = instance;
|
||||||
|
if (!_packageNameMap.ContainsKey(key.Package.Name))
|
||||||
|
_packageNameMap[key.Package.Name] = key.Package;
|
||||||
|
if (!_packageConfigReverseLookup.TryGetValue(key.Package, out var list))
|
||||||
|
{
|
||||||
|
list = new ConcurrentBag<(ContentPackage Package, string ConfigName)>();
|
||||||
|
_packageConfigReverseLookup[key.Package] = list;
|
||||||
|
}
|
||||||
|
list.Add(key);
|
||||||
|
// save hook
|
||||||
|
instance.OnValueChanged += this.SaveConfigEvent;
|
||||||
|
_eventService.PublishEvent<IEventConfigVarInstanced>(sub => sub.OnConfigCreated(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddProfileInstance((ContentPackage Package, string ProfileName) key, IConfigProfileInfo profile)
|
||||||
|
{
|
||||||
|
_configProfiles[key] = profile.ProfileValues.ToImmutableArray();
|
||||||
|
if (!_packageNameMap.ContainsKey(key.Package.Name))
|
||||||
|
_packageNameMap[key.Package.Name] = key.Package;
|
||||||
|
if (!_packageProfilesReverseLookup.TryGetValue(key.Package, out var list))
|
||||||
|
{
|
||||||
|
list = new ConcurrentBag<(ContentPackage Package, string ProfileName)>();
|
||||||
|
_packageProfilesReverseLookup[key.Package] = list;
|
||||||
|
}
|
||||||
|
list.Add(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<FluentResults.Result> LoadConfigsAsync(ImmutableArray<IConfigResourceInfo> configResources)
|
||||||
|
{
|
||||||
|
using var lck = await _disposeOpsLock.AcquireReaderLock();
|
||||||
|
_base.CheckDisposed();
|
||||||
|
|
||||||
|
if (configResources.IsDefaultOrEmpty)
|
||||||
|
return FluentResults.Result.Fail($"{nameof(LoadConfigsAsync)}: Array is empty.");
|
||||||
|
|
||||||
|
var results = await _configResourceConverter.TryParseResourcesAsync(configResources);
|
||||||
|
var ret = new FluentResults.Result();
|
||||||
|
|
||||||
|
foreach (var result in results)
|
||||||
|
{
|
||||||
|
if (result.Errors.Any())
|
||||||
|
ret.Errors.AddRange(result.Errors);
|
||||||
|
if (result.IsFailed || result.Value is not { Count: > 0 } res)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (var configInfo in res)
|
||||||
|
{
|
||||||
|
if (_configs.ContainsKey((configInfo.OwnerPackage, configInfo.InternalName)))
|
||||||
|
{
|
||||||
|
ret.Errors.Add(new Error($"{nameof(LoadConfigsAsync)}: Config already exists for the compound key {configInfo.OwnerPackage.Name} | {configInfo.InternalName}"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_configTypeInitializers.TryGetValue(configInfo.DataType, out var initializer))
|
||||||
|
{
|
||||||
|
ret.Errors.Add(new Error($"{nameof(LoadConfigsAsync)} No type initializer for {configInfo.DataType}"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var cfg = initializer(configInfo);
|
||||||
|
if (cfg.Errors.Any())
|
||||||
|
ret.Errors.AddRange(cfg.Errors);
|
||||||
|
if (cfg.IsFailed || cfg.Value is not {} val)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
AddConfigInstance((configInfo.OwnerPackage, configInfo.InternalName), val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<FluentResults.Result> LoadConfigsProfilesAsync(ImmutableArray<IConfigProfileResourceInfo> configProfileResources)
|
||||||
|
{
|
||||||
|
using var lck = await _disposeOpsLock.AcquireReaderLock();
|
||||||
|
_base.CheckDisposed();
|
||||||
|
|
||||||
|
if (configProfileResources.IsDefaultOrEmpty)
|
||||||
|
return FluentResults.Result.Fail($"{nameof(LoadConfigsProfilesAsync)}: Array is empty.");
|
||||||
|
|
||||||
|
var results = await _configProfileResourceConverter.TryParseResourcesAsync(configProfileResources);
|
||||||
|
var ret = new FluentResults.Result();
|
||||||
|
|
||||||
|
foreach (var result in results)
|
||||||
|
{
|
||||||
|
if (result.Errors.Any())
|
||||||
|
ret.Errors.AddRange(result.Errors);
|
||||||
|
if (result.IsFailed || result.Value is not { Count: > 0 } res)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (var profileInfo in res)
|
||||||
|
{
|
||||||
|
if (_configProfiles.ContainsKey((profileInfo.OwnerPackage, profileInfo.InternalName)))
|
||||||
|
{
|
||||||
|
ret.Errors.Add(new Error($"{nameof(LoadConfigsProfilesAsync)}: Config already exists for the compound key {profileInfo.OwnerPackage.Name} | {profileInfo.InternalName}"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddProfileInstance((profileInfo.OwnerPackage, profileInfo.InternalName), profileInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FluentResults.Result<TConfig> AddConfig<TConfig>(IConfigInfo configInfo) where TConfig : IConfigBase
|
||||||
|
{
|
||||||
|
using var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult();
|
||||||
|
_base.CheckDisposed();
|
||||||
|
|
||||||
|
if (configInfo is null)
|
||||||
|
return FluentResults.Result.Fail($"{nameof(AddConfig)}: Config is null.");
|
||||||
|
|
||||||
|
if (!_configTypeInitializers.TryGetValue(configInfo.DataType, out var initializer))
|
||||||
|
return FluentResults.Result.Fail($"{nameof(AddConfig)}: No type initializer for {configInfo.DataType}");
|
||||||
|
|
||||||
|
var errList = new List<IError>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var cfg = initializer(configInfo);
|
||||||
|
if (cfg.Errors.Any())
|
||||||
|
errList.AddRange(cfg.Errors);
|
||||||
|
if (cfg.IsFailed || cfg.Value is null)
|
||||||
|
return FluentResults.Result.Fail($"Failed to initialize {configInfo.DataType}").WithErrors(errList);
|
||||||
|
AddConfigInstance((configInfo.OwnerPackage, configInfo.InternalName), cfg.Value);
|
||||||
|
return (TConfig)cfg.Value;
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
return FluentResults.Result.Fail($"Failed to initialize {configInfo.DataType}").WithError(new ExceptionalError(ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FluentResults.Result ApplyProfileSettings(ContentPackage package, string profileName)
|
||||||
|
{
|
||||||
|
using var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult();
|
||||||
|
_base.CheckDisposed();
|
||||||
|
|
||||||
|
if (package == null || string.IsNullOrEmpty(profileName))
|
||||||
|
return FluentResults.Result.Fail($"{nameof(ApplyProfileSettings)}: ContentPackage and/or name were null or empty.");
|
||||||
|
|
||||||
|
if (!_configProfiles.TryGetValue((package, profileName), out var list))
|
||||||
|
return FluentResults.Result.Fail($"No profiles found for package {package.Name} with name {profileName}");
|
||||||
|
|
||||||
|
if (list.IsDefaultOrEmpty)
|
||||||
|
return FluentResults.Result.Fail($"{nameof(ApplyProfileSettings)}: No stored values for profile {profileName}.");
|
||||||
|
|
||||||
|
var errList = new List<IError>();
|
||||||
|
|
||||||
|
foreach (var profileVal in list)
|
||||||
|
{
|
||||||
|
if (!_configs.TryGetValue((package, profileVal.ConfigName), out var val))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!val.TrySetValue(profileVal.Value))
|
||||||
|
errList.Add(new Error($"Failed to apply value from profile named {profileName} to {val.InternalName}"));
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return FluentResults.Result.Ok().WithErrors(errList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FluentResults.Result DisposePackageData(ContentPackage package)
|
||||||
|
{
|
||||||
|
// stop regular ops during deletion ops
|
||||||
|
using var lck = _disposeOpsLock.AcquireWriterLock().GetAwaiter().GetResult();
|
||||||
|
_base.CheckDisposed();
|
||||||
|
|
||||||
|
if (package is null)
|
||||||
|
return FluentResults.Result.Fail($"{nameof(DisposePackageData)}: Package was null.");
|
||||||
|
|
||||||
|
var errList = new List<IError>();
|
||||||
|
|
||||||
|
if (_packageConfigReverseLookup.Remove(package, out var cfgKeys))
|
||||||
|
{
|
||||||
|
if (cfgKeys.Any())
|
||||||
|
{
|
||||||
|
foreach (var key in cfgKeys)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_configs.Remove(key, out var cfg);
|
||||||
|
cfg?.Dispose();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
errList.Add(new ExceptionalError(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_packageProfilesReverseLookup.Remove(package, out var profileKeys))
|
||||||
|
{
|
||||||
|
if (profileKeys.Any())
|
||||||
|
{
|
||||||
|
foreach (var key in profileKeys)
|
||||||
|
{
|
||||||
|
_configProfiles.Remove(key, out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_packageNameMap.Remove(package.Name, out _);
|
||||||
|
|
||||||
|
return FluentResults.Result.Ok().WithErrors(errList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<IReadOnlyDictionary<(ContentPackage Package, string ConfigName), IConfigBase>> GetConfigsForPackage(ContentPackage package)
|
||||||
|
{
|
||||||
|
using var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult();
|
||||||
|
_base.CheckDisposed();
|
||||||
|
|
||||||
|
if (!_packageConfigReverseLookup.TryGetValue(package, out var keys) || keys.IsEmpty)
|
||||||
|
return FluentResults.Result.Fail($"No configs found for package {package.Name}");
|
||||||
|
|
||||||
|
return _configs.Where(kvp => keys.Contains(kvp.Key)).ToFrozenDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<IReadOnlyDictionary<(ContentPackage Package, string ConfigName), ImmutableArray<(string ConfigName, OneOf<string, XElement> Value)>>> GetProfilesForPackage(ContentPackage package)
|
||||||
|
{
|
||||||
|
using var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult();
|
||||||
|
_base.CheckDisposed();
|
||||||
|
|
||||||
|
if (!_packageProfilesReverseLookup.TryGetValue(package, out var keys) || keys.IsEmpty)
|
||||||
|
return FluentResults.Result.Fail($"No profiles found for package {package.Name}");
|
||||||
|
|
||||||
|
return _configProfiles.Where(kvp => keys.Contains(kvp.Key)).ToFrozenDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<(ContentPackage Package, string Name), IConfigBase> GetAllConfigs()
|
||||||
|
{
|
||||||
|
using var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult();
|
||||||
|
_base.CheckDisposed();
|
||||||
|
|
||||||
|
return _configs.ToFrozenDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetConfig<T>(ContentPackage package, string name, out T config) where T : IConfigBase
|
||||||
|
{
|
||||||
|
using var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult();
|
||||||
|
_base.CheckDisposed();
|
||||||
|
|
||||||
|
config = default;
|
||||||
|
if (!_configs.TryGetValue((package, name), out var value))
|
||||||
|
return false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
config = (T)value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<FluentResults.Result> SaveAllConfigs()
|
||||||
|
{
|
||||||
|
using var lck = await _disposeOpsLock.AcquireReaderLock();
|
||||||
|
_base.CheckDisposed();
|
||||||
|
if (_configs.IsEmpty)
|
||||||
|
return FluentResults.Result.Ok();
|
||||||
|
var toSave = _configs.Where(kvp => kvp.Value is not null).Select(kvp => kvp.Value)
|
||||||
|
.ToImmutableArray();
|
||||||
|
var errList = ImmutableArray.CreateBuilder<IError>();
|
||||||
|
foreach (var config in toSave)
|
||||||
|
{
|
||||||
|
var res = await SaveConfigInternal(config);
|
||||||
|
if (res.Errors.Any())
|
||||||
|
errList.AddRange(res.Errors);
|
||||||
|
}
|
||||||
|
return FluentResults.Result.Ok().WithErrors(errList.MoveToImmutable());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<FluentResults.Result> SaveConfigsForPackage(ContentPackage package)
|
||||||
|
{
|
||||||
|
if (package is null)
|
||||||
|
return FluentResults.Result.Fail($"{nameof(SaveConfigsForPackage)}: Package was null.");
|
||||||
|
using var lck = await _disposeOpsLock.AcquireReaderLock();
|
||||||
|
_base.CheckDisposed();
|
||||||
|
if (!_packageConfigReverseLookup.TryGetValue(package, out var keys) || keys.IsEmpty)
|
||||||
|
return FluentResults.Result.Fail($"No configs found for package {package.Name}");
|
||||||
|
ConcurrentQueue<IConfigBase> toSave = new();
|
||||||
|
foreach (var key in keys)
|
||||||
|
{
|
||||||
|
if (_configs.TryGetValue(key, out var config))
|
||||||
|
toSave.Enqueue(config);
|
||||||
|
}
|
||||||
|
if (toSave.IsEmpty)
|
||||||
|
return FluentResults.Result.Fail($"No configs found for package {package.Name}");
|
||||||
|
var errList = ImmutableArray.CreateBuilder<IError>();
|
||||||
|
while (toSave.TryDequeue(out var config))
|
||||||
|
{
|
||||||
|
var res = await SaveConfigInternal(config);
|
||||||
|
if (res.Errors.Any())
|
||||||
|
errList.AddRange(res.Errors);
|
||||||
|
}
|
||||||
|
return FluentResults.Result.Ok().WithErrors(errList.MoveToImmutable());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<FluentResults.Result> SaveConfig((ContentPackage Package, string ConfigName) config)
|
||||||
|
{
|
||||||
|
if (config.Package is null || config.ConfigName.IsNullOrWhiteSpace())
|
||||||
|
return FluentResults.Result.Fail($"{nameof(SaveConfig)}: Config properties were null or empty.");
|
||||||
|
using var lck = await _disposeOpsLock.AcquireReaderLock();
|
||||||
|
_base.CheckDisposed();
|
||||||
|
if (!_configs.TryGetValue(config, out var instance))
|
||||||
|
return FluentResults.Result.Fail($"{nameof(SaveConfig)}: No config found for package {config.Package.Name} and name {config.ConfigName}");
|
||||||
|
return await SaveConfigInternal(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveConfigEvent(IConfigBase instance)
|
||||||
|
{
|
||||||
|
using var lck = _disposeOpsLock.AcquireWriterLock().GetAwaiter().GetResult();
|
||||||
|
_base.CheckDisposed();
|
||||||
|
SaveConfigInternal(instance).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<FluentResults.Result> SaveConfigInternal(IConfigBase instance)
|
||||||
|
{
|
||||||
|
var localStorePath = Path.Combine("Config", SanitizedFileName($"{instance.OwnerPackage.Name}.xml)"));
|
||||||
|
// Locking and checks must be handled by the caller.
|
||||||
|
var val = instance.GetSerializableValue();
|
||||||
|
var docRes = await _storageService.Value.LoadLocalXmlAsync(instance.OwnerPackage, localStorePath);
|
||||||
|
XDocument doc;
|
||||||
|
XElement cfgElement;
|
||||||
|
XElement valueElement;
|
||||||
|
|
||||||
|
// structure is
|
||||||
|
/*
|
||||||
|
* <Config ContentPackage="[PackageName]">
|
||||||
|
* <[instance.InternalName]>
|
||||||
|
* <Value>
|
||||||
|
* <--Contents Here->
|
||||||
|
* </Value>
|
||||||
|
* </[instance.InternalName]>
|
||||||
|
* </Config>
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (docRes.IsFailed || docRes.Value is null)
|
||||||
|
{
|
||||||
|
doc = new XDocument(
|
||||||
|
new XElement("Config", new XAttribute("ContentPackage", instance.OwnerPackage.Name),
|
||||||
|
cfgElement = new XElement(instance.InternalName, valueElement = new XElement("Value"))));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
doc = docRes.Value;
|
||||||
|
var e1 = doc.GetChildElement("Config");
|
||||||
|
if (e1 is null)
|
||||||
|
{
|
||||||
|
e1 = new XElement("Config");
|
||||||
|
doc.Add(e1);
|
||||||
|
}
|
||||||
|
|
||||||
|
cfgElement = e1.GetChildElement(instance.InternalName);
|
||||||
|
if (cfgElement is null)
|
||||||
|
{
|
||||||
|
cfgElement = new XElement(instance.InternalName);
|
||||||
|
e1.Add(cfgElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
valueElement = cfgElement.GetChildElement("Value");
|
||||||
|
if (valueElement is null)
|
||||||
|
{
|
||||||
|
valueElement = new XElement("Value");
|
||||||
|
cfgElement.Add(valueElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
valueElement.Remove(); // remove from cfg
|
||||||
|
|
||||||
|
// get potential updated element
|
||||||
|
var updatedElement = val.Match<XElement>(str =>
|
||||||
|
{
|
||||||
|
valueElement.RemoveAll();
|
||||||
|
valueElement.Value = str;
|
||||||
|
return valueElement;
|
||||||
|
}, element =>
|
||||||
|
{
|
||||||
|
valueElement.RemoveAll();
|
||||||
|
valueElement.Add(element);
|
||||||
|
return valueElement;
|
||||||
|
});
|
||||||
|
|
||||||
|
// (re) add updated element.
|
||||||
|
cfgElement.Add(updatedElement);
|
||||||
|
|
||||||
|
return await _storageService.Value.SaveLocalXmlAsync(instance.OwnerPackage, localStorePath, doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly Regex RemoveInvalidChars = new Regex($"[{Regex.Escape(new string(System.IO.Path.GetInvalidFileNameChars()))}]",
|
||||||
|
RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);
|
||||||
|
|
||||||
|
private string SanitizedFileName(string fileName, string replacement = "_")
|
||||||
|
{
|
||||||
|
return RemoveInvalidChars.Replace(fileName, replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,6 +327,16 @@ public sealed class ContentPackageInfoLookup : IPackageInfoLookupService, IEvent
|
|||||||
).GetAwaiter().GetResult();
|
).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsPackageEnabled(ContentPackage package)
|
||||||
|
{
|
||||||
|
if (package is null)
|
||||||
|
return false;
|
||||||
|
using (_packageSetsLock.AcquireReaderLock().GetAwaiter().GetResult())
|
||||||
|
{
|
||||||
|
return _enabledPackages.Contains(package);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Result<IPackageInfo>> Lookup(string packageName)
|
public async Task<Result<IPackageInfo>> Lookup(string packageName)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Barotrauma.LuaCs.Services;
|
|
||||||
|
|
||||||
public interface LocalizationService
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
+204
-1
@@ -2,13 +2,216 @@
|
|||||||
using MoonSharp.Interpreter;
|
using MoonSharp.Interpreter;
|
||||||
using MoonSharp.Interpreter.Interop;
|
using MoonSharp.Interpreter.Interop;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Barotrauma.LuaCs.Services.Safe;
|
||||||
|
using FluentResults;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using MoonSharp.Interpreter.Loaders;
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Services;
|
namespace Barotrauma.LuaCs.Services;
|
||||||
|
|
||||||
public class LuaScriptManagementService : ILuaScriptManagementService
|
public class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService
|
||||||
{
|
{
|
||||||
|
public LuaScriptManagementService(ILuaScriptLoader loader, ILuaScriptServicesConfig luaScriptServicesConfig)
|
||||||
|
{
|
||||||
|
_luaScriptLoader = loader;
|
||||||
|
_luaScriptServicesConfig = luaScriptServicesConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly ILuaScriptLoader _luaScriptLoader;
|
||||||
|
private readonly ILuaScriptServicesConfig _luaScriptServicesConfig;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_luaScriptLoader.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsDisposed
|
||||||
|
{
|
||||||
|
get => throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FluentResults.Result Reset()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<object> GetGlobalTableValue(string tableName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<FluentResults.Result> LoadScriptResourcesAsync(ImmutableArray<ILuaScriptResourceInfo> resourcesInfo)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FluentResults.Result ExecuteLoadedScriptsForPackage(ContentPackage package)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FluentResults.Result ExecuteLoadedScriptsForPackages(IEnumerable<ContentPackage> packages)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FluentResults.Result ExecuteLoadedScripts()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FluentResults.Result DisposePackageResources(ContentPackage package)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FluentResults.Result UnloadActiveScripts()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FluentResults.Result DisposeAllPackageResources()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUserDataDescriptor RegisterType(Type type)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUserDataDescriptor RegisterGenericType(Type type)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUserDataDescriptor GetTypeInfo(string typeName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUserDataDescriptor GetGenericTypeInfo(string typeName, params string[] typeNameArgs)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnregisterType(Type type)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsRegistered(Type type)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsTargetType(object obj, string typeName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TypeOf(object obj)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public object CreateStatic(string typeName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public object CreateEnumTable(string typeName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldInfo FindFieldRecursively(Type type, string fieldName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MakeFieldAccessible(IUserDataDescriptor descriptor, string fieldName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodInfo FindMethodRecursively(Type type, string methodName, Type[] types = null)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MakeMethodAccessible(IUserDataDescriptor descriptor, string methodName, string[] parameters = null)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PropertyInfo FindPropertyRecursively(Type type, string propertyName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MakePropertyAccessible(IUserDataDescriptor descriptor, string propertyName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddMethod(IUserDataDescriptor descriptor, string methodName, object function)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddField(IUserDataDescriptor descriptor, string fieldName, DynValue value)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveMember(IUserDataDescriptor descriptor, string memberName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasMember(object obj, string memberName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynValue CreateUserDataFromDescriptor(DynValue scriptObject, IUserDataDescriptor descriptor)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynValue CreateUserDataFromType(DynValue scriptObject, Type desiredType)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Table GetObjectTable(object obj, string tableName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Table GetTable(string tableName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Table GetOrCreateObjectTable(object obj, string tableName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Table GetOrCreateTable(string tableName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ internal partial class NetworkingService : INetworkingService
|
|||||||
ReceiveIds
|
ReceiveIds
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dictionary<Guid, INetVar> netVars = new Dictionary<Guid, INetVar>();
|
private Dictionary<Guid, INetworkSyncEntity> netVars = new Dictionary<Guid, INetworkSyncEntity>();
|
||||||
private Dictionary<Guid, NetMessageReceived> netReceives = new Dictionary<Guid, NetMessageReceived>();
|
private Dictionary<Guid, NetMessageReceived> netReceives = new Dictionary<Guid, NetMessageReceived>();
|
||||||
private Dictionary<ushort, Guid> packetToId = new Dictionary<ushort, Guid>();
|
private Dictionary<ushort, Guid> packetToId = new Dictionary<ushort, Guid>();
|
||||||
private Dictionary<Guid, ushort> idToPacket = new Dictionary<Guid, ushort>();
|
private Dictionary<Guid, ushort> idToPacket = new Dictionary<Guid, ushort>();
|
||||||
@@ -46,7 +46,7 @@ internal partial class NetworkingService : INetworkingService
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RegisterNetVar(INetVar netVar)
|
public void RegisterNetVar(INetworkSyncEntity netVar)
|
||||||
{
|
{
|
||||||
netVars[netVar.InstanceId] = netVar;
|
netVars[netVar.InstanceId] = netVar;
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ internal partial class NetworkingService : INetworkingService
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendNetVar(INetVar netVar)
|
public void SendNetVar(INetworkSyncEntity netVar)
|
||||||
{
|
{
|
||||||
if (netVars.ContainsKey(netVar.InstanceId))
|
if (netVars.ContainsKey(netVar.InstanceId))
|
||||||
{
|
{
|
||||||
|
|||||||
+19
-49
@@ -27,7 +27,6 @@ public partial class PackageManagementService : IPackageManagementService
|
|||||||
private readonly IProcessorService<IReadOnlyList<IAssemblyResourceInfo>, IAssembliesResourcesInfo> _assemblyInfoConverter;
|
private readonly IProcessorService<IReadOnlyList<IAssemblyResourceInfo>, IAssembliesResourcesInfo> _assemblyInfoConverter;
|
||||||
private readonly IProcessorService<IReadOnlyList<IConfigResourceInfo>, IConfigsResourcesInfo> _configsInfoConverter;
|
private readonly IProcessorService<IReadOnlyList<IConfigResourceInfo>, IConfigsResourcesInfo> _configsInfoConverter;
|
||||||
private readonly IProcessorService<IReadOnlyList<IConfigProfileResourceInfo>, IConfigProfilesResourcesInfo> _configProfilesConverter;
|
private readonly IProcessorService<IReadOnlyList<IConfigProfileResourceInfo>, IConfigProfilesResourcesInfo> _configProfilesConverter;
|
||||||
private readonly IProcessorService<IReadOnlyList<ILocalizationResourceInfo>, ILocalizationsResourcesInfo> _localizationsConverter;
|
|
||||||
private readonly IProcessorService<IReadOnlyList<ILuaScriptResourceInfo>, ILuaScriptsResourcesInfo> _luaScriptsConverter;
|
private readonly IProcessorService<IReadOnlyList<ILuaScriptResourceInfo>, ILuaScriptsResourcesInfo> _luaScriptsConverter;
|
||||||
|
|
||||||
|
|
||||||
@@ -57,8 +56,6 @@ public partial class PackageManagementService : IPackageManagementService
|
|||||||
return FluentResults.Result.Ok();
|
return FluentResults.Result.Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImmutableArray<ILocalizationResourceInfo> Localizations => _modInfos.IsEmpty ? ImmutableArray<ILocalizationResourceInfo>.Empty
|
|
||||||
: _modInfos.SelectMany(kvp => kvp.Value.Localizations).ToImmutableArray();
|
|
||||||
public ImmutableArray<IConfigResourceInfo> Configs => _modInfos.IsEmpty ? ImmutableArray<IConfigResourceInfo>.Empty
|
public ImmutableArray<IConfigResourceInfo> Configs => _modInfos.IsEmpty ? ImmutableArray<IConfigResourceInfo>.Empty
|
||||||
: _modInfos.SelectMany(kvp => kvp.Value.Configs).ToImmutableArray();
|
: _modInfos.SelectMany(kvp => kvp.Value.Configs).ToImmutableArray();
|
||||||
public ImmutableArray<IConfigProfileResourceInfo> ConfigProfiles => _modInfos.IsEmpty ? ImmutableArray<IConfigProfileResourceInfo>.Empty
|
public ImmutableArray<IConfigProfileResourceInfo> ConfigProfiles => _modInfos.IsEmpty ? ImmutableArray<IConfigProfileResourceInfo>.Empty
|
||||||
@@ -103,6 +100,25 @@ public partial class PackageManagementService : IPackageManagementService
|
|||||||
: _modInfos.Select(kvp => kvp.Key).ToImmutableArray();
|
: _modInfos.Select(kvp => kvp.Key).ToImmutableArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsPackageLoaded(ContentPackage package)
|
||||||
|
{
|
||||||
|
return package is not null && _modInfos.ContainsKey(package);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableArray<T> FilterUnloadableResources<T>(IReadOnlyList<T> resources, bool enabledPackagesOnly = false)
|
||||||
|
where T : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo
|
||||||
|
{
|
||||||
|
return resources
|
||||||
|
.Where(r => r is not null)
|
||||||
|
.Where(r => (r.SupportedTargets & ModUtils.Environment.CurrentTarget) > 0)
|
||||||
|
.Where(r => (r.SupportedPlatforms & ModUtils.Environment.CurrentPlatform) > 0)
|
||||||
|
.Where(r => !r.Dependencies.Any() || r.Dependencies.All(d =>
|
||||||
|
d.Dependency.GetPackage() is {} p // cp is valid
|
||||||
|
&& _modInfos.ContainsKey(p) // cp is parsed
|
||||||
|
&& (!enabledPackagesOnly || _packageInfoLookupService.IsPackageEnabled(p)))) // cp is enabled
|
||||||
|
.ToImmutableArray();
|
||||||
|
}
|
||||||
|
|
||||||
public void DisposePackageInfos(ContentPackage package)
|
public void DisposePackageInfos(ContentPackage package)
|
||||||
{
|
{
|
||||||
_modInfos.TryRemove(package, out _);
|
_modInfos.TryRemove(package, out _);
|
||||||
@@ -217,26 +233,6 @@ public partial class PackageManagementService : IPackageManagementService
|
|||||||
$"{nameof(GetConfigProfilesInfos)}: ContentPackage {package.Name} is not registered.");
|
$"{nameof(GetConfigProfilesInfos)}: ContentPackage {package.Name} is not registered.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<ILocalizationsResourcesInfo> GetLocalizationsInfos(ContentPackage package, bool onlySupportedResources = true)
|
|
||||||
{
|
|
||||||
((IService)this).CheckDisposed();
|
|
||||||
if (package is null)
|
|
||||||
return FluentResults.Result.Fail($"{nameof(GetLocalizationsInfos)}: ContentPackage is null.");
|
|
||||||
|
|
||||||
if (_modInfos.TryGetValue(package, out var result))
|
|
||||||
{
|
|
||||||
return FluentResults.Result.Ok<ILocalizationsResourcesInfo>(_localizationsConverter.Process(onlySupportedResources?
|
|
||||||
result.Localizations.Where(r =>
|
|
||||||
(r.SupportedPlatforms & ModUtils.Environment.CurrentPlatform) > 0
|
|
||||||
&& (r.SupportedTargets & ModUtils.Environment.CurrentTarget) > 0).ToImmutableArray()
|
|
||||||
: result.Localizations
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return FluentResults.Result.Fail(
|
|
||||||
$"{nameof(GetLocalizationsInfos)}: ContentPackage {package.Name} is not registered.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result<ILuaScriptsResourcesInfo> GetLuaScriptsInfos(ContentPackage package, bool onlySupportedResources = true)
|
public Result<ILuaScriptsResourcesInfo> GetLuaScriptsInfos(ContentPackage package, bool onlySupportedResources = true)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
@@ -320,27 +316,6 @@ public partial class PackageManagementService : IPackageManagementService
|
|||||||
return FluentResults.Result.Ok(_configProfilesConverter.Process(builder.MoveToImmutable()));
|
return FluentResults.Result.Ok(_configProfilesConverter.Process(builder.MoveToImmutable()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<ILocalizationsResourcesInfo> GetLocalizationsInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true)
|
|
||||||
{
|
|
||||||
((IService)this).CheckDisposed();
|
|
||||||
if (packages is null || packages.Count == 0)
|
|
||||||
return FluentResults.Result.Fail($"{nameof(GetLocalizationsInfos)}: ContentPackage list is null or empty.");
|
|
||||||
var builder = ImmutableArray.CreateBuilder<ILocalizationResourceInfo>();
|
|
||||||
foreach (var package in packages)
|
|
||||||
{
|
|
||||||
if (_modInfos.TryGetValue(package, out var result) && result.Localizations is { IsEmpty: false })
|
|
||||||
{
|
|
||||||
builder.AddRange(onlySupportedResources?
|
|
||||||
result.Localizations.Where(r =>
|
|
||||||
(r.SupportedPlatforms & ModUtils.Environment.CurrentPlatform) > 0
|
|
||||||
&& (r.SupportedTargets & ModUtils.Environment.CurrentTarget) > 0).ToImmutableArray()
|
|
||||||
: result.Localizations);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return FluentResults.Result.Ok(_localizationsConverter.Process(builder.MoveToImmutable()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result<ILuaScriptsResourcesInfo> GetLuaScriptsInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true)
|
public Result<ILuaScriptsResourcesInfo> GetLuaScriptsInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
@@ -377,11 +352,6 @@ public partial class PackageManagementService : IPackageManagementService
|
|||||||
return await Task.Run(() => GetConfigProfilesInfos(packages, onlySupportedResources));
|
return await Task.Run(() => GetConfigProfilesInfos(packages, onlySupportedResources));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<ILocalizationsResourcesInfo>> GetLocalizationsInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true)
|
|
||||||
{
|
|
||||||
return await Task.Run(() => GetLocalizationsInfos(packages, onlySupportedResources));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Result<ILuaScriptsResourcesInfo>> GetLuaScriptsInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true)
|
public async Task<Result<ILuaScriptsResourcesInfo>> GetLuaScriptsInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true)
|
||||||
{
|
{
|
||||||
return await Task.Run(() => GetLuaScriptsInfos(packages, onlySupportedResources));
|
return await Task.Run(() => GetLuaScriptsInfos(packages, onlySupportedResources));
|
||||||
|
|||||||
+278
@@ -0,0 +1,278 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using Barotrauma.LuaCs.Data;
|
||||||
|
using FarseerPhysics.Common;
|
||||||
|
using FluentResults;
|
||||||
|
using OneOf;
|
||||||
|
|
||||||
|
namespace Barotrauma.LuaCs.Services.Processing;
|
||||||
|
|
||||||
|
public class ConfigIOService : IConfigIOService
|
||||||
|
{
|
||||||
|
private readonly IStorageService _storageService;
|
||||||
|
private readonly IConfigServiceConfig _configServiceConfig;
|
||||||
|
|
||||||
|
public ConfigIOService(IStorageService storageService, IConfigServiceConfig configServiceConfig)
|
||||||
|
{
|
||||||
|
this._storageService = storageService;
|
||||||
|
storageService.UseCaching = true;
|
||||||
|
_configServiceConfig = configServiceConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// stateless service
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateless service
|
||||||
|
public bool IsDisposed => false;
|
||||||
|
public FluentResults.Result Reset()
|
||||||
|
{
|
||||||
|
_storageService.PurgeCache();
|
||||||
|
return FluentResults.Result.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Result<IReadOnlyList<IConfigInfo>>> TryParseResourceAsync(IConfigResourceInfo src)
|
||||||
|
{
|
||||||
|
if (src?.OwnerPackage is null || src.FilePaths.IsDefaultOrEmpty)
|
||||||
|
return FluentResults.Result.Fail($"{nameof(TryParseResourceAsync)}: Config resource and/or components were null.");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var infos = await _storageService.LoadPackageXmlFilesAsync(src.OwnerPackage, src.FilePaths);
|
||||||
|
if (infos.IsDefaultOrEmpty)
|
||||||
|
return FluentResults.Result.Fail($"{nameof(TryParseResourceAsync)}: No resources found.");
|
||||||
|
|
||||||
|
var errList = new List<IError>();
|
||||||
|
|
||||||
|
var resList = infos.Select(info =>
|
||||||
|
{
|
||||||
|
if (info.Item2.Errors.Any())
|
||||||
|
errList.AddRange(info.Item2.Errors);
|
||||||
|
if (info.Item2.IsFailed || info.Item2.Value is not { } configXDoc)
|
||||||
|
{
|
||||||
|
errList.Add(new Error($"Unable to parse file: {info.Item1}"));
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (info.Item1, configXDoc);
|
||||||
|
})
|
||||||
|
.Where(doc => !doc.Item1.IsNullOrWhiteSpace() && doc.configXDoc != null)
|
||||||
|
.SelectMany(doc => doc.configXDoc.Root.GetChildElements("Configuration"))
|
||||||
|
.SelectMany(cfgContainer => cfgContainer.GetChildElements("Configs"))
|
||||||
|
.SelectMany(cfgContainer => cfgContainer.GetChildElements("Config"))
|
||||||
|
.Select(async cfgElement =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OneOf.OneOf<string, XElement> defaultValue = cfgElement.GetChildElement("Value");
|
||||||
|
if (defaultValue.AsT1 is null)
|
||||||
|
defaultValue = cfgElement.GetAttributeString("Value", string.Empty);
|
||||||
|
|
||||||
|
var internalName = cfgElement.GetAttributeString("Name", string.Empty);
|
||||||
|
if (internalName.IsNullOrWhiteSpace())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new ConfigInfo()
|
||||||
|
{
|
||||||
|
DataType = Type.GetType(cfgElement.GetAttributeString("Type", "string")),
|
||||||
|
OwnerPackage = src.OwnerPackage,
|
||||||
|
DefaultValue = defaultValue,
|
||||||
|
Value = await LoadConfigDataFromLocal(src.OwnerPackage, internalName) is { IsSuccess: true } res
|
||||||
|
? res.Value : defaultValue,
|
||||||
|
EditableStates = cfgElement.GetAttributeBool("ReadOnly", false)
|
||||||
|
? RunState.Unloaded // read-only
|
||||||
|
: RunState.Running, // editable at runtime
|
||||||
|
InternalName = internalName,
|
||||||
|
NetSync = Enum.Parse<NetSync>(
|
||||||
|
cfgElement.GetAttributeString("NetSync", nameof(NetSync.None))),
|
||||||
|
#if CLIENT
|
||||||
|
DisplayName = cfgElement.GetAttributeString("DisplayName", null),
|
||||||
|
Description = cfgElement.GetAttributeString("Description", null),
|
||||||
|
DisplayCategory = cfgElement.GetAttributeString("Category", null),
|
||||||
|
ShowInMenus = cfgElement.GetAttributeBool("ShowInMenus", true),
|
||||||
|
Tooltip = cfgElement.GetAttributeString("Tooltip", null),
|
||||||
|
ImageIconPath = cfgElement.GetAttributeString("Image", null)
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
errList.Add(new Error($"Failed to parse config var for package {src.OwnerPackage}"));
|
||||||
|
errList.Add(new ExceptionalError(e));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.Where(task => task is not null)
|
||||||
|
.ToImmutableArray();
|
||||||
|
|
||||||
|
var result = (await Task.WhenAll(resList)).ToImmutableArray();
|
||||||
|
|
||||||
|
var ret = FluentResults.Result.Ok((IReadOnlyList<IConfigInfo>)result);
|
||||||
|
if (errList.Any())
|
||||||
|
ret.Errors.AddRange(errList);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
return FluentResults.Result.Fail($"Failed to parse config resource for package {src.OwnerPackage}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ImmutableArray<Result<IReadOnlyList<IConfigInfo>>>> TryParseResourcesAsync(IEnumerable<IConfigResourceInfo> sources)
|
||||||
|
{
|
||||||
|
var results = new ConcurrentQueue<Result<IReadOnlyList<IConfigInfo>>>();
|
||||||
|
|
||||||
|
var src = sources.ToImmutableArray();
|
||||||
|
if (!src.Any())
|
||||||
|
return ImmutableArray<Result<IReadOnlyList<IConfigInfo>>>.Empty;
|
||||||
|
|
||||||
|
await src.ParallelForEachAsync(async cfg =>
|
||||||
|
{
|
||||||
|
var res = await TryParseResourceAsync(cfg);
|
||||||
|
results.Enqueue(res);
|
||||||
|
}, 2); // we only need 2 parallels to buffer against disk loading.
|
||||||
|
|
||||||
|
return results.ToImmutableArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Result<IReadOnlyList<IConfigProfileInfo>>> TryParseResourceAsync(IConfigProfileResourceInfo src)
|
||||||
|
{
|
||||||
|
if (src?.OwnerPackage is null || src.FilePaths.IsDefaultOrEmpty)
|
||||||
|
return FluentResults.Result.Fail($"{nameof(TryParseResourceAsync)}: Profile resource and/or components were null.");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var infos = await _storageService.LoadPackageXmlFilesAsync(src.OwnerPackage, src.FilePaths);
|
||||||
|
if (infos.IsDefaultOrEmpty)
|
||||||
|
return FluentResults.Result.Fail($"{nameof(TryParseResourceAsync)}: No resources found.");
|
||||||
|
|
||||||
|
var errList = new List<IError>();
|
||||||
|
|
||||||
|
var resList = infos.Select(info =>
|
||||||
|
{
|
||||||
|
if (info.Item2.Errors.Any())
|
||||||
|
errList.AddRange(info.Item2.Errors);
|
||||||
|
if (info.Item2.IsFailed || info.Item2.Value is not { } configXDoc)
|
||||||
|
{
|
||||||
|
errList.Add(new Error($"Unable to parse file: {info.Item1}"));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return configXDoc;
|
||||||
|
})
|
||||||
|
.Where(doc => doc is not null)
|
||||||
|
.SelectMany(doc => doc.Root.GetChildElements("Configuration"))
|
||||||
|
.SelectMany(cfgContainer => cfgContainer.GetChildElements("Profiles"))
|
||||||
|
.SelectMany(cfgContainer => cfgContainer.GetChildElements("Profile"))
|
||||||
|
.Select(cfgElement =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return new ConfigProfileInfo()
|
||||||
|
{
|
||||||
|
OwnerPackage = src.OwnerPackage,
|
||||||
|
InternalName = cfgElement.GetAttributeString("Name", null),
|
||||||
|
ProfileValues = cfgElement.GetChildElements("ConfigValue")
|
||||||
|
.Select<XElement, (string ConfigName, OneOf.OneOf<string, XElement> Value)>(element =>
|
||||||
|
{
|
||||||
|
if (element.GetAttributeString("Name", null) is not { } name)
|
||||||
|
return default;
|
||||||
|
if (element.GetAttributeString("Value", null) is { } value)
|
||||||
|
return (name, value);
|
||||||
|
if (element.GetChildElement("Value") is { } xValue)
|
||||||
|
return (name, xValue);
|
||||||
|
return default;
|
||||||
|
})
|
||||||
|
.Where(val => val.ConfigName is not null && val.Value.Match<bool>(
|
||||||
|
s => !s.IsNullOrWhiteSpace(),
|
||||||
|
element => element is not null))
|
||||||
|
.ToList()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
errList.Add(new Error($"Failed to parse profile var for package {src.OwnerPackage}"));
|
||||||
|
errList.Add(new ExceptionalError(e));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.Where(cfgInfo => cfgInfo != null && !cfgInfo.InternalName.IsNullOrWhiteSpace())
|
||||||
|
.ToImmutableArray();
|
||||||
|
|
||||||
|
var ret = FluentResults.Result.Ok((IReadOnlyList<IConfigProfileInfo>)resList);
|
||||||
|
if (errList.Any())
|
||||||
|
ret.Errors.AddRange(errList);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
return FluentResults.Result.Fail($"Failed to parse profile resource for package {src.OwnerPackage}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ImmutableArray<Result<IReadOnlyList<IConfigProfileInfo>>>> TryParseResourcesAsync(IEnumerable<IConfigProfileResourceInfo> sources)
|
||||||
|
{
|
||||||
|
var results = new ConcurrentQueue<Result<IReadOnlyList<IConfigProfileInfo>>>();
|
||||||
|
|
||||||
|
var src = sources.ToImmutableArray();
|
||||||
|
if (!src.Any())
|
||||||
|
return ImmutableArray<Result<IReadOnlyList<IConfigProfileInfo>>>.Empty;
|
||||||
|
|
||||||
|
await src.ParallelForEachAsync(async cfg =>
|
||||||
|
{
|
||||||
|
var res = await TryParseResourceAsync(cfg);
|
||||||
|
results.Enqueue(res);
|
||||||
|
}, 2); // we only need 2 parallels to buffer against disk loading.
|
||||||
|
|
||||||
|
return results.ToImmutableArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly Regex RemoveInvalidChars = new Regex($"[{Regex.Escape(new string(System.IO.Path.GetInvalidFileNameChars()))}]",
|
||||||
|
RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);
|
||||||
|
|
||||||
|
private string SanitizedFileName(string fileName, string replacement = "_")
|
||||||
|
{
|
||||||
|
return RemoveInvalidChars.Replace(fileName, replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<FluentResults.Result> SaveConfigDataLocal(ContentPackage package, string configName, XElement serializedValue)
|
||||||
|
{
|
||||||
|
if (package is null || package.Name.IsNullOrWhiteSpace() || configName.IsNullOrWhiteSpace() || serializedValue is null)
|
||||||
|
return FluentResults.Result.Fail($"{nameof(SaveConfigDataLocal)}: Argument(s) were null");
|
||||||
|
|
||||||
|
var res = await LoadPackageConfigDocInternal(package);
|
||||||
|
|
||||||
|
throw new NotImplementedException(); //TODO: Complete once the locally saved data structure is finalized.
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Result<OneOf<string, XElement>>> LoadConfigDataFromLocal(ContentPackage package, string configName)
|
||||||
|
{
|
||||||
|
if (package is null || package.Name.IsNullOrWhiteSpace() || configName.IsNullOrWhiteSpace())
|
||||||
|
return FluentResults.Result.Fail($"{nameof(LoadConfigDataFromLocal)}: Argument(s) were null");
|
||||||
|
|
||||||
|
var filePath = _configServiceConfig.LocalConfigPathPartial.Replace(
|
||||||
|
_configServiceConfig.FileNamePattern,
|
||||||
|
$"{SanitizedFileName(package.Name)}.xml");
|
||||||
|
|
||||||
|
var res = await _storageService.LoadLocalXmlAsync(package, filePath);
|
||||||
|
|
||||||
|
throw new NotImplementedException(); //TODO: Complete once the locally saved data structure is finalized.
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<FluentResults.Result<XDocument>> LoadPackageConfigDocInternal(ContentPackage package)
|
||||||
|
{
|
||||||
|
var filePath = _configServiceConfig.LocalConfigPathPartial.Replace(
|
||||||
|
_configServiceConfig.FileNamePattern,
|
||||||
|
$"{SanitizedFileName(package.Name)}.xml");
|
||||||
|
|
||||||
|
throw new NotImplementedException(); //TODO: Complete once the locally saved data structure is finalized.
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using Barotrauma.LuaCs.Data;
|
||||||
|
using Barotrauma.LuaCs.Services.Processing;
|
||||||
|
|
||||||
|
namespace Barotrauma.LuaCs.Services.Processing;
|
||||||
|
|
||||||
|
public interface IConfigIOService : IReusableService,
|
||||||
|
IConverterServiceAsync<IConfigResourceInfo, IReadOnlyList<IConfigInfo>>,
|
||||||
|
IConverterServiceAsync<IConfigProfileResourceInfo, IReadOnlyList<IConfigProfileInfo>>
|
||||||
|
{
|
||||||
|
Task<FluentResults.Result> SaveConfigDataLocal(ContentPackage package, string configName, XElement serializedValue);
|
||||||
|
Task<FluentResults.Result<OneOf.OneOf<string, XElement>>> LoadConfigDataFromLocal(ContentPackage package, string configName);
|
||||||
|
}
|
||||||
+13
-44
@@ -69,12 +69,8 @@ public partial class ModConfigService : IConverterServiceAsync<ContentPackage, I
|
|||||||
LuaScripts = lua,
|
LuaScripts = lua,
|
||||||
Configs = ImmutableArray<IConfigResourceInfo>.Empty,
|
Configs = ImmutableArray<IConfigResourceInfo>.Empty,
|
||||||
ConfigProfiles = ImmutableArray<IConfigProfileResourceInfo>.Empty,
|
ConfigProfiles = ImmutableArray<IConfigProfileResourceInfo>.Empty,
|
||||||
Localizations = ImmutableArray<ILocalizationResourceInfo>.Empty,
|
|
||||||
Package = src,
|
Package = src,
|
||||||
PackageName = src.Name
|
PackageName = src.Name
|
||||||
#if CLIENT
|
|
||||||
,Styles = ImmutableArray<IStylesResourceInfo>.Empty
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -85,40 +81,6 @@ public partial class ModConfigService : IConverterServiceAsync<ContentPackage, I
|
|||||||
|
|
||||||
private partial Task<Result<IModConfigInfo>> GetModConfigInfoAsync(ContentPackage package, XElement root);
|
private partial Task<Result<IModConfigInfo>> GetModConfigInfoAsync(ContentPackage package, XElement root);
|
||||||
|
|
||||||
private ImmutableArray<ILocalizationResourceInfo> GetLocalizations(ContentPackage src, IEnumerable<XElement> elements)
|
|
||||||
{
|
|
||||||
var builder = ImmutableArray.CreateBuilder<ILocalizationResourceInfo>();
|
|
||||||
|
|
||||||
if (GetXmlFilesList(src, elements, "Localizations")
|
|
||||||
is not { IsSuccess: true, Value: { } xmlFiles })
|
|
||||||
return ImmutableArray<ILocalizationResourceInfo>.Empty;
|
|
||||||
|
|
||||||
foreach (var file in xmlFiles)
|
|
||||||
{
|
|
||||||
// get dependencies
|
|
||||||
var deps = GetElementsDependenciesData(file.Item1, src);
|
|
||||||
// get platform, culture and target architecture
|
|
||||||
var info = GetElementsAttributesData(file.Item1, file.Item2.First());
|
|
||||||
|
|
||||||
builder.Add(new LocalizationResourceInfo()
|
|
||||||
{
|
|
||||||
Dependencies = deps,
|
|
||||||
Optional = info.IsOptional,
|
|
||||||
FilePaths = file.Item2,
|
|
||||||
InternalName = info.Name,
|
|
||||||
LoadPriority = info.LoadPriority,
|
|
||||||
OwnerPackage = src,
|
|
||||||
SupportedCultures = info.SupportedCultures,
|
|
||||||
SupportedPlatforms = info.SupportedPlatforms,
|
|
||||||
SupportedTargets = info.SupportedTargets
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.Count > 0
|
|
||||||
? builder.ToImmutable()
|
|
||||||
: ImmutableArray<ILocalizationResourceInfo>.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ImmutableArray<IAssemblyResourceInfo> GetAssemblies(ContentPackage src, IEnumerable<XElement> elements)
|
private ImmutableArray<IAssemblyResourceInfo> GetAssemblies(ContentPackage src, IEnumerable<XElement> elements)
|
||||||
{
|
{
|
||||||
var builder = ImmutableArray.CreateBuilder<IAssemblyResourceInfo>();
|
var builder = ImmutableArray.CreateBuilder<IAssemblyResourceInfo>();
|
||||||
@@ -265,7 +227,7 @@ public partial class ModConfigService : IConverterServiceAsync<ContentPackage, I
|
|||||||
// get platform, culture and target architecture
|
// get platform, culture and target architecture
|
||||||
var info = GetElementsAttributesData(file.Item1, file.Item2.First());
|
var info = GetElementsAttributesData(file.Item1, file.Item2.First());
|
||||||
|
|
||||||
builder.Add(new LuaScriptScriptResourceInfo()
|
builder.Add(new LuaScriptsResourceInfo()
|
||||||
{
|
{
|
||||||
Dependencies = deps,
|
Dependencies = deps,
|
||||||
Optional = info.IsOptional,
|
Optional = info.IsOptional,
|
||||||
@@ -540,8 +502,15 @@ public partial class ModConfigService : IConverterServiceAsync<ContentPackage, I
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var sharedFound = _storageService.FindFilesInPackage(src, "CSharp/Shared", "*.cs", true)
|
var sharedCsBuilder = ImmutableArray.CreateBuilder<string>();
|
||||||
is { IsSuccess: true, Value: { IsDefaultOrEmpty: false } filesCssShared };
|
if (_storageService.FindFilesInPackage(src, "CSharp/Shared", "*.cs", true)
|
||||||
|
is { IsSuccess: true, Value: { IsDefaultOrEmpty: false } files })
|
||||||
|
{
|
||||||
|
sharedCsBuilder.AddRange<string>(files);
|
||||||
|
}
|
||||||
|
|
||||||
|
var filesCssShared = sharedCsBuilder.MoveToImmutable();
|
||||||
|
var sharedFound = !filesCssShared.IsDefaultOrEmpty;
|
||||||
|
|
||||||
// source files legacy: server
|
// source files legacy: server
|
||||||
if (_storageService.FindFilesInPackage(src, "CSharp/Server", "*.cs", true)
|
if (_storageService.FindFilesInPackage(src, "CSharp/Server", "*.cs", true)
|
||||||
@@ -550,7 +519,7 @@ public partial class ModConfigService : IConverterServiceAsync<ContentPackage, I
|
|||||||
builder.Add(new AssemblyResourceInfo()
|
builder.Add(new AssemblyResourceInfo()
|
||||||
{
|
{
|
||||||
Dependencies = ImmutableArray<IPackageDependency>.Empty,
|
Dependencies = ImmutableArray<IPackageDependency>.Empty,
|
||||||
FilePaths = sharedFound ? filesCssServer.Concat(filesCssShared).ToImmutableArray() : filesCssServer,
|
FilePaths = sharedFound ? filesCssServer.Concat(filesCssShared).ToImmutableArray() : filesCssServer,
|
||||||
FriendlyName = "CssServer",
|
FriendlyName = "CssServer",
|
||||||
InternalName = "CssServer",
|
InternalName = "CssServer",
|
||||||
IsScript = true,
|
IsScript = true,
|
||||||
@@ -594,7 +563,7 @@ public partial class ModConfigService : IConverterServiceAsync<ContentPackage, I
|
|||||||
if (_storageService.FindFilesInPackage(src, "Lua", "*.lua", true)
|
if (_storageService.FindFilesInPackage(src, "Lua", "*.lua", true)
|
||||||
is { IsSuccess: true, Value: { IsDefaultOrEmpty: false } fileAll })
|
is { IsSuccess: true, Value: { IsDefaultOrEmpty: false } fileAll })
|
||||||
{
|
{
|
||||||
builder.Add(new LuaScriptScriptResourceInfo()
|
builder.Add(new LuaScriptsResourceInfo()
|
||||||
{
|
{
|
||||||
Dependencies = ImmutableArray<IPackageDependency>.Empty,
|
Dependencies = ImmutableArray<IPackageDependency>.Empty,
|
||||||
FilePaths = fileAll.Where(path => !path.Contains("Autorun")).ToImmutableArray(),
|
FilePaths = fileAll.Where(path => !path.Contains("Autorun")).ToImmutableArray(),
|
||||||
@@ -607,7 +576,7 @@ public partial class ModConfigService : IConverterServiceAsync<ContentPackage, I
|
|||||||
SupportedTargets = Target.Client | Target.Server
|
SupportedTargets = Target.Client | Target.Server
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Add(new LuaScriptScriptResourceInfo()
|
builder.Add(new LuaScriptsResourceInfo()
|
||||||
{
|
{
|
||||||
Dependencies = ImmutableArray<IPackageDependency>.Empty,
|
Dependencies = ImmutableArray<IPackageDependency>.Empty,
|
||||||
FilePaths = fileAll.Where(path => path.Contains("Autorun")).ToImmutableArray(),
|
FilePaths = fileAll.Where(path => path.Contains("Autorun")).ToImmutableArray(),
|
||||||
|
|||||||
-6
@@ -9,7 +9,6 @@ public partial class ResourceInfoArrayPacker :
|
|||||||
IProcessorService<IReadOnlyList<IAssemblyResourceInfo>, IAssembliesResourcesInfo>,
|
IProcessorService<IReadOnlyList<IAssemblyResourceInfo>, IAssembliesResourcesInfo>,
|
||||||
IProcessorService<IReadOnlyList<IConfigResourceInfo>, IConfigsResourcesInfo>,
|
IProcessorService<IReadOnlyList<IConfigResourceInfo>, IConfigsResourcesInfo>,
|
||||||
IProcessorService<IReadOnlyList<IConfigProfileResourceInfo>, IConfigProfilesResourcesInfo>,
|
IProcessorService<IReadOnlyList<IConfigProfileResourceInfo>, IConfigProfilesResourcesInfo>,
|
||||||
IProcessorService<IReadOnlyList<ILocalizationResourceInfo>, ILocalizationsResourcesInfo>,
|
|
||||||
IProcessorService<IReadOnlyList<ILuaScriptResourceInfo>, ILuaScriptsResourcesInfo>
|
IProcessorService<IReadOnlyList<ILuaScriptResourceInfo>, ILuaScriptsResourcesInfo>
|
||||||
{
|
{
|
||||||
private bool _isDisposed;
|
private bool _isDisposed;
|
||||||
@@ -28,11 +27,6 @@ public partial class ResourceInfoArrayPacker :
|
|||||||
return new ConfigProfilesResourcesInfo(src.ToImmutableArray());
|
return new ConfigProfilesResourcesInfo(src.ToImmutableArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ILocalizationsResourcesInfo Process(IReadOnlyList<ILocalizationResourceInfo> src)
|
|
||||||
{
|
|
||||||
return new LocalizationResourcesInfo(src.ToImmutableArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ILuaScriptsResourcesInfo Process(IReadOnlyList<ILuaScriptResourceInfo> src)
|
public ILuaScriptsResourcesInfo Process(IReadOnlyList<ILuaScriptResourceInfo> src)
|
||||||
{
|
{
|
||||||
return new LuaScriptsResourcesInfo(src.ToImmutableArray());
|
return new LuaScriptsResourcesInfo(src.ToImmutableArray());
|
||||||
|
|||||||
@@ -1,8 +1,32 @@
|
|||||||
using Barotrauma.LuaCs.Configuration;
|
using System.Collections.Generic;
|
||||||
|
using Barotrauma.LuaCs.Configuration;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Services.Safe;
|
namespace Barotrauma.LuaCs.Services.Safe;
|
||||||
|
|
||||||
public interface ILuaConfigService : ILuaService
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ public interface ILuaDataService : ILuaService
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns stored table data for the given object or creates a new table if one doesn't exist.
|
/// Returns stored table data for the given object or creates a new table if one doesn't exist.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>Note: tables are stored using weak references and will be automatically deleted when the object is
|
||||||
|
/// garbage collected.</remarks>
|
||||||
/// <param name="obj"></param>
|
/// <param name="obj"></param>
|
||||||
/// <param name="tableName"></param>
|
/// <param name="tableName"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using MoonSharp.Interpreter.Loaders;
|
||||||
|
|
||||||
|
namespace Barotrauma.LuaCs.Services.Safe;
|
||||||
|
|
||||||
|
public interface ILuaScriptLoader : IService, IScriptLoader
|
||||||
|
{
|
||||||
|
void ClearCaches();
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
|
||||||
|
namespace Barotrauma.LuaCs.Services.Safe;
|
||||||
|
|
||||||
|
public interface ISafeStorageService : IStorageService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Checks the given file path to see if it can be read. This includes any permissions, whitelists and OS checks.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The absolute path to the file.</param>
|
||||||
|
/// <param name="readOnly">Whether to only check for read permissions only, or full RWM if false.</param>
|
||||||
|
/// <param name="checkWhitelistOnly">Whether to only check if the file is safe to access, without checking accessibility at the OS level.</param>
|
||||||
|
/// <returns>Whether the file is accessible.</returns>
|
||||||
|
bool IsFileAccessible(string path, bool readOnly, bool checkWhitelistOnly = true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the given path to the specified whitelists.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Either the fully-qualified or local reference path to the given file.</param>
|
||||||
|
/// <param name="readOnly"></param>
|
||||||
|
void AddFileToWhitelist(string path, bool readOnly = true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the given path from all whitelists (Read|Write).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path"></param>
|
||||||
|
void RemoveFileFromAllWhitelists(string path);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the whitelist filtering for read-only file permissions for the instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePaths">List of absolute file paths allowed.</param>
|
||||||
|
FluentResults.Result SetReadOnlyWhitelist(ImmutableArray<string> filePaths);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the whitelist filtering for read & write file permissions for the instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePaths">List of absolute file paths allowed.</param>
|
||||||
|
FluentResults.Result SetReadWriteWhitelist(ImmutableArray<string> filePaths);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes all paths from all white lists.
|
||||||
|
/// </summary>
|
||||||
|
void ClearAllWhitelists();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the service instance is in file read-only mode.
|
||||||
|
/// </summary>
|
||||||
|
bool IsReadOnlyMode { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the service into file read-only mode. Cannot be undone.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool EnableReadOnlyMode();
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.IO;
|
||||||
|
using MoonSharp.Interpreter;
|
||||||
|
using MoonSharp.Interpreter.Loaders;
|
||||||
|
using System.Linq;
|
||||||
|
using Barotrauma.LuaCs.Data;
|
||||||
|
using Barotrauma.LuaCs.Services.Safe;
|
||||||
|
|
||||||
|
namespace Barotrauma.LuaCs.Services.Safe
|
||||||
|
{
|
||||||
|
public class LuaScriptLoader : ScriptLoaderBase, ILuaScriptLoader
|
||||||
|
{
|
||||||
|
public LuaScriptLoader(IStorageService storageService, Lazy<ILoggerService> loggerService, ILuaScriptServicesConfig luaScriptServicesConfig)
|
||||||
|
{
|
||||||
|
this._storageService = storageService;
|
||||||
|
this._loggerService = loggerService;
|
||||||
|
this._luaScriptServicesConfig = luaScriptServicesConfig;
|
||||||
|
_storageService.UseCaching = _luaScriptServicesConfig.UseCaching;
|
||||||
|
if (_luaScriptServicesConfig.SafeLuaIOEnabled)
|
||||||
|
{
|
||||||
|
//_storageService.EnableWhitelistOnly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly IStorageService _storageService;
|
||||||
|
private readonly Lazy<ILoggerService> _loggerService;
|
||||||
|
private readonly ILuaScriptServicesConfig _luaScriptServicesConfig;
|
||||||
|
|
||||||
|
public override object LoadFile(string file, Table globalContext)
|
||||||
|
{
|
||||||
|
((IService)this).CheckDisposed();
|
||||||
|
|
||||||
|
if (!CanReadFromPath(file))
|
||||||
|
{
|
||||||
|
LogErrors<string>($"File access to \"{file}\" is not allowed.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_storageService.TryLoadText(file) is not { IsSuccess: true, Value: not null } script)
|
||||||
|
{
|
||||||
|
LogErrors<string>($"Failed to load file \"{file}\".");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (script.Value.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
LogErrors<string>($"The file \"{file}\" was empty.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return script.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearCaches()
|
||||||
|
{
|
||||||
|
((IService)this).CheckDisposed();
|
||||||
|
_storageService?.PurgeCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ScriptFileExists(string file)
|
||||||
|
{
|
||||||
|
((IService)this).CheckDisposed();
|
||||||
|
|
||||||
|
if (!CanReadFromPath(file))
|
||||||
|
{
|
||||||
|
LogErrors<string>($"File access to \"{file}\" is not allowed.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _storageService.FileExists(file);
|
||||||
|
|
||||||
|
if (result is { IsFailed: true })
|
||||||
|
{
|
||||||
|
LogErrors<string>($"Unable to find and load file \"{file}\".");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.IsSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanReadFromPath(string file)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanWriteToPath(string file)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogErrors<T>(string message, FluentResults.Result<T> result = null)
|
||||||
|
{
|
||||||
|
_loggerService.Value.LogError($"{nameof(LuaScriptLoader)}: {message}");
|
||||||
|
|
||||||
|
if (result is null || result.Errors.Count <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var error in result.Errors)
|
||||||
|
{
|
||||||
|
_loggerService.Value.LogError($"{nameof(LuaScriptLoader)}: Error: {error.Message}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (IsDisposed)
|
||||||
|
return;
|
||||||
|
IsDisposed = true;
|
||||||
|
|
||||||
|
_storageService?.Dispose();
|
||||||
|
_loggerService?.Value.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsDisposed { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Barotrauma.IO;
|
||||||
|
using Barotrauma.LuaCs.Data;
|
||||||
|
using FarseerPhysics.Common;
|
||||||
|
using FluentResults;
|
||||||
|
|
||||||
|
namespace Barotrauma.LuaCs.Services.Safe;
|
||||||
|
|
||||||
|
public class SafeStorageService : StorageService, ISafeStorageService
|
||||||
|
{
|
||||||
|
private ConcurrentDictionary<string, byte> _fileListRead = new (), _fileListReadWrite = new();
|
||||||
|
|
||||||
|
public SafeStorageService(IStorageServiceConfig configData) : base(configData)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetFullPath(string path) => System.IO.Path.GetFullPath(path).CleanUpPathCrossPlatform();
|
||||||
|
|
||||||
|
public bool IsFileAccessible(string path, bool readOnly, bool checkWhitelistOnly = true)
|
||||||
|
{
|
||||||
|
((IService)this).CheckDisposed();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
path = GetFullPath(path);
|
||||||
|
if (!readOnly && IsReadOnlyMode)
|
||||||
|
return false;
|
||||||
|
if (readOnly)
|
||||||
|
{
|
||||||
|
if (!_fileListRead.ContainsKey(path))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!_fileListReadWrite.ContainsKey(path))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (checkWhitelistOnly)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
using var fs = System.IO.File.Open(
|
||||||
|
path, FileMode.Open, readOnly ? FileAccess.Read : FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddFileToWhitelist(string path, bool readOnly = true)
|
||||||
|
{
|
||||||
|
((IService)this).CheckDisposed();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
path = GetFullPath(path);
|
||||||
|
_fileListRead.AddOrUpdate(path, s => 0, (s, b) => 0);
|
||||||
|
if (!readOnly && !IsReadOnlyMode)
|
||||||
|
_fileListRead.AddOrUpdate(path, s => 0, (s, b) => 0);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveFileFromAllWhitelists(string path)
|
||||||
|
{
|
||||||
|
((IService)this).CheckDisposed();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
path = GetFullPath(path);
|
||||||
|
_fileListRead.TryRemove(path, out _);
|
||||||
|
_fileListReadWrite.TryRemove(path, out _);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FluentResults.Result SetReadOnlyWhitelist(ImmutableArray<string> filePaths)
|
||||||
|
{
|
||||||
|
((IService)this).CheckDisposed();
|
||||||
|
if (filePaths.IsDefaultOrEmpty)
|
||||||
|
return FluentResults.Result.Fail($"{nameof(SetReadOnlyWhitelist)}: FilePaths cannot be empty.");
|
||||||
|
var res = new FluentResults.Result();
|
||||||
|
foreach (var path in filePaths)
|
||||||
|
{
|
||||||
|
// TODO: Cleanup path and add it.
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FluentResults.Result SetReadWriteWhitelist(ImmutableArray<string> filePaths)
|
||||||
|
{
|
||||||
|
((IService)this).CheckDisposed();
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearAllWhitelists()
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _isReadOnlyMode = 0;
|
||||||
|
public bool IsReadOnlyMode => ModUtils.Threading.GetBool(ref _isReadOnlyMode);
|
||||||
|
|
||||||
|
public bool EnableReadOnlyMode()
|
||||||
|
{
|
||||||
|
ModUtils.Threading.SetBool(ref _isReadOnlyMode, true);
|
||||||
|
return ModUtils.Threading.GetBool(ref _isReadOnlyMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,117 +1,151 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using Barotrauma.LuaCs.Configuration;
|
using Barotrauma.LuaCs.Data;
|
||||||
using Barotrauma.LuaCs.Services;
|
|
||||||
using Barotrauma.Steam;
|
using Barotrauma.Steam;
|
||||||
using FluentResults;
|
using FluentResults;
|
||||||
using FluentResults.LuaCs;
|
using FluentResults.LuaCs;
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using OneOf.Types;
|
|
||||||
using Error = FluentResults.Error;
|
using Error = FluentResults.Error;
|
||||||
using File = Barotrauma.IO.File;
|
|
||||||
using Path = Barotrauma.IO.Path;
|
using Path = Barotrauma.IO.Path;
|
||||||
using Success = OneOf.Types.Success;
|
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Services;
|
namespace Barotrauma.LuaCs.Services;
|
||||||
|
|
||||||
public class StorageService : IStorageService
|
public class StorageService : IStorageService
|
||||||
{
|
{
|
||||||
|
|
||||||
public StorageService(Lazy<IConfigService> configService)
|
public StorageService(IStorageServiceConfig configData)
|
||||||
{
|
{
|
||||||
_configService = configService;
|
_configData = configData;
|
||||||
}
|
}
|
||||||
private readonly Lazy<IConfigService> _configService;
|
|
||||||
private IConfigEntry<string> _kLocalStoragePath = null;
|
|
||||||
private IConfigEntry<string> _kLocalFilePathRules = null;
|
|
||||||
private const string _packagePathKeyword = "<PACKNAME>";
|
|
||||||
private readonly string _runLocation = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location.CleanUpPath());
|
|
||||||
|
|
||||||
// TODO: Rewrite the config to get info from .ctor.
|
private readonly ConcurrentDictionary<string, OneOf.OneOf<byte[], string, XDocument>> _fsCache = new();
|
||||||
private IConfigEntry<string> LocalStoragePath => _kLocalStoragePath ??= GetOrCreateConfig(nameof(LocalStoragePath), "/Data/Mods");
|
protected readonly IStorageServiceConfig _configData;
|
||||||
private IConfigEntry<string> LocalFilePathRule => _kLocalFilePathRules ??= GetOrCreateConfig(nameof(LocalFilePathRule), _packagePathKeyword);
|
|
||||||
private IConfigEntry<string> GetOrCreateConfig(string name, string defaultValue)
|
|
||||||
{
|
|
||||||
var c = _configService.Value
|
|
||||||
.GetConfig<IConfigEntry<string>>(ModUtils.Definitions.LuaCsForBarotrauma, name);
|
|
||||||
if (c is not null)
|
|
||||||
return c;
|
|
||||||
|
|
||||||
var c1 = _configService.Value.AddConfigEntry(
|
|
||||||
ModUtils.Definitions.LuaCsForBarotrauma,
|
|
||||||
name, defaultValue, NetSync.None, valueChangePredicate: (value) => false);
|
|
||||||
if (c1.IsSuccess)
|
|
||||||
return c1.Value;
|
|
||||||
|
|
||||||
throw new KeyNotFoundException("Cannot find storage value for key: " + name);
|
|
||||||
|
|
||||||
}
|
|
||||||
public bool IsDisposed { get; private set; }
|
|
||||||
|
|
||||||
|
public bool IsDisposed => ModUtils.Threading.GetBool(ref _isDisposed);
|
||||||
|
private int _isDisposed = 0;
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (IsDisposed)
|
ModUtils.Threading.SetBool(ref _isDisposed, true);
|
||||||
return;
|
|
||||||
IsDisposed = true;
|
|
||||||
_kLocalStoragePath = null;
|
|
||||||
_kLocalFilePathRules = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public FluentResults.Result<XDocument> LoadLocalXml(ContentPackage package, string localFilePath) =>
|
public void PurgeCache()
|
||||||
|
{
|
||||||
|
((IService)this).CheckDisposed();
|
||||||
|
_fsCache.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PurgeFileFromCache(string absolutePath)
|
||||||
|
{
|
||||||
|
((IService)this).CheckDisposed();
|
||||||
|
|
||||||
|
if (absolutePath.IsNullOrWhiteSpace())
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//sanitation pass
|
||||||
|
absolutePath = System.IO.Path.GetFullPath(absolutePath).CleanUpPath();
|
||||||
|
_fsCache.Remove(absolutePath, out _);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PurgeFilesFromCache(params string[] absolutePaths)
|
||||||
|
{
|
||||||
|
((IService)this).CheckDisposed();
|
||||||
|
|
||||||
|
if (absolutePaths.Length < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var path in absolutePaths)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (path.IsNullOrWhiteSpace())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//sanitation pass
|
||||||
|
var path2 = System.IO.Path.GetFullPath(path).CleanUpPath();
|
||||||
|
_fsCache.Remove(path2, out _);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _useCaching;
|
||||||
|
public bool UseCaching
|
||||||
|
{
|
||||||
|
get => ModUtils.Threading.GetBool(ref _useCaching);
|
||||||
|
set => ModUtils.Threading.SetBool(ref _useCaching, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual FluentResults.Result<XDocument> LoadLocalXml(ContentPackage package, string localFilePath) =>
|
||||||
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? TryLoadXml(r.Value) : r.ToResult();
|
? TryLoadXml(r.Value) : r.ToResult();
|
||||||
public FluentResults.Result<byte[]> LoadLocalBinary(ContentPackage package, string localFilePath) =>
|
public virtual FluentResults.Result<byte[]> LoadLocalBinary(ContentPackage package, string localFilePath) =>
|
||||||
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? TryLoadBinary(r.Value) : r.ToResult();
|
? TryLoadBinary(r.Value) : r.ToResult();
|
||||||
public FluentResults.Result<string> LoadLocalText(ContentPackage package, string localFilePath) =>
|
public virtual FluentResults.Result<string> LoadLocalText(ContentPackage package, string localFilePath) =>
|
||||||
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? TryLoadText(r.Value) : r.ToResult();
|
? TryLoadText(r.Value) : r.ToResult();
|
||||||
public FluentResults.Result SaveLocalXml(ContentPackage package, string localFilePath, XDocument document) =>
|
public virtual FluentResults.Result SaveLocalXml(ContentPackage package, string localFilePath, XDocument document) =>
|
||||||
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? TrySaveXml(r.Value, document) : r.ToResult();
|
? TrySaveXml(r.Value, document) : r.ToResult();
|
||||||
public FluentResults.Result SaveLocalBinary(ContentPackage package, string localFilePath, in byte[] bytes) =>
|
public virtual FluentResults.Result SaveLocalBinary(ContentPackage package, string localFilePath, in byte[] bytes) =>
|
||||||
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? TrySaveBinary(r.Value, bytes) : r.ToResult();
|
? TrySaveBinary(r.Value, bytes) : r.ToResult();
|
||||||
public FluentResults.Result SaveLocalText(ContentPackage package, string localFilePath, in string text) =>
|
public virtual FluentResults.Result SaveLocalText(ContentPackage package, string localFilePath, in string text) =>
|
||||||
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? TrySaveText(r.Value, text) : r.ToResult();
|
? TrySaveText(r.Value, text) : r.ToResult();
|
||||||
public async Task<FluentResults.Result<XDocument>> LoadLocalXmlAsync(ContentPackage package, string localFilePath) =>
|
public virtual async Task<FluentResults.Result<XDocument>> LoadLocalXmlAsync(ContentPackage package, string localFilePath) =>
|
||||||
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? await TryLoadXmlAsync(r.Value) : r.ToResult();
|
? await TryLoadXmlAsync(r.Value) : r.ToResult();
|
||||||
public async Task<FluentResults.Result<byte[]>> LoadLocalBinaryAsync(ContentPackage package, string localFilePath) =>
|
public virtual async Task<FluentResults.Result<byte[]>> LoadLocalBinaryAsync(ContentPackage package, string localFilePath) =>
|
||||||
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? await TryLoadBinaryAsync(r.Value) : r.ToResult();
|
? await TryLoadBinaryAsync(r.Value) : r.ToResult();
|
||||||
public async Task<FluentResults.Result<string>> LoadLocalTextAsync(ContentPackage package, string localFilePath) =>
|
public virtual async Task<FluentResults.Result<string>> LoadLocalTextAsync(ContentPackage package, string localFilePath) =>
|
||||||
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? await TryLoadTextAsync(r.Value) : r.ToResult();
|
? await TryLoadTextAsync(r.Value) : r.ToResult();
|
||||||
public async Task<FluentResults.Result> SaveLocalXmlAsync(ContentPackage package, string localFilePath, XDocument document) =>
|
public virtual async Task<FluentResults.Result> SaveLocalXmlAsync(ContentPackage package, string localFilePath, XDocument document) =>
|
||||||
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? await TrySaveXmlAsync(r.Value, document) : r.ToResult();
|
? await TrySaveXmlAsync(r.Value, document) : r.ToResult();
|
||||||
public async Task<FluentResults.Result> SaveLocalBinaryAsync(ContentPackage package, string localFilePath, byte[] bytes) =>
|
public virtual async Task<FluentResults.Result> SaveLocalBinaryAsync(ContentPackage package, string localFilePath, byte[] bytes) =>
|
||||||
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? await TrySaveBinaryAsync(r.Value, bytes) : r.ToResult();
|
? await TrySaveBinaryAsync(r.Value, bytes) : r.ToResult();
|
||||||
public async Task<FluentResults.Result> SaveLocalTextAsync(ContentPackage package, string localFilePath, string text) =>
|
public virtual async Task<FluentResults.Result> SaveLocalTextAsync(ContentPackage package, string localFilePath, string text) =>
|
||||||
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? await TrySaveTextAsync(r.Value, text) : r.ToResult();
|
? await TrySaveTextAsync(r.Value, text) : r.ToResult();
|
||||||
public FluentResults.Result<XDocument> LoadPackageXml(ContentPackage package, string localFilePath) =>
|
public virtual FluentResults.Result<XDocument> LoadPackageXml(ContentPackage package, string localFilePath) =>
|
||||||
GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? TryLoadXml(r.Value) : r.ToResult();
|
? TryLoadXml(r.Value) : r.ToResult();
|
||||||
public FluentResults.Result<byte[]> LoadPackageBinary(ContentPackage package, string localFilePath) =>
|
public virtual FluentResults.Result<byte[]> LoadPackageBinary(ContentPackage package, string localFilePath) =>
|
||||||
GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? TryLoadBinary(r.Value) : r.ToResult();
|
? TryLoadBinary(r.Value) : r.ToResult();
|
||||||
public FluentResults.Result<string> LoadPackageText(ContentPackage package, string localFilePath) =>
|
public virtual FluentResults.Result<string> LoadPackageText(ContentPackage package, string localFilePath) =>
|
||||||
GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? TryLoadText(r.Value) : r.ToResult();
|
? TryLoadText(r.Value) : r.ToResult();
|
||||||
|
|
||||||
public ImmutableArray<(string, FluentResults.Result<XDocument>)> LoadPackageXmlFiles(ContentPackage package, ImmutableArray<string> localFilePaths)
|
|
||||||
|
|
||||||
|
public virtual ImmutableArray<(string, FluentResults.Result<XDocument>)> LoadPackageXmlFiles(ContentPackage package, ImmutableArray<string> localFilePaths)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
if (localFilePaths.IsDefaultOrEmpty)
|
if (localFilePaths.IsDefaultOrEmpty)
|
||||||
@@ -122,7 +156,7 @@ public class StorageService : IStorageService
|
|||||||
return builder.MoveToImmutable();
|
return builder.MoveToImmutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImmutableArray<(string, FluentResults.Result<byte[]>)> LoadPackageBinaryFiles(ContentPackage package, ImmutableArray<string> localFilePaths)
|
public virtual ImmutableArray<(string, FluentResults.Result<byte[]>)> LoadPackageBinaryFiles(ContentPackage package, ImmutableArray<string> localFilePaths)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
if (localFilePaths.IsDefaultOrEmpty)
|
if (localFilePaths.IsDefaultOrEmpty)
|
||||||
@@ -133,7 +167,7 @@ public class StorageService : IStorageService
|
|||||||
return builder.MoveToImmutable();
|
return builder.MoveToImmutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImmutableArray<(string, FluentResults.Result<string>)> LoadPackageTextFiles(ContentPackage package, ImmutableArray<string> localFilePaths)
|
public virtual ImmutableArray<(string, FluentResults.Result<string>)> LoadPackageTextFiles(ContentPackage package, ImmutableArray<string> localFilePaths)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
if (localFilePaths.IsDefaultOrEmpty)
|
if (localFilePaths.IsDefaultOrEmpty)
|
||||||
@@ -144,7 +178,7 @@ public class StorageService : IStorageService
|
|||||||
return builder.MoveToImmutable();
|
return builder.MoveToImmutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FluentResults.Result<ImmutableArray<string>> FindFilesInPackage(ContentPackage package, string localSubfolder, string regexFilter, bool searchRecursively)
|
public virtual FluentResults.Result<ImmutableArray<string>> FindFilesInPackage(ContentPackage package, string localSubfolder, string regexFilter, bool searchRecursively)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
var r = GetAbsFromPackage(package, localSubfolder);
|
var r = GetAbsFromPackage(package, localSubfolder);
|
||||||
@@ -157,53 +191,60 @@ public class StorageService : IStorageService
|
|||||||
.WithValue(arr.ToImmutableArray());
|
.WithValue(arr.ToImmutableArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FluentResults.Result<XDocument>> LoadPackageXmlAsync(ContentPackage package, string localFilePath) =>
|
public virtual async Task<FluentResults.Result<XDocument>> LoadPackageXmlAsync(ContentPackage package, string localFilePath) =>
|
||||||
GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? await TryLoadXmlAsync(r.Value) : r.ToResult();
|
? await TryLoadXmlAsync(r.Value) : r.ToResult();
|
||||||
|
|
||||||
public async Task<FluentResults.Result<byte[]>> LoadPackageBinaryAsync(ContentPackage package, string localFilePath) =>
|
public virtual async Task<FluentResults.Result<byte[]>> LoadPackageBinaryAsync(ContentPackage package, string localFilePath) =>
|
||||||
GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? await TryLoadBinaryAsync(r.Value) : r.ToResult();
|
? await TryLoadBinaryAsync(r.Value) : r.ToResult();
|
||||||
|
|
||||||
public async Task<FluentResults.Result<string>> LoadPackageTextAsync(ContentPackage package, string localFilePath) =>
|
public virtual async Task<FluentResults.Result<string>> LoadPackageTextAsync(ContentPackage package, string localFilePath) =>
|
||||||
GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null }
|
||||||
? await TryLoadTextAsync(r.Value) : r.ToResult();
|
? await TryLoadTextAsync(r.Value) : r.ToResult();
|
||||||
|
|
||||||
public async Task<ImmutableArray<(string, FluentResults.Result<XDocument>)>> LoadPackageXmlFilesAsync(ContentPackage package, ImmutableArray<string> localFilePaths)
|
public virtual async Task<ImmutableArray<(string, FluentResults.Result<XDocument>)>> LoadPackageXmlFilesAsync(ContentPackage package, ImmutableArray<string> localFilePaths)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
if (localFilePaths.IsDefaultOrEmpty)
|
if (localFilePaths.IsDefaultOrEmpty)
|
||||||
return ImmutableArray<(string, FluentResults.Result<XDocument>)>.Empty;
|
return ImmutableArray<(string, FluentResults.Result<XDocument>)>.Empty;
|
||||||
var builder = ImmutableArray.CreateBuilder<(string, FluentResults.Result<XDocument>)>(localFilePaths.Length);
|
var builder = ImmutableArray.CreateBuilder<(string, FluentResults.Result<XDocument>)>(localFilePaths.Length);
|
||||||
foreach (var path in localFilePaths)
|
|
||||||
|
await localFilePaths.ParallelForEachAsync(async path =>
|
||||||
|
{
|
||||||
builder.Add((path, await LoadPackageXmlAsync(package, path)));
|
builder.Add((path, await LoadPackageXmlAsync(package, path)));
|
||||||
|
}, maxDegreeOfParallelism: 2);
|
||||||
return builder.MoveToImmutable();
|
return builder.MoveToImmutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ImmutableArray<(string, FluentResults.Result<byte[]>)>> LoadPackageBinaryFilesAsync(ContentPackage package, ImmutableArray<string> localFilePaths)
|
public virtual async Task<ImmutableArray<(string, FluentResults.Result<byte[]>)>> LoadPackageBinaryFilesAsync(ContentPackage package, ImmutableArray<string> localFilePaths)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
if (localFilePaths.IsDefaultOrEmpty)
|
if (localFilePaths.IsDefaultOrEmpty)
|
||||||
return ImmutableArray<(string, FluentResults.Result<byte[]>)>.Empty;
|
return ImmutableArray<(string, FluentResults.Result<byte[]>)>.Empty;
|
||||||
var builder = ImmutableArray.CreateBuilder<(string, FluentResults.Result<byte[]>)>(localFilePaths.Length);
|
var builder = ImmutableArray.CreateBuilder<(string, FluentResults.Result<byte[]>)>(localFilePaths.Length);
|
||||||
foreach (var path in localFilePaths)
|
await localFilePaths.ParallelForEachAsync(async path =>
|
||||||
|
{
|
||||||
builder.Add((path, await LoadPackageBinaryAsync(package, path)));
|
builder.Add((path, await LoadPackageBinaryAsync(package, path)));
|
||||||
|
}, maxDegreeOfParallelism: 2);
|
||||||
return builder.MoveToImmutable();
|
return builder.MoveToImmutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ImmutableArray<(string, FluentResults.Result<string>)>> LoadPackageTextFilesAsync(ContentPackage package, ImmutableArray<string> localFilePaths)
|
public virtual async Task<ImmutableArray<(string, FluentResults.Result<string>)>> LoadPackageTextFilesAsync(ContentPackage package, ImmutableArray<string> localFilePaths)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
if (localFilePaths.IsDefaultOrEmpty)
|
if (localFilePaths.IsDefaultOrEmpty)
|
||||||
return ImmutableArray<(string, FluentResults.Result<string>)>.Empty;
|
return ImmutableArray<(string, FluentResults.Result<string>)>.Empty;
|
||||||
var builder = ImmutableArray.CreateBuilder<(string, FluentResults.Result<string>)>(localFilePaths.Length);
|
var builder = ImmutableArray.CreateBuilder<(string, FluentResults.Result<string>)>(localFilePaths.Length);
|
||||||
foreach (var path in localFilePaths)
|
await localFilePaths.ParallelForEachAsync(async path =>
|
||||||
|
{
|
||||||
builder.Add((path, await LoadPackageTextAsync(package, path)));
|
builder.Add((path, await LoadPackageTextAsync(package, path)));
|
||||||
|
}, maxDegreeOfParallelism: 2);
|
||||||
return builder.MoveToImmutable();
|
return builder.MoveToImmutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public FluentResults.Result<XDocument> TryLoadXml(string filePath, Encoding encoding = null)
|
public virtual FluentResults.Result<XDocument> TryLoadXml(string filePath, Encoding encoding = null)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
var r = TryLoadText(filePath, encoding);
|
var r = TryLoadText(filePath, encoding);
|
||||||
@@ -216,32 +257,48 @@ public class StorageService : IStorageService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FluentResults.Result<string> TryLoadText(string filePath, Encoding encoding = null)
|
public virtual FluentResults.Result<string> TryLoadText(string filePath, Encoding encoding = null)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
|
if (UseCaching && _fsCache.TryGetValue(filePath, out var result)
|
||||||
|
&& result.TryPickT1(out var cachedVal, out _))
|
||||||
|
{
|
||||||
|
return FluentResults.Result.Ok(cachedVal);
|
||||||
|
}
|
||||||
|
|
||||||
return IOExceptionsOperationRunner(nameof(TryLoadText), filePath, () =>
|
return IOExceptionsOperationRunner(nameof(TryLoadText), filePath, () =>
|
||||||
{
|
{
|
||||||
var fp = filePath.CleanUpPath();
|
var fp = filePath.CleanUpPath();
|
||||||
fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp);
|
fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp);
|
||||||
var fileText = encoding is null ? System.IO.File.ReadAllText(fp) : System.IO.File.ReadAllText(fp, encoding);
|
var fileText = encoding is null ? System.IO.File.ReadAllText(fp) : System.IO.File.ReadAllText(fp, encoding);
|
||||||
|
if (UseCaching)
|
||||||
|
_fsCache[filePath] = fileText;
|
||||||
return new FluentResults.Result<string>().WithSuccess($"Loaded file successfully").WithValue(fileText);
|
return new FluentResults.Result<string>().WithSuccess($"Loaded file successfully").WithValue(fileText);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public FluentResults.Result<byte[]> TryLoadBinary(string filePath)
|
public virtual FluentResults.Result<byte[]> TryLoadBinary(string filePath)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
|
if (UseCaching && _fsCache.TryGetValue(filePath, out var result)
|
||||||
|
&& result.TryPickT0(out var cachedVal, out _))
|
||||||
|
{
|
||||||
|
return FluentResults.Result.Ok(cachedVal);
|
||||||
|
}
|
||||||
|
|
||||||
return IOExceptionsOperationRunner(nameof(TryLoadBinary), filePath, () =>
|
return IOExceptionsOperationRunner(nameof(TryLoadBinary), filePath, () =>
|
||||||
{
|
{
|
||||||
var fp = filePath.CleanUpPath();
|
var fp = filePath.CleanUpPath();
|
||||||
fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp);
|
fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp);
|
||||||
var fileData = System.IO.File.ReadAllBytes(fp);
|
var fileData = System.IO.File.ReadAllBytes(fp);
|
||||||
|
if (UseCaching)
|
||||||
|
_fsCache[filePath] = fileData;
|
||||||
return new FluentResults.Result<byte[]>().WithSuccess($"Loaded file successfully").WithValue(fileData);
|
return new FluentResults.Result<byte[]>().WithSuccess($"Loaded file successfully").WithValue(fileData);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public FluentResults.Result TrySaveXml(string filePath, in XDocument document, Encoding encoding = null) => TrySaveText(filePath, document.ToString(), encoding);
|
public virtual FluentResults.Result TrySaveXml(string filePath, in XDocument document, Encoding encoding = null) => TrySaveText(filePath, document.ToString(), encoding);
|
||||||
public FluentResults.Result TrySaveText(string filePath, in string text, Encoding encoding = null)
|
public virtual FluentResults.Result TrySaveText(string filePath, in string text, Encoding encoding = null)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
if (text.IsNullOrWhiteSpace())
|
if (text.IsNullOrWhiteSpace())
|
||||||
@@ -251,18 +308,19 @@ public class StorageService : IStorageService
|
|||||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||||
.WithMetadata(MetadataType.Sources, filePath));
|
.WithMetadata(MetadataType.Sources, filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
string t = text; //copy
|
string t = text; //copy
|
||||||
return IOExceptionsOperationRunner(nameof(TrySaveText), filePath, () =>
|
return IOExceptionsOperationRunner(nameof(TrySaveText), filePath, () =>
|
||||||
{
|
{
|
||||||
var fp = filePath.CleanUpPath();
|
var fp = filePath.CleanUpPath();
|
||||||
fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp);
|
fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp);
|
||||||
System.IO.File.WriteAllText(fp, t, encoding);
|
System.IO.File.WriteAllText(fp, t, encoding);
|
||||||
|
if (UseCaching)
|
||||||
|
_fsCache[filePath] = t;
|
||||||
return new FluentResults.Result().WithSuccess($"Saved to file successfully");
|
return new FluentResults.Result().WithSuccess($"Saved to file successfully");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public FluentResults.Result TrySaveBinary(string filePath, in byte[] bytes)
|
public virtual FluentResults.Result TrySaveBinary(string filePath, in byte[] bytes)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
if (bytes is null || bytes.Length == 0)
|
if (bytes is null || bytes.Length == 0)
|
||||||
@@ -279,11 +337,13 @@ public class StorageService : IStorageService
|
|||||||
var fp = filePath.CleanUpPath();
|
var fp = filePath.CleanUpPath();
|
||||||
fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp);
|
fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp);
|
||||||
System.IO.File.WriteAllBytes(fp, b);
|
System.IO.File.WriteAllBytes(fp, b);
|
||||||
|
if (UseCaching)
|
||||||
|
_fsCache[filePath] = b;
|
||||||
return new FluentResults.Result().WithSuccess($"Saved to file successfully");
|
return new FluentResults.Result().WithSuccess($"Saved to file successfully");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public FluentResults.Result<bool> FileExists(string filePath)
|
public virtual FluentResults.Result<bool> FileExists(string filePath)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
return IOExceptionsOperationRunner<bool>(nameof(FileExists), filePath, () =>
|
return IOExceptionsOperationRunner<bool>(nameof(FileExists), filePath, () =>
|
||||||
@@ -294,7 +354,7 @@ public class StorageService : IStorageService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public FluentResults.Result<bool> DirectoryExists(string directoryPath)
|
public virtual FluentResults.Result<bool> DirectoryExists(string directoryPath)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
try
|
try
|
||||||
@@ -308,12 +368,19 @@ public class StorageService : IStorageService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FluentResults.Result<XDocument>> TryLoadXmlAsync(string filePath, Encoding encoding = null)
|
public virtual async Task<FluentResults.Result<XDocument>> TryLoadXmlAsync(string filePath, Encoding encoding = null)
|
||||||
{
|
{
|
||||||
|
((IService)this).CheckDisposed();
|
||||||
|
if (UseCaching && _fsCache.TryGetValue(filePath, out var cachedVal)
|
||||||
|
&& cachedVal.TryPickT2(out var cachedDoc, out _))
|
||||||
|
return FluentResults.Result.Ok(cachedDoc);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
|
await using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
|
||||||
return await XDocument.LoadAsync(fs, LoadOptions.PreserveWhitespace, CancellationToken.None);
|
var doc = await XDocument.LoadAsync(fs, LoadOptions.PreserveWhitespace, CancellationToken.None);
|
||||||
|
if (UseCaching)
|
||||||
|
_fsCache[filePath] = doc;
|
||||||
|
return FluentResults.Result.Ok(doc);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -321,20 +388,33 @@ public class StorageService : IStorageService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FluentResults.Result<string>> TryLoadTextAsync(string filePath, Encoding encoding = null)
|
public virtual async Task<FluentResults.Result<string>> TryLoadTextAsync(string filePath, Encoding encoding = null)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
|
if (UseCaching && _fsCache.TryGetValue(filePath, out var cachedVal)
|
||||||
|
&& cachedVal.TryPickT1(out var cachedTxt, out _))
|
||||||
|
return FluentResults.Result.Ok(cachedTxt);
|
||||||
|
|
||||||
return await IOExceptionsOperationRunnerAsync<string>(nameof(TryLoadTextAsync), filePath, async () =>
|
return await IOExceptionsOperationRunnerAsync<string>(nameof(TryLoadTextAsync), filePath, async () =>
|
||||||
{
|
{
|
||||||
var fp = filePath.CleanUpPath();
|
var fp = filePath.CleanUpPath();
|
||||||
fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp);
|
fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp);
|
||||||
return await System.IO.File.ReadAllTextAsync(fp);
|
var txt = await System.IO.File.ReadAllTextAsync(fp);
|
||||||
|
if (UseCaching)
|
||||||
|
_fsCache[filePath] = txt;
|
||||||
|
return FluentResults.Result.Ok(txt);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FluentResults.Result<byte[]>> TryLoadBinaryAsync(string filePath)
|
public virtual async Task<FluentResults.Result<byte[]>> TryLoadBinaryAsync(string filePath)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
|
if (UseCaching && _fsCache.TryGetValue(filePath, out var cachedVal)
|
||||||
|
&& cachedVal.TryPickT0(out var cachedBin, out _))
|
||||||
|
{
|
||||||
|
return cachedBin;
|
||||||
|
}
|
||||||
|
|
||||||
return await IOExceptionsOperationRunnerAsync<byte[]>(nameof(TryLoadTextAsync), filePath, async () =>
|
return await IOExceptionsOperationRunnerAsync<byte[]>(nameof(TryLoadTextAsync), filePath, async () =>
|
||||||
{
|
{
|
||||||
var fp = filePath.CleanUpPath();
|
var fp = filePath.CleanUpPath();
|
||||||
@@ -343,8 +423,8 @@ public class StorageService : IStorageService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FluentResults.Result> TrySaveXmlAsync(string filePath, XDocument document, Encoding encoding = null) => await TrySaveTextAsync(filePath, document.ToString(), encoding);
|
public virtual async Task<FluentResults.Result> TrySaveXmlAsync(string filePath, XDocument document, Encoding encoding = null) => await TrySaveTextAsync(filePath, document.ToString(), encoding);
|
||||||
public async Task<FluentResults.Result> TrySaveTextAsync(string filePath, string text, Encoding encoding = null)
|
public virtual async Task<FluentResults.Result> TrySaveTextAsync(string filePath, string text, Encoding encoding = null)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
if (text.IsNullOrWhiteSpace())
|
if (text.IsNullOrWhiteSpace())
|
||||||
@@ -361,11 +441,13 @@ public class StorageService : IStorageService
|
|||||||
var fp = filePath.CleanUpPath();
|
var fp = filePath.CleanUpPath();
|
||||||
fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp);
|
fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp);
|
||||||
await System.IO.File.WriteAllTextAsync(fp, t, encoding);
|
await System.IO.File.WriteAllTextAsync(fp, t, encoding);
|
||||||
|
if (UseCaching)
|
||||||
|
_fsCache[filePath] = t;
|
||||||
return new FluentResults.Result().WithSuccess($"Saved to file successfully");
|
return new FluentResults.Result().WithSuccess($"Saved to file successfully");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FluentResults.Result> TrySaveBinaryAsync(string filePath, byte[] bytes)
|
public virtual async Task<FluentResults.Result> TrySaveBinaryAsync(string filePath, byte[] bytes)
|
||||||
{
|
{
|
||||||
((IService)this).CheckDisposed();
|
((IService)this).CheckDisposed();
|
||||||
if (bytes is null || bytes.Length == 0)
|
if (bytes is null || bytes.Length == 0)
|
||||||
@@ -382,6 +464,8 @@ public class StorageService : IStorageService
|
|||||||
var fp = filePath.CleanUpPath();
|
var fp = filePath.CleanUpPath();
|
||||||
fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp);
|
fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp);
|
||||||
await System.IO.File.WriteAllBytesAsync(fp, b);
|
await System.IO.File.WriteAllBytesAsync(fp, b);
|
||||||
|
if (UseCaching)
|
||||||
|
_fsCache[filePath] = b;
|
||||||
return new FluentResults.Result().WithSuccess($"Saved to file successfully");
|
return new FluentResults.Result().WithSuccess($"Saved to file successfully");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -392,41 +476,9 @@ public class StorageService : IStorageService
|
|||||||
{
|
{
|
||||||
return await operation?.Invoke()!;
|
return await operation?.Invoke()!;
|
||||||
}
|
}
|
||||||
catch (ArgumentNullException ane)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
return ReturnException(ane, filepath).WithError(GetGeneralError(funcName, filepath));
|
return ReturnException(e, filepath).WithError(GetGeneralError(funcName, filepath));
|
||||||
}
|
|
||||||
catch (ArgumentException ae)
|
|
||||||
{
|
|
||||||
return ReturnException(ae, filepath).WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (PathTooLongException ptle)
|
|
||||||
{
|
|
||||||
return ReturnException(ptle, filepath).WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (NotSupportedException nse)
|
|
||||||
{
|
|
||||||
return ReturnException(nse, filepath).WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (UnauthorizedAccessException uae)
|
|
||||||
{
|
|
||||||
return ReturnException(uae, filepath).WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (DirectoryNotFoundException dnfe)
|
|
||||||
{
|
|
||||||
return ReturnException(dnfe, filepath).WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException fnfe)
|
|
||||||
{
|
|
||||||
return ReturnException(fnfe, filepath).WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (SecurityException se)
|
|
||||||
{
|
|
||||||
return ReturnException(se, filepath).WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (IOException ioe)
|
|
||||||
{
|
|
||||||
return ReturnException(ioe, filepath).WithError(GetGeneralError(nameof(SaveLocalXml), filepath));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,41 +488,9 @@ public class StorageService : IStorageService
|
|||||||
{
|
{
|
||||||
return await operation?.Invoke()!;
|
return await operation?.Invoke()!;
|
||||||
}
|
}
|
||||||
catch (ArgumentNullException ane)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
return ReturnException(ane, filepath).WithError(GetGeneralError(funcName, filepath));
|
return ReturnException(e, filepath).WithError(GetGeneralError(funcName, filepath));
|
||||||
}
|
|
||||||
catch (ArgumentException ae)
|
|
||||||
{
|
|
||||||
return ReturnException(ae, filepath).WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (PathTooLongException ptle)
|
|
||||||
{
|
|
||||||
return ReturnException(ptle, filepath).WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (NotSupportedException nse)
|
|
||||||
{
|
|
||||||
return ReturnException(nse, filepath).WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (UnauthorizedAccessException uae)
|
|
||||||
{
|
|
||||||
return ReturnException(uae, filepath).WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (DirectoryNotFoundException dnfe)
|
|
||||||
{
|
|
||||||
return ReturnException(dnfe, filepath).WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException fnfe)
|
|
||||||
{
|
|
||||||
return ReturnException(fnfe, filepath).WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (SecurityException se)
|
|
||||||
{
|
|
||||||
return ReturnException(se, filepath).WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (IOException ioe)
|
|
||||||
{
|
|
||||||
return ReturnException(ioe, filepath).WithError(GetGeneralError(nameof(SaveLocalXml), filepath));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,50 +500,9 @@ public class StorageService : IStorageService
|
|||||||
{
|
{
|
||||||
return operation?.Invoke();
|
return operation?.Invoke();
|
||||||
}
|
}
|
||||||
catch (ArgumentNullException ane)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
return ReturnException(ane, filepath)
|
return ReturnException(e, filepath).WithError(GetGeneralError(funcName, filepath));
|
||||||
.WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (ArgumentException ae)
|
|
||||||
{
|
|
||||||
return ReturnException(ae, filepath)
|
|
||||||
.WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (PathTooLongException ptle)
|
|
||||||
{
|
|
||||||
return ReturnException(ptle, filepath)
|
|
||||||
.WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (NotSupportedException nse)
|
|
||||||
{
|
|
||||||
return ReturnException(nse, filepath)
|
|
||||||
.WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (UnauthorizedAccessException uae)
|
|
||||||
{
|
|
||||||
return ReturnException(uae, filepath)
|
|
||||||
.WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (DirectoryNotFoundException dnfe)
|
|
||||||
{
|
|
||||||
return ReturnException(dnfe, filepath)
|
|
||||||
.WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException fnfe)
|
|
||||||
{
|
|
||||||
return ReturnException(fnfe, filepath)
|
|
||||||
.WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (SecurityException se)
|
|
||||||
{
|
|
||||||
return ReturnException(se, filepath)
|
|
||||||
.WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (IOException ioe)
|
|
||||||
{
|
|
||||||
return ReturnException(ioe, filepath)
|
|
||||||
.WithError(GetGeneralError(nameof(SaveLocalXml), filepath));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,50 +512,9 @@ public class StorageService : IStorageService
|
|||||||
{
|
{
|
||||||
return operation?.Invoke();
|
return operation?.Invoke();
|
||||||
}
|
}
|
||||||
catch (ArgumentNullException ane)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
return ReturnException(ane, filepath)
|
return ReturnException(e, filepath).WithError(GetGeneralError(funcName, filepath));
|
||||||
.WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (ArgumentException ae)
|
|
||||||
{
|
|
||||||
return ReturnException(ae, filepath)
|
|
||||||
.WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (PathTooLongException ptle)
|
|
||||||
{
|
|
||||||
return ReturnException(ptle, filepath)
|
|
||||||
.WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (NotSupportedException nse)
|
|
||||||
{
|
|
||||||
return ReturnException(nse, filepath)
|
|
||||||
.WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (UnauthorizedAccessException uae)
|
|
||||||
{
|
|
||||||
return ReturnException(uae, filepath)
|
|
||||||
.WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (DirectoryNotFoundException dnfe)
|
|
||||||
{
|
|
||||||
return ReturnException(dnfe, filepath)
|
|
||||||
.WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException fnfe)
|
|
||||||
{
|
|
||||||
return ReturnException(fnfe, filepath)
|
|
||||||
.WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (SecurityException se)
|
|
||||||
{
|
|
||||||
return ReturnException(se, filepath)
|
|
||||||
.WithError(GetGeneralError(funcName, filepath));
|
|
||||||
}
|
|
||||||
catch (IOException ioe)
|
|
||||||
{
|
|
||||||
return ReturnException(ioe, filepath)
|
|
||||||
.WithError(GetGeneralError(nameof(SaveLocalXml), filepath));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -610,14 +548,11 @@ public class StorageService : IStorageService
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new FluentResults.Result<string>().WithSuccess($"Path constructed")
|
return new FluentResults.Result<string>().WithSuccess($"Path constructed")
|
||||||
.WithValue( System.IO.Path.GetFullPath(System.IO.Path.Combine(
|
.WithValue(System.IO.Path.GetFullPath(System.IO.Path.Combine(
|
||||||
_runLocation,
|
_configData.RunLocation,
|
||||||
LocalStoragePath.Value,
|
_configData.LocalPackageDataPath.Replace(
|
||||||
LocalFilePathRule.Value.Replace(_packagePathKeyword, package.Name.IsNullOrWhiteSpace()
|
_configData.LocalDataPathRegex,
|
||||||
? package.TryExtractSteamWorkshopId(out var id)
|
package.TryExtractSteamWorkshopId(out var id) ? id.Value.ToString() : package.Name),
|
||||||
? id.Value.ToString()
|
|
||||||
: "_fallbackFolder"
|
|
||||||
: package.Name),
|
|
||||||
localFilePath)));
|
localFilePath)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+26
-59
@@ -3,77 +3,44 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml.Linq;
|
||||||
using Barotrauma.LuaCs.Configuration;
|
using Barotrauma.LuaCs.Configuration;
|
||||||
using Barotrauma.LuaCs.Data;
|
using Barotrauma.LuaCs.Data;
|
||||||
using Barotrauma.LuaCs.Services;
|
using Barotrauma.LuaCs.Services;
|
||||||
using Barotrauma.LuaCs.Services.Safe;
|
using Barotrauma.LuaCs.Services.Safe;
|
||||||
using Barotrauma.Networking;
|
using Barotrauma.Networking;
|
||||||
|
using FluentResults;
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Services;
|
namespace Barotrauma.LuaCs.Services;
|
||||||
|
|
||||||
public partial interface IConfigService : IReusableService, ILuaConfigService
|
public partial interface IConfigService : IReusableService, ILuaConfigService
|
||||||
{
|
{
|
||||||
/*
|
/// <summary>
|
||||||
* Resource Files.
|
/// 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
|
||||||
Task<FluentResults.Result> LoadConfigsAsync(ImmutableArray<IConfigResourceInfo> configResources);
|
Task<FluentResults.Result> LoadConfigsAsync(ImmutableArray<IConfigResourceInfo> configResources);
|
||||||
Task<FluentResults.Result> LoadConfigsProfilesAsync(ImmutableArray<IConfigProfileResourceInfo> configProfileResources);
|
Task<FluentResults.Result> LoadConfigsProfilesAsync(ImmutableArray<IConfigProfileResourceInfo> configProfileResources);
|
||||||
FluentResults.Result DisposeConfigs(ImmutableArray<IConfigResourceInfo> configResources);
|
|
||||||
FluentResults.Result DisposeConfigsProfiles(ImmutableArray<IConfigProfileResourceInfo> configProfilesResources);
|
|
||||||
FluentResults.Result DisposeConfigs(ContentPackage package);
|
|
||||||
FluentResults.Result DisposeConfigsProfiles(ContentPackage package);
|
|
||||||
|
|
||||||
|
// Immediate Mode
|
||||||
|
FluentResults.Result<TConfig> AddConfig<TConfig>(IConfigInfo configInfo) where TConfig : IConfigBase;
|
||||||
|
|
||||||
/*
|
// Utility
|
||||||
* Immediate mode
|
FluentResults.Result ApplyProfileSettings(ContentPackage package, string profileName);
|
||||||
*/
|
FluentResults.Result DisposePackageData(ContentPackage package);
|
||||||
FluentResults.Result<IConfigEntry<T>> AddConfigEntry<T>(ContentPackage package, string name,
|
FluentResults.Result<IReadOnlyDictionary<(ContentPackage Package, string ConfigName), IConfigBase>> GetConfigsForPackage(ContentPackage package);
|
||||||
T defaultValue,
|
FluentResults.Result<IReadOnlyDictionary<(ContentPackage Package, string ConfigName), ImmutableArray<(string ConfigName, OneOf.OneOf<string, XElement> Value)>>>
|
||||||
NetSync syncMode = NetSync.None,
|
GetProfilesForPackage(ContentPackage package);
|
||||||
ClientPermissions permissions = ClientPermissions.None,
|
IReadOnlyDictionary<(ContentPackage Package, string Name), IConfigBase> GetAllConfigs();
|
||||||
Func<T, bool> valueChangePredicate = null,
|
bool TryGetConfig<T>(ContentPackage package, string name, out T config) where T : IConfigBase;
|
||||||
Action<IConfigEntry<T>> onValueChanged = null) where T : IConvertible, IEquatable<T>;
|
Task<FluentResults.Result> SaveAllConfigs();
|
||||||
|
Task<FluentResults.Result> SaveConfigsForPackage(ContentPackage package);
|
||||||
FluentResults.Result<IConfigList> AddConfigList(ContentPackage package, string name,
|
Task<FluentResults.Result> SaveConfig((ContentPackage Package, string ConfigName) config);
|
||||||
int defaultIndex, IReadOnlyList<string> values,
|
|
||||||
NetSync syncMode = NetSync.None,
|
|
||||||
ClientPermissions permissions = ClientPermissions.None,
|
|
||||||
Func<IConfigList, int, bool> valueChangePredicate = null,
|
|
||||||
Action<IConfigList, int> onValueChanged = null);
|
|
||||||
|
|
||||||
FluentResults.Result<IConfigRangeEntry<T>> AddConfigRangeEntry<T>(ContentPackage package, string name,
|
|
||||||
T defaultValue, T minValue, T maxValue,
|
|
||||||
Func<IConfigRangeEntry<T>, int> getStepCount,
|
|
||||||
NetSync syncMode = NetSync.None,
|
|
||||||
ClientPermissions permissions = ClientPermissions.None,
|
|
||||||
Func<T, bool> valueChangePredicate = null,
|
|
||||||
Action<IConfigEntry<T>> onValueChanged = null) where T : IConvertible, IEquatable<T>;
|
|
||||||
|
|
||||||
FluentResults.Result<IConfigEntry<T>> AddConfigEntry<T>(string packageName, string name,
|
|
||||||
T defaultValue,
|
|
||||||
NetSync syncMode = NetSync.None,
|
|
||||||
ClientPermissions permissions = ClientPermissions.None,
|
|
||||||
Func<T, bool> valueChangePredicate = null,
|
|
||||||
Action<IConfigEntry<T>> onValueChanged = null) where T : IConvertible, IEquatable<T>;
|
|
||||||
|
|
||||||
FluentResults.Result<IConfigList> AddConfigList(string packageName, string name,
|
|
||||||
int defaultIndex, IReadOnlyList<string> values,
|
|
||||||
NetSync syncMode = NetSync.None,
|
|
||||||
ClientPermissions permissions = ClientPermissions.None,
|
|
||||||
Func<IConfigList, int, bool> valueChangePredicate = null,
|
|
||||||
Action<IConfigList, int> onValueChanged = null);
|
|
||||||
|
|
||||||
FluentResults.Result<IConfigRangeEntry<T>> AddConfigRangeEntry<T>(string packageName, string name,
|
|
||||||
T defaultValue, T minValue, T maxValue,
|
|
||||||
Func<IConfigRangeEntry<T>, int> getStepCount,
|
|
||||||
NetSync syncMode = NetSync.None,
|
|
||||||
ClientPermissions permissions = ClientPermissions.None,
|
|
||||||
Func<T, bool> valueChangePredicate = null,
|
|
||||||
Action<IConfigEntry<T>> onValueChanged = null) where T : IConvertible, IEquatable<T>;
|
|
||||||
|
|
||||||
FluentResults.Result<IReadOnlyDictionary<string, IConfigBase>> GetConfigsForPackage(ContentPackage package);
|
|
||||||
FluentResults.Result<IReadOnlyDictionary<string, IConfigBase>> GetConfigsForPackage(string packageName);
|
|
||||||
IReadOnlyDictionary<(ContentPackage, string), IConfigBase> GetAllConfigs();
|
|
||||||
T GetConfig<T>(ContentPackage package, string name) where T : IConfigBase;
|
|
||||||
T GetConfig<T>(string packageName, string name) where T : IConfigBase;
|
|
||||||
}
|
}
|
||||||
|
|||||||
-34
@@ -1,34 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Immutable;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Barotrauma.LuaCs.Data;
|
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Services;
|
|
||||||
|
|
||||||
public interface ILocalizationService : IReusableService
|
|
||||||
{
|
|
||||||
IReadOnlyCollection<CultureInfo> GetLoadedLocales();
|
|
||||||
void Remove(ImmutableArray<ILocalizationResourceInfo> localizations);
|
|
||||||
void DisposePackage(ContentPackage package);
|
|
||||||
FluentResults.Result SetCurrentCulture(CultureInfo culture);
|
|
||||||
FluentResults.Result SetCurrentCulture(string cultureName);
|
|
||||||
Task<FluentResults.Result> LoadLocalizations(ImmutableArray<ILocalizationResourceInfo> localizationResources);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to get a localized string without a fallback. Returns success/failure and associated data.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">Neutral localization key.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
FluentResults.Result<string> GetLocalizedString(string key);
|
|
||||||
FluentResults.Result<string> GetLocalizedString(string key, CultureInfo targetCulture);
|
|
||||||
string GetLocalizedString(string key, string fallback);
|
|
||||||
string GetLocalizedString(string key, string fallback, CultureInfo targetCulture);
|
|
||||||
FluentResults.Result<string> GetLocalizedStringForPackage(ContentPackage package, string key);
|
|
||||||
FluentResults.Result<string> GetLocalizedStringForPackage(ContentPackage package, string key, CultureInfo targetCulture);
|
|
||||||
string GetLocalizedStringForPackage(ContentPackage package, string key, string fallback);
|
|
||||||
string GetLocalizedStringForPackage(ContentPackage package, string key, string fallback, CultureInfo targetCulture);
|
|
||||||
FluentResults.Result RegisterLocalizationResolver(CultureInfo targetCulture, Func<string, CultureInfo, string> factoryResolver);
|
|
||||||
bool IsCurrentCultureSupported(IResourceCultureInfo culturesInfo);
|
|
||||||
}
|
|
||||||
+49
-3
@@ -4,6 +4,7 @@ using System.Collections.Immutable;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Barotrauma.LuaCs.Data;
|
using Barotrauma.LuaCs.Data;
|
||||||
|
using FluentResults;
|
||||||
using MoonSharp.Interpreter;
|
using MoonSharp.Interpreter;
|
||||||
using MoonSharp.Interpreter.Interop;
|
using MoonSharp.Interpreter.Interop;
|
||||||
|
|
||||||
@@ -13,13 +14,58 @@ public interface ILuaScriptManagementService : IReusableService
|
|||||||
{
|
{
|
||||||
#region Script_Ops
|
#region Script_Ops
|
||||||
|
|
||||||
|
Result<object> GetGlobalTableValue(string tableName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses and loads script sources (code) into a memory cache without executing it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourcesInfo"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
// [Required]
|
||||||
Task<FluentResults.Result> LoadScriptResourcesAsync(ImmutableArray<ILuaScriptResourceInfo> resourcesInfo);
|
Task<FluentResults.Result> LoadScriptResourcesAsync(ImmutableArray<ILuaScriptResourceInfo> resourcesInfo);
|
||||||
|
|
||||||
FluentResults.Result ExecuteLoadedScripts(ContentPackage package, bool pauseExecutionOnError = false, bool verboseLogging = false);
|
/// <summary>
|
||||||
FluentResults.Result ExecuteLoadedScripts(ImmutableArray<ILuaScriptResourceInfo> scripts, bool pauseExecutionOnError = false, bool verboseLogging = false);
|
/// Executes cached scripts (code) for the given <see cref="ContentPackage"/>.
|
||||||
FluentResults.Result ExecuteLoadedScripts(bool pauseExecutionOnError = false, bool verboseLogging = false);
|
/// </summary>
|
||||||
|
/// <param name="package"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
// [Required]
|
||||||
|
FluentResults.Result ExecuteLoadedScriptsForPackage(ContentPackage package);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes cached scripts (code) for the given collection <see cref="ContentPackage"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="packages"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
// [Required]
|
||||||
|
FluentResults.Result ExecuteLoadedScriptsForPackages(IEnumerable<ContentPackage> packages);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
// [Required]
|
||||||
|
FluentResults.Result ExecuteLoadedScripts();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="package"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
// [Required]
|
||||||
FluentResults.Result DisposePackageResources(ContentPackage package);
|
FluentResults.Result DisposePackageResources(ContentPackage package);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calls dispose on, and clears active refs for, currently running scripts. Does not clear caches.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
FluentResults.Result UnloadActiveScripts();
|
FluentResults.Result UnloadActiveScripts();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unloads all scripts and clears all caches/references.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <remarks>May be functionally equivalent to <see cref="IReusableService.Reset"/></remarks>
|
||||||
FluentResults.Result DisposeAllPackageResources();
|
FluentResults.Result DisposeAllPackageResources();
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
+8
-3
@@ -8,7 +8,7 @@ namespace Barotrauma.LuaCs.Services;
|
|||||||
|
|
||||||
internal delegate void NetMessageReceived(IReadMessage netMessage);
|
internal delegate void NetMessageReceived(IReadMessage netMessage);
|
||||||
|
|
||||||
internal partial interface INetworkingService : IReusableService, ILuaCsNetworking
|
internal partial interface INetworkingService : IReusableService, ILuaCsNetworking, IEntityNetworkingService
|
||||||
{
|
{
|
||||||
bool IsActive { get; }
|
bool IsActive { get; }
|
||||||
bool IsSynchronized { get; }
|
bool IsSynchronized { get; }
|
||||||
@@ -20,6 +20,11 @@ internal partial interface INetworkingService : IReusableService, ILuaCsNetworki
|
|||||||
#elif CLIENT
|
#elif CLIENT
|
||||||
public void Send(IWriteMessage netMessage, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable);
|
public void Send(IWriteMessage netMessage, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable);
|
||||||
#endif
|
#endif
|
||||||
public void RegisterNetVar(INetVar netVar);
|
|
||||||
public void SendNetVar(INetVar netVar);
|
}
|
||||||
|
|
||||||
|
public interface IEntityNetworkingService
|
||||||
|
{
|
||||||
|
public void RegisterNetVar(INetworkSyncEntity netVar);
|
||||||
|
public void SendNetVar(INetworkSyncEntity netVar);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -7,6 +7,7 @@ namespace Barotrauma.LuaCs.Services;
|
|||||||
|
|
||||||
public interface IPackageInfoLookupService : IReusableService
|
public interface IPackageInfoLookupService : IReusableService
|
||||||
{
|
{
|
||||||
|
bool IsPackageEnabled(ContentPackage package);
|
||||||
Task<FluentResults.Result<IPackageInfo>> Lookup(string packageName);
|
Task<FluentResults.Result<IPackageInfo>> Lookup(string packageName);
|
||||||
Task<FluentResults.Result<IPackageInfo>> Lookup(string packageName, ulong steamWorkshopId);
|
Task<FluentResults.Result<IPackageInfo>> Lookup(string packageName, ulong steamWorkshopId);
|
||||||
Task<FluentResults.Result<IPackageInfo>> Lookup(ulong steamWorkshopId);
|
Task<FluentResults.Result<IPackageInfo>> Lookup(ulong steamWorkshopId);
|
||||||
|
|||||||
+17
-20
@@ -9,11 +9,7 @@ using Barotrauma.LuaCs.Data;
|
|||||||
|
|
||||||
namespace Barotrauma.LuaCs.Services;
|
namespace Barotrauma.LuaCs.Services;
|
||||||
|
|
||||||
public interface IPackageManagementService : IReusableService, ILocalizationsResourcesInfo, IConfigsResourcesInfo, IConfigProfilesResourcesInfo, ILuaScriptsResourcesInfo, IAssembliesResourcesInfo
|
public interface IPackageManagementService : IReusableService, IConfigsResourcesInfo, IConfigProfilesResourcesInfo, ILuaScriptsResourcesInfo, IAssembliesResourcesInfo
|
||||||
#if CLIENT
|
|
||||||
,IStylesResourcesInfo
|
|
||||||
#endif
|
|
||||||
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads and parses the provided <see cref="ContentPackage"/> for <see cref="IResourceInfo"/> supported by the current runtime environment.
|
/// Loads and parses the provided <see cref="ContentPackage"/> for <see cref="IResourceInfo"/> supported by the current runtime environment.
|
||||||
@@ -30,6 +26,20 @@ public interface IPackageManagementService : IReusableService, ILocalizationsRes
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IReadOnlyList<(ContentPackage, FluentResults.Result)>> LoadPackagesInfosAsync(IReadOnlyList<ContentPackage> packages);
|
Task<IReadOnlyList<(ContentPackage, FluentResults.Result)>> LoadPackagesInfosAsync(IReadOnlyList<ContentPackage> packages);
|
||||||
IReadOnlyList<ContentPackage> GetAllLoadedPackages();
|
IReadOnlyList<ContentPackage> GetAllLoadedPackages();
|
||||||
|
bool IsPackageLoaded(ContentPackage package);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Filters out resources not suitable for the current environment using the following criteria: <br/>
|
||||||
|
/// - Platform (Operating System)<br/>
|
||||||
|
/// - Target (Client|Server)<br/>
|
||||||
|
/// - Null/Invalid<br/>
|
||||||
|
/// - Dependency Package Registered in PMS<br/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resources"></param>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
ImmutableArray<T> FilterUnloadableResources<T>(IReadOnlyList<T> resources, bool enabledPackagesOnly = false)
|
||||||
|
where T : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo;
|
||||||
void DisposePackageInfos(ContentPackage package);
|
void DisposePackageInfos(ContentPackage package);
|
||||||
void DisposePackagesInfos(IReadOnlyList<ContentPackage> packages);
|
void DisposePackagesInfos(IReadOnlyList<ContentPackage> packages);
|
||||||
FluentResults.Result<IPackageDependency> GetPackageDependencyInfo(ContentPackage ownerPackage, string packageName, ulong steamWorkshopId);
|
FluentResults.Result<IPackageDependency> GetPackageDependencyInfo(ContentPackage ownerPackage, string packageName, ulong steamWorkshopId);
|
||||||
@@ -38,28 +48,15 @@ public interface IPackageManagementService : IReusableService, ILocalizationsRes
|
|||||||
FluentResults.Result<IAssembliesResourcesInfo> GetAssembliesInfos(ContentPackage package, bool onlySupportedResources = true);
|
FluentResults.Result<IAssembliesResourcesInfo> GetAssembliesInfos(ContentPackage package, bool onlySupportedResources = true);
|
||||||
FluentResults.Result<IConfigsResourcesInfo> GetConfigsInfos(ContentPackage package, bool onlySupportedResources = true);
|
FluentResults.Result<IConfigsResourcesInfo> GetConfigsInfos(ContentPackage package, bool onlySupportedResources = true);
|
||||||
FluentResults.Result<IConfigProfilesResourcesInfo> GetConfigProfilesInfos(ContentPackage package, bool onlySupportedResources = true);
|
FluentResults.Result<IConfigProfilesResourcesInfo> GetConfigProfilesInfos(ContentPackage package, bool onlySupportedResources = true);
|
||||||
FluentResults.Result<ILocalizationsResourcesInfo> GetLocalizationsInfos(ContentPackage package, bool onlySupportedResources = true);
|
|
||||||
FluentResults.Result<ILuaScriptsResourcesInfo> GetLuaScriptsInfos(ContentPackage package, bool onlySupportedResources = true);
|
FluentResults.Result<ILuaScriptsResourcesInfo> GetLuaScriptsInfos(ContentPackage package, bool onlySupportedResources = true);
|
||||||
#if CLIENT
|
|
||||||
FluentResults.Result<IStylesResourcesInfo> GetStylesInfos(ContentPackage package, bool onlySupportedResources = true);
|
|
||||||
#endif
|
|
||||||
// collection
|
// collection
|
||||||
FluentResults.Result<IAssembliesResourcesInfo> GetAssembliesInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
FluentResults.Result<IAssembliesResourcesInfo> GetAssembliesInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||||
FluentResults.Result<IConfigsResourcesInfo> GetConfigsInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
FluentResults.Result<IConfigsResourcesInfo> GetConfigsInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||||
FluentResults.Result<IConfigProfilesResourcesInfo> GetConfigProfilesInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
FluentResults.Result<IConfigProfilesResourcesInfo> GetConfigProfilesInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||||
FluentResults.Result<ILocalizationsResourcesInfo> GetLocalizationsInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
FluentResults.Result<ILuaScriptsResourcesInfo> GetLuaScriptsInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||||
FluentResults.Result<ILuaScriptsResourcesInfo> GetLuaScriptsInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
|
||||||
#if CLIENT
|
|
||||||
FluentResults.Result<IStylesResourcesInfo> GetStylesInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Task<FluentResults.Result<IAssembliesResourcesInfo>> GetAssembliesInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
Task<FluentResults.Result<IAssembliesResourcesInfo>> GetAssembliesInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||||
Task<FluentResults.Result<IConfigsResourcesInfo>> GetConfigsInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
Task<FluentResults.Result<IConfigsResourcesInfo>> GetConfigsInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||||
Task<FluentResults.Result<IConfigProfilesResourcesInfo>> GetConfigProfilesInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
Task<FluentResults.Result<IConfigProfilesResourcesInfo>> GetConfigProfilesInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||||
Task<FluentResults.Result<ILocalizationsResourcesInfo>> GetLocalizationsInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
Task<FluentResults.Result<ILuaScriptsResourcesInfo>> GetLuaScriptsInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||||
Task<FluentResults.Result<ILuaScriptsResourcesInfo>> GetLuaScriptsInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
|
||||||
#if CLIENT
|
|
||||||
Task<FluentResults.Result<IStylesResourcesInfo>> GetStylesInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-1
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Immutable;
|
using System;
|
||||||
|
using System.Collections.Immutable;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
@@ -7,6 +8,26 @@ namespace Barotrauma.LuaCs.Services;
|
|||||||
|
|
||||||
public interface IStorageService : IService
|
public interface IStorageService : IService
|
||||||
{
|
{
|
||||||
|
|
||||||
|
bool UseCaching { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes all cached file data.
|
||||||
|
/// </summary>
|
||||||
|
void PurgeCache();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes the data for the supplied file path from the data cache.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="absolutePath"></param>
|
||||||
|
void PurgeFileFromCache(string absolutePath);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes the data from the supplied file paths from the data cache.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="absolutePaths"></param>
|
||||||
|
void PurgeFilesFromCache(params string[] absolutePaths);
|
||||||
|
|
||||||
// -- local game folder storage
|
// -- local game folder storage
|
||||||
FluentResults.Result<XDocument> LoadLocalXml(ContentPackage package, string localFilePath);
|
FluentResults.Result<XDocument> LoadLocalXml(ContentPackage package, string localFilePath);
|
||||||
FluentResults.Result<byte[]> LoadLocalBinary(ContentPackage package, string localFilePath);
|
FluentResults.Result<byte[]> LoadLocalBinary(ContentPackage package, string localFilePath);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Loader;
|
using System.Runtime.Loader;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Barotrauma.Extensions;
|
using Barotrauma.Extensions;
|
||||||
@@ -55,22 +56,131 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
|||||||
//internal
|
//internal
|
||||||
private readonly IAssemblyManagementService _assemblyManagementService;
|
private readonly IAssemblyManagementService _assemblyManagementService;
|
||||||
private readonly Action<AssemblyLoader> _onUnload;
|
private readonly Action<AssemblyLoader> _onUnload;
|
||||||
|
private readonly Func<AssemblyLoadContext, AssemblyName, Assembly> _onResolvingManaged;
|
||||||
|
private readonly Func<Assembly, string, IntPtr> _onResolvingUnmanagedDll;
|
||||||
private readonly ConcurrentDictionary<string, AssemblyDependencyResolver> _dependencyResolvers = new();
|
private readonly ConcurrentDictionary<string, AssemblyDependencyResolver> _dependencyResolvers = new();
|
||||||
private readonly ConcurrentDictionary<AssemblyOrStringKey, AssemblyData> _loadedAssemblyData = new();
|
private readonly ConcurrentDictionary<AssemblyOrStringKey, AssemblyData> _loadedAssemblyData = new();
|
||||||
|
|
||||||
private readonly ThreadLocal<bool> _isResolving = new(static()=>false); // cyclic resolution exit
|
private readonly ThreadLocal<bool> _isResolving = new(static()=>false); // cyclic resolution exit
|
||||||
|
private readonly ThreadLocal<bool> _isResolvingNative = new(static () => false);
|
||||||
|
|
||||||
public AssemblyLoader(IAssemblyManagementService assemblyManagementService,
|
public AssemblyLoader(
|
||||||
|
IAssemblyManagementService assemblyManagementService,
|
||||||
Guid id, string name,
|
Guid id, string name,
|
||||||
bool isReferenceOnlyMode, Action<AssemblyLoader> onUnload)
|
bool isReferenceOnlyMode,
|
||||||
|
Action<AssemblyLoader> onUnload = null)
|
||||||
: base(isCollectible: true, name: name)
|
: base(isCollectible: true, name: name)
|
||||||
{
|
{
|
||||||
_assemblyManagementService = assemblyManagementService;
|
_assemblyManagementService = assemblyManagementService;
|
||||||
Id = id;
|
Id = id;
|
||||||
IsReferenceOnlyMode = isReferenceOnlyMode;
|
IsReferenceOnlyMode = isReferenceOnlyMode;
|
||||||
_onUnload = onUnload;
|
base.Unloading += OnUnload;
|
||||||
if (_onUnload is not null)
|
base.Resolving += OnResolvingManagedAssembly;
|
||||||
base.Unloading += OnUnload;
|
base.ResolvingUnmanagedDll += OnResolvingUnmanagedDll;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IntPtr OnResolvingUnmanagedDll(Assembly invokingAssembly, string assemblyName)
|
||||||
|
{
|
||||||
|
if (IsDisposed)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (_isResolvingNative.Value)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
AreOperationRunning = true;
|
||||||
|
_isResolvingNative.Value = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!_dependencyResolvers.IsEmpty)
|
||||||
|
{
|
||||||
|
foreach (var resolver in _dependencyResolvers)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var path = resolver.Value.ResolveUnmanagedDllToPath(assemblyName);
|
||||||
|
if (path.IsNullOrWhiteSpace())
|
||||||
|
continue;
|
||||||
|
return base.LoadUnmanagedDllFromPath(path);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_onResolvingUnmanagedDll is not null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _onResolvingUnmanagedDll(invokingAssembly, assemblyName);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
AreOperationRunning = false;
|
||||||
|
_isResolvingNative.Value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Assembly OnResolvingManagedAssembly(AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName)
|
||||||
|
{
|
||||||
|
if (IsDisposed)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (_isResolving.Value)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
AreOperationRunning = true;
|
||||||
|
_isResolving.Value = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!_dependencyResolvers.IsEmpty)
|
||||||
|
{
|
||||||
|
foreach (var resolver in _dependencyResolvers)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var path = resolver.Value.ResolveAssemblyToPath(assemblyName);
|
||||||
|
if (path.IsNullOrWhiteSpace())
|
||||||
|
continue;
|
||||||
|
return assemblyLoadContext.LoadFromAssemblyPath(path);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_onResolvingManaged is not null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _onResolvingManaged(assemblyLoadContext, assemblyName);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
AreOperationRunning = false;
|
||||||
|
_isResolving.Value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<MetadataReference> AssemblyReferences
|
public IEnumerable<MetadataReference> AssemblyReferences
|
||||||
@@ -107,10 +217,13 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return res.WithError(new ExceptionalError(ex)
|
res = res.WithError(new ExceptionalError(ex)
|
||||||
.WithMetadata(MetadataType.Sources, path));
|
.WithMetadata(MetadataType.Sources, path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (res.Errors.Any())
|
||||||
|
return FluentResults.Result.Fail(res.Errors);
|
||||||
return FluentResults.Result.Ok();
|
return FluentResults.Result.Ok();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -140,12 +253,15 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
|||||||
|
|
||||||
if (_loadedAssemblyData.ContainsKey(assemblyName))
|
if (_loadedAssemblyData.ContainsKey(assemblyName))
|
||||||
{
|
{
|
||||||
return new Result<Assembly>().WithError(new Error($"The name provided is already assigned to an assembly!")
|
return new Result<Assembly>().WithError(
|
||||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
new Error($"The name provided is already assigned to an assembly!")
|
||||||
.WithMetadata(MetadataType.RootObject, syntaxTrees));
|
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||||
|
.WithMetadata(MetadataType.RootObject, syntaxTrees));
|
||||||
}
|
}
|
||||||
|
|
||||||
var compilationAssemblyName = compileWithInternalAccess ? IAssemblyLoaderService.InternalsAwareAssemblyName : assemblyName;
|
var compilationAssemblyName = compileWithInternalAccess
|
||||||
|
? IAssemblyLoaderService.InternalsAwareAssemblyName
|
||||||
|
: assemblyName;
|
||||||
|
|
||||||
compilationOptions ??= new CSharpCompilationOptions(
|
compilationOptions ??= new CSharpCompilationOptions(
|
||||||
outputKind: OutputKind.DynamicallyLinkedLibrary,
|
outputKind: OutputKind.DynamicallyLinkedLibrary,
|
||||||
@@ -166,34 +282,39 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
|||||||
}
|
}
|
||||||
|
|
||||||
using var asmMemoryStream = new MemoryStream();
|
using var asmMemoryStream = new MemoryStream();
|
||||||
var result = CSharpCompilation.Create(compilationAssemblyName, syntaxTrees, metadataReferences, compilationOptions).Emit(asmMemoryStream);
|
var result = CSharpCompilation
|
||||||
|
.Create(compilationAssemblyName, syntaxTrees, metadataReferences, compilationOptions)
|
||||||
|
.Emit(asmMemoryStream);
|
||||||
if (!result.Success)
|
if (!result.Success)
|
||||||
{
|
{
|
||||||
var res = new FluentResults.Result().WithError(
|
var res = new FluentResults.Result().WithError(
|
||||||
new Error($"Compilation failed for assembly {assemblyName}!")
|
new Error($"Compilation failed for assembly {assemblyName}!")
|
||||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||||
.WithMetadata(MetadataType.RootObject, syntaxTrees));
|
.WithMetadata(MetadataType.RootObject, syntaxTrees));
|
||||||
var failuresDiag = result.Diagnostics.Where(d => d.IsWarningAsError || d.Severity == DiagnosticSeverity.Error);
|
var failuresDiag =
|
||||||
|
result.Diagnostics.Where(d => d.IsWarningAsError || d.Severity == DiagnosticSeverity.Error);
|
||||||
foreach (var diag in failuresDiag)
|
foreach (var diag in failuresDiag)
|
||||||
{
|
{
|
||||||
res = res.WithError(new Error(diag.GetMessage())
|
res = res.WithError(new Error(diag.GetMessage())
|
||||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||||
.WithMetadata(MetadataType.ExceptionDetails, diag.Descriptor.Description));
|
.WithMetadata(MetadataType.ExceptionDetails, diag.Descriptor.Description));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
asmMemoryStream.Seek(0, SeekOrigin.Begin);
|
asmMemoryStream.Seek(0, SeekOrigin.Begin);
|
||||||
try
|
var data = new AssemblyData(LoadFromStream(asmMemoryStream), asmMemoryStream.ToArray());
|
||||||
{
|
_loadedAssemblyData[data.Assembly] = data;
|
||||||
var data = new AssemblyData(LoadFromStream(asmMemoryStream), asmMemoryStream.ToArray());
|
return new Result<Assembly>().WithSuccess($"Compiled assembly {assemblyName} successful.")
|
||||||
_loadedAssemblyData[data.Assembly] = data;
|
.WithValue(data.Assembly);
|
||||||
return new Result<Assembly>().WithSuccess($"Compiled assembly {assemblyName} successful.").WithValue(data.Assembly);
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
catch (Exception ex)
|
{
|
||||||
{
|
return new FluentResults.Result().WithError(new ExceptionalError(ex)
|
||||||
return new FluentResults.Result().WithError(new ExceptionalError(ex));
|
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||||
}
|
.WithMetadata(MetadataType.RootObject, assemblyName)
|
||||||
|
.WithMetadata(MetadataType.Sources, syntaxTrees));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -211,7 +332,7 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (assemblyFilePath.IsNullOrWhiteSpace())
|
if (assemblyFilePath.IsNullOrWhiteSpace())
|
||||||
return new Result<Assembly>().WithError(new Error($"The path provided is null!"));
|
return new Result<Assembly>().WithError(new Error($"The path provided is empty."));
|
||||||
|
|
||||||
if (additionalDependencyPaths.Any())
|
if (additionalDependencyPaths.Any())
|
||||||
{
|
{
|
||||||
@@ -219,7 +340,7 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
|||||||
if (!r.IsFailed)
|
if (!r.IsFailed)
|
||||||
{
|
{
|
||||||
// we have errors, loading may not work.
|
// we have errors, loading may not work.
|
||||||
return FluentResults.Result.Fail(new Error($"Failed to load dependency paths")
|
return FluentResults.Result.Fail(new Error($"Failed to load dependency paths.")
|
||||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||||
.WithMetadata(MetadataType.RootObject, assemblyFilePath))
|
.WithMetadata(MetadataType.RootObject, assemblyFilePath))
|
||||||
.WithErrors(r.Errors);
|
.WithErrors(r.Errors);
|
||||||
@@ -240,61 +361,51 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
|||||||
{
|
{
|
||||||
var assembly = LoadFromAssemblyPath(sanitizedFilePath);
|
var assembly = LoadFromAssemblyPath(sanitizedFilePath);
|
||||||
_loadedAssemblyData[assembly] = new AssemblyData(assembly, sanitizedFilePath);
|
_loadedAssemblyData[assembly] = new AssemblyData(assembly, sanitizedFilePath);
|
||||||
return new Result<Assembly>().WithSuccess($"Loaded assembly'{assembly.GetName()}'").WithValue(assembly);
|
return new Result<Assembly>().WithSuccess($"Loaded assembly '{assembly.GetName()}'").WithValue(assembly);
|
||||||
}
|
|
||||||
catch (ArgumentNullException ane)
|
|
||||||
{
|
|
||||||
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(ane)
|
|
||||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
|
||||||
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
|
|
||||||
.WithMetadata(MetadataType.ExceptionDetails, ane.Message)
|
|
||||||
.WithMetadata(MetadataType.StackTrace, ane.StackTrace));
|
|
||||||
}
|
|
||||||
catch (ArgumentException ae)
|
|
||||||
{
|
|
||||||
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(ae)
|
|
||||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
|
||||||
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
|
|
||||||
.WithMetadata(MetadataType.ExceptionDetails, ae.Message)
|
|
||||||
.WithMetadata(MetadataType.StackTrace, ae.StackTrace));
|
|
||||||
}
|
|
||||||
catch (FileLoadException fle)
|
|
||||||
{
|
|
||||||
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(fle)
|
|
||||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
|
||||||
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
|
|
||||||
.WithMetadata(MetadataType.ExceptionDetails, fle.Message)
|
|
||||||
.WithMetadata(MetadataType.StackTrace, fle.StackTrace));
|
|
||||||
}
|
}
|
||||||
catch (FileNotFoundException fnfe)
|
catch (FileNotFoundException fnfe)
|
||||||
{
|
{
|
||||||
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(fnfe)
|
// last attempt
|
||||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
try
|
||||||
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
|
{
|
||||||
.WithMetadata(MetadataType.ExceptionDetails, fnfe.Message)
|
var assemblyName = new AssemblyName(System.IO.Path.GetFileName(sanitizedFilePath));
|
||||||
.WithMetadata(MetadataType.StackTrace, fnfe.StackTrace));
|
foreach (var resolver in _dependencyResolvers)
|
||||||
}
|
{
|
||||||
catch (BadImageFormatException bife)
|
try
|
||||||
{
|
{
|
||||||
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(bife)
|
var path = resolver.Value.ResolveAssemblyToPath(assemblyName);
|
||||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
return base.LoadFromAssemblyPath(path);
|
||||||
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
|
}
|
||||||
.WithMetadata(MetadataType.ExceptionDetails, bife.Message)
|
catch
|
||||||
.WithMetadata(MetadataType.StackTrace, bife.StackTrace));
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return GenerateExceptionReturn(fnfe);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return GenerateExceptionReturn(fnfe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(e)
|
return GenerateExceptionReturn(e);
|
||||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
|
||||||
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
|
|
||||||
.WithMetadata(MetadataType.ExceptionDetails, e.Message)
|
|
||||||
.WithMetadata(MetadataType.StackTrace, e.StackTrace));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
AreOperationRunning = false;
|
AreOperationRunning = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FluentResults.Result<Assembly> GenerateExceptionReturn<T>(T exception) where T : Exception
|
||||||
|
{
|
||||||
|
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(exception)
|
||||||
|
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||||
|
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
|
||||||
|
.WithMetadata(MetadataType.ExceptionDetails, exception.Message)
|
||||||
|
.WithMetadata(MetadataType.StackTrace, exception.StackTrace));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FluentResults.Result<Assembly> GetAssemblyByName(string assemblyName)
|
public FluentResults.Result<Assembly> GetAssemblyByName(string assemblyName)
|
||||||
@@ -303,7 +414,7 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
|||||||
return FluentResults.Result.Fail(new Error($"Loader is disposed!"));
|
return FluentResults.Result.Fail(new Error($"Loader is disposed!"));
|
||||||
if (assemblyName.IsNullOrWhiteSpace())
|
if (assemblyName.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
return FluentResults.Result.Fail(new Error($"Assembly name is null")
|
return FluentResults.Result.Fail(new Error($"Assembly name is empty.")
|
||||||
.WithMetadata(MetadataType.ExceptionObject, this));
|
.WithMetadata(MetadataType.ExceptionObject, this));
|
||||||
}
|
}
|
||||||
AreOperationRunning = true;
|
AreOperationRunning = true;
|
||||||
@@ -311,7 +422,7 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
|||||||
{
|
{
|
||||||
if (_loadedAssemblyData.TryGetValue(assemblyName, out var data))
|
if (_loadedAssemblyData.TryGetValue(assemblyName, out var data))
|
||||||
{
|
{
|
||||||
return new Result<Assembly>().WithSuccess(new Success($"Assembly found")).WithValue(data.Assembly);
|
return new Result<Assembly>().WithSuccess(new Success($"Assembly found.")).WithValue(data.Assembly);
|
||||||
}
|
}
|
||||||
|
|
||||||
// search any assemblies that were background loaded and we're unaware of.
|
// search any assemblies that were background loaded and we're unaware of.
|
||||||
@@ -332,11 +443,11 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
|||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Result<Assembly>().WithSuccess(new Success($"Assembly found")).WithValue(assembly1);
|
return new Result<Assembly>().WithSuccess(new Success($"Assembly found.")).WithValue(assembly1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return FluentResults.Result.Fail(new Error($"Assembly named { assemblyName } not found!"));
|
return FluentResults.Result.Fail(new Error($"Assembly named '{ assemblyName }' not found!"));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -420,60 +531,97 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
|||||||
return; // we don't want to invoke events twice nor cause strong GC handles.
|
return; // we don't want to invoke events twice nor cause strong GC handles.
|
||||||
IsDisposed = true;
|
IsDisposed = true;
|
||||||
this.Unload();
|
this.Unload();
|
||||||
|
this.DisposeInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
~AssemblyLoader()
|
||||||
|
{
|
||||||
|
this.DisposeInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUnload(AssemblyLoadContext context)
|
||||||
|
{
|
||||||
|
// Try to wait for loading ops on other threads if they happen to occur with a timeout.
|
||||||
|
// This should be an edge, should it even occur.
|
||||||
|
DateTime timeout = DateTime.Now.AddSeconds(2);
|
||||||
|
while (timeout > DateTime.Now)
|
||||||
|
{
|
||||||
|
if (!AreOperationRunning)
|
||||||
|
break;
|
||||||
|
Thread.Sleep(1000/Timing.FixedUpdateRate-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var wf = new WeakReference<IAssemblyLoaderService>(this);
|
||||||
|
_onUnload?.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisposeInternal()
|
||||||
|
{
|
||||||
|
IsDisposed = true;
|
||||||
|
base.Resolving -= OnResolvingManagedAssembly;
|
||||||
|
base.ResolvingUnmanagedDll -= OnResolvingUnmanagedDll;
|
||||||
|
base.Unloading -= OnUnload;
|
||||||
|
this._dependencyResolvers.Clear();
|
||||||
|
this._loadedAssemblyData.Clear();
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Assembly Load(AssemblyName assemblyName)
|
protected override Assembly Load(AssemblyName assemblyName)
|
||||||
{
|
{
|
||||||
if (_isResolving.Value)
|
if (IsDisposed)
|
||||||
return null;
|
return null;
|
||||||
|
AreOperationRunning = true;
|
||||||
_isResolving.Value = true;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_loadedAssemblyData.TryGetValue(assemblyName.FullName, out var data))
|
if (_loadedAssemblyData.TryGetValue(assemblyName.FullName, out var assembly))
|
||||||
return data.Assembly;
|
return assembly.Assembly;
|
||||||
var ids = new[] { this.Id };
|
|
||||||
if (_assemblyManagementService.GetLoadedAssembly(assemblyName, in ids) is { IsSuccess: true } ret)
|
|
||||||
return ret.Value;
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
catch (ArgumentNullException _)
|
catch
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_isResolving.Value = false;
|
AreOperationRunning = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the default import resolver since native libraries are niche and not blocking for unloading.
|
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
|
||||||
// Implement if conflicts become an issue.
|
|
||||||
/*protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
|
|
||||||
{
|
{
|
||||||
// Implement NativeLibrary::InternalLoadUnmanagedDll()
|
if (IsDisposed)
|
||||||
throw new NotImplementedException();
|
return 0;
|
||||||
}*/
|
|
||||||
|
|
||||||
private void OnUnload(AssemblyLoadContext context)
|
GCHandle? handle = null;
|
||||||
{
|
AreOperationRunning = true;
|
||||||
IsDisposed = true;
|
try
|
||||||
|
|
||||||
// Try to wait for loading ops on other threads if they happen to occur.
|
|
||||||
// Minor race condition on the loop exit but this loader is not intended to be thread-safe by design, this is just to cover edge cases.
|
|
||||||
DateTime timeout = DateTime.Now.AddSeconds(5);
|
|
||||||
while (timeout > DateTime.Now)
|
|
||||||
{
|
{
|
||||||
if (!AreOperationRunning)
|
if (_loadedAssemblyData.TryGetValue(unmanagedDllName, out var assemblyData))
|
||||||
break;
|
{
|
||||||
|
handle = GCHandle.Alloc(assemblyData.Assembly, GCHandleType.Pinned);
|
||||||
|
nint asmPtr = GCHandle.ToIntPtr(handle.Value);
|
||||||
|
return asmPtr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
AreOperationRunning = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (handle.HasValue)
|
||||||
|
handle.Value.Free();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored. We just want to ensure that free is called.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Unloading -= OnUnload;
|
return 0;
|
||||||
var wf = new WeakReference<IAssemblyLoaderService>(this);
|
|
||||||
_onUnload?.Invoke(this);
|
|
||||||
this._dependencyResolvers.Clear();
|
|
||||||
this._loadedAssemblyData.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly record struct AssemblyData
|
private readonly record struct AssemblyData
|
||||||
@@ -538,7 +686,7 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
|||||||
|
|
||||||
public int GetHashCode(AssemblyOrStringKey obj)
|
public int GetHashCode(AssemblyOrStringKey obj)
|
||||||
{
|
{
|
||||||
return obj.HashCode;
|
return this.HashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static implicit operator AssemblyOrStringKey(Assembly assembly) => new AssemblyOrStringKey(assembly);
|
public static implicit operator AssemblyOrStringKey(Assembly assembly) => new AssemblyOrStringKey(assembly);
|
||||||
|
|||||||
Reference in New Issue
Block a user