IT BUILDS!!!

- Removed LocalizationServices and other sus things.
- Rewrote AssemblyLoader
[In Progress] SafeStorageService
[In Progress] LuaScriptLoader
This commit is contained in:
MapleWheels
2025-03-30 06:20:45 -04:00
committed by Maplewheels
parent 52d920d969
commit c6713f37bb
67 changed files with 3336 additions and 1283 deletions
@@ -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.
}
}
@@ -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);
}
@@ -69,12 +69,8 @@ public partial class ModConfigService : IConverterServiceAsync<ContentPackage, I
LuaScripts = lua,
Configs = ImmutableArray<IConfigResourceInfo>.Empty,
ConfigProfiles = ImmutableArray<IConfigProfileResourceInfo>.Empty,
Localizations = ImmutableArray<ILocalizationResourceInfo>.Empty,
Package = src,
PackageName = src.Name
#if CLIENT
,Styles = ImmutableArray<IStylesResourceInfo>.Empty
#endif
};
}
catch (Exception e)
@@ -84,40 +80,6 @@ public partial class ModConfigService : IConverterServiceAsync<ContentPackage, I
}
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)
{
@@ -265,7 +227,7 @@ public partial class ModConfigService : IConverterServiceAsync<ContentPackage, I
// get platform, culture and target architecture
var info = GetElementsAttributesData(file.Item1, file.Item2.First());
builder.Add(new LuaScriptScriptResourceInfo()
builder.Add(new LuaScriptsResourceInfo()
{
Dependencies = deps,
Optional = info.IsOptional,
@@ -539,9 +501,16 @@ public partial class ModConfigService : IConverterServiceAsync<ContentPackage, I
SupportedTargets = Target.Client
});
}
var sharedFound = _storageService.FindFilesInPackage(src, "CSharp/Shared", "*.cs", true)
is { IsSuccess: true, Value: { IsDefaultOrEmpty: false } filesCssShared };
var sharedCsBuilder = ImmutableArray.CreateBuilder<string>();
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
if (_storageService.FindFilesInPackage(src, "CSharp/Server", "*.cs", true)
@@ -550,7 +519,7 @@ public partial class ModConfigService : IConverterServiceAsync<ContentPackage, I
builder.Add(new AssemblyResourceInfo()
{
Dependencies = ImmutableArray<IPackageDependency>.Empty,
FilePaths = sharedFound ? filesCssServer.Concat(filesCssShared).ToImmutableArray() : filesCssServer,
FilePaths = sharedFound ? filesCssServer.Concat(filesCssShared).ToImmutableArray() : filesCssServer,
FriendlyName = "CssServer",
InternalName = "CssServer",
IsScript = true,
@@ -594,7 +563,7 @@ public partial class ModConfigService : IConverterServiceAsync<ContentPackage, I
if (_storageService.FindFilesInPackage(src, "Lua", "*.lua", true)
is { IsSuccess: true, Value: { IsDefaultOrEmpty: false } fileAll })
{
builder.Add(new LuaScriptScriptResourceInfo()
builder.Add(new LuaScriptsResourceInfo()
{
Dependencies = ImmutableArray<IPackageDependency>.Empty,
FilePaths = fileAll.Where(path => !path.Contains("Autorun")).ToImmutableArray(),
@@ -607,7 +576,7 @@ public partial class ModConfigService : IConverterServiceAsync<ContentPackage, I
SupportedTargets = Target.Client | Target.Server
});
builder.Add(new LuaScriptScriptResourceInfo()
builder.Add(new LuaScriptsResourceInfo()
{
Dependencies = ImmutableArray<IPackageDependency>.Empty,
FilePaths = fileAll.Where(path => path.Contains("Autorun")).ToImmutableArray(),
@@ -9,7 +9,6 @@ public partial class ResourceInfoArrayPacker :
IProcessorService<IReadOnlyList<IAssemblyResourceInfo>, IAssembliesResourcesInfo>,
IProcessorService<IReadOnlyList<IConfigResourceInfo>, IConfigsResourcesInfo>,
IProcessorService<IReadOnlyList<IConfigProfileResourceInfo>, IConfigProfilesResourcesInfo>,
IProcessorService<IReadOnlyList<ILocalizationResourceInfo>, ILocalizationsResourcesInfo>,
IProcessorService<IReadOnlyList<ILuaScriptResourceInfo>, ILuaScriptsResourcesInfo>
{
private bool _isDisposed;
@@ -28,11 +27,6 @@ public partial class ResourceInfoArrayPacker :
return new ConfigProfilesResourcesInfo(src.ToImmutableArray());
}
public ILocalizationsResourcesInfo Process(IReadOnlyList<ILocalizationResourceInfo> src)
{
return new LocalizationResourcesInfo(src.ToImmutableArray());
}
public ILuaScriptsResourcesInfo Process(IReadOnlyList<ILuaScriptResourceInfo> src)
{
return new LuaScriptsResourcesInfo(src.ToImmutableArray());