diff --git a/Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/Processing/ModConfigService.cs b/Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/Processing/ModConfigService.cs deleted file mode 100644 index 3c59ac10e..000000000 --- a/Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/Processing/ModConfigService.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using System.Xml.Linq; -using Barotrauma.LuaCs.Data; -using FluentResults; - -namespace Barotrauma.LuaCs.Services.Processing; - -public partial class ModConfigService -{ - private partial async Task> GetModConfigInfoAsync(ContentPackage package, XElement root) - { - var asm = root.GetChildElements("Assembly").ToImmutableArray(); - var cfg = root.GetChildElements("Config").ToImmutableArray(); - var lua = root.GetChildElements("Lua").ToImmutableArray(); - - return FluentResults.Result.Ok(new ModConfigInfo() - { - Package = package, - PackageName = package.Name, - Assemblies = asm.Any() ? GetAssemblies(package, asm) : ImmutableArray.Empty, - Configs = cfg.Any() ? GetConfigs(package, cfg) : ImmutableArray.Empty, - ConfigProfiles = cfg.Any() ? GetConfigProfiles(package, cfg) : ImmutableArray.Empty, - LuaScripts = lua.Any() ? GetLuaScripts(package, lua) : ImmutableArray.Empty - }); - } -} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs index 6b94165f4..4714df7cc 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs @@ -54,10 +54,7 @@ namespace Barotrauma // TODO: INetworkingService // TODO: [Resource Converter/Parser Services] - // Loaders and Processors (yes the naming is reversed, oops). - _servicesProvider.RegisterServiceType, ModConfigService>(ServiceLifetime.Transient); - _servicesProvider.RegisterServiceType, ModConfigService>(ServiceLifetime.Transient); - _servicesProvider.RegisterServiceType(ServiceLifetime.Transient); + _servicesProvider.RegisterServiceType(ServiceLifetime.Transient); // service config data _servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/ConfigIOService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/ConfigIOService.cs deleted file mode 100644 index b82098744..000000000 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/ConfigIOService.cs +++ /dev/null @@ -1,278 +0,0 @@ -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>> 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.Select(fp => fp.FullPath)]); - if (infos.IsDefaultOrEmpty) - return FluentResults.Result.Fail($"{nameof(TryParseResourceAsync)}: No resources found."); - - var errList = new List(); - - 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 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( - 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)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>>> TryParseResourcesAsync(IEnumerable sources) - { - var results = new ConcurrentQueue>>(); - - var src = sources.ToImmutableArray(); - if (!src.Any()) - return ImmutableArray>>.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>> 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(); - - 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 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( - 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)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>>> TryParseResourcesAsync(IEnumerable sources) - { - var results = new ConcurrentQueue>>(); - - var src = sources.ToImmutableArray(); - if (!src.Any()) - return ImmutableArray>>.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 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>> 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> 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. - } -} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/IConfigIOService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/IConfigIOService.cs deleted file mode 100644 index 72d1dff10..000000000 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/IConfigIOService.cs +++ /dev/null @@ -1,15 +0,0 @@ -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, - IParserServiceAsync>, - IParserServiceAsync> -{ - Task SaveConfigDataLocal(ContentPackage package, string configName, XElement serializedValue); - Task>> LoadConfigDataFromLocal(ContentPackage package, string configName); -} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/ModConfigService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/ModConfigService.cs index 60dcbd4e1..0c3bce1f1 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/ModConfigService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/ModConfigService.cs @@ -9,6 +9,8 @@ using System.Threading.Tasks; using System.Xml.Linq; using Barotrauma.LuaCs.Data; using FluentResults; +using Microsoft.Toolkit.Diagnostics; +using MoonSharp.VsCodeDebugger.SDK; namespace Barotrauma.LuaCs.Services.Processing; @@ -33,7 +35,7 @@ public sealed class ModConfigService : IModConfigService _configProfileParserService = configProfileParserService; } - #region Disposal + #region Dispose public void Dispose() { @@ -44,15 +46,14 @@ public sealed class ModConfigService : IModConfigService public bool IsDisposed { get => ModUtils.Threading.GetBool(ref _isDisposed); - protected set => ModUtils.Threading.SetBool(ref _isDisposed, value); + private set => ModUtils.Threading.SetBool(ref _isDisposed, value); } #endregion public async Task> CreateConfigAsync(ContentPackage src) { - if (src is null) - ArgumentNullException.ThrowIfNull($"{nameof(CreateConfigAsync)}: Source is null."); + Guard.IsNotNull(src, nameof(src)); if (await TryGetModConfigXmlAsync(src) is { IsSuccess: true, Value: { } config }) { @@ -64,20 +65,23 @@ public sealed class ModConfigService : IModConfigService public async Task Config)>> CreateConfigsAsync(ImmutableArray src) { - var builder = ImmutableArray.CreateBuilder<(ContentPackage Source, Result Config)>(); + var builder = new ConcurrentQueue<(ContentPackage Source, Result Config)>(); - foreach (var package in src) + await src.ParallelForEachAsync(async package => { - builder.Add((package, await CreateConfigAsync(package))); - } - - return builder.ToImmutable(); + var res = await CreateConfigAsync(package); + builder.Enqueue((package, res)); + }); + + return builder.OrderBy(pkg => src.IndexOf(pkg.Source)).ToImmutableArray(); } //--- Helpers private async Task> TryGetModConfigXmlAsync(ContentPackage src) { - + return await _storageService.LoadPackageXmlAsync(src, "ModConfig.xml") is { IsSuccess: true, Value: { Root: {} config} } + ? FluentResults.Result.Ok(config) + : FluentResults.Result.Fail("ModConfig.xml not found"); } private async Task> CreateFromConfigXmlAsync(XElement src)