[Milestone] PackageManagementService completed.
- ContentPackageInfoLookup Service completed. - Implemented ModConfigService.cs - Implemented some of the resource processors.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public class ConfigService : IConfigService
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.LuaCs.Events;
|
||||
using Barotrauma.Steam;
|
||||
using FluentResults;
|
||||
using OneOf;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides <see cref="IPackageInfo"/> resolution for dynamically locating the best matching package at the time of consumption.
|
||||
/// </summary>
|
||||
public sealed class ContentPackageInfoLookup : IPackageInfoLookupService, IEventEnabledPackageListChanged, IEventAllPackageListChanged
|
||||
{
|
||||
#region INTERNAL
|
||||
|
||||
// packageinfo query data
|
||||
private readonly ConcurrentDictionary<OneOf.OneOf<string, ulong, (string, ulong)>, IPackageInfo> _packageInfoMap = new();
|
||||
// package query data
|
||||
private readonly ConcurrentDictionary<uint, ImmutableArray<ContentPackage>> _packageIdGroups = new();
|
||||
private readonly ConcurrentDictionary<ContentPackage, ImmutableArray<uint>> _reversePackageIdGroups = new();
|
||||
private readonly HashSet<ContentPackage> _enabledPackages;
|
||||
private readonly HashSet<ContentPackage> _allPackages;
|
||||
// threading
|
||||
private readonly AsyncReaderWriterLock _packageIdGroupsLock = new();
|
||||
private readonly AsyncReaderWriterLock _packageSetsLock = new();
|
||||
// services
|
||||
private readonly IEventService _eventService;
|
||||
private readonly IPackageListRetrievalService _packageListRetrievalService;
|
||||
|
||||
private int _isDisposed = 0;
|
||||
private uint _idCounter = 0;
|
||||
|
||||
// returns ++_idCounter;
|
||||
private uint GetNextId() => Interlocked.Increment(ref _idCounter);
|
||||
|
||||
private ContentPackage GetBestMatchPackage(IPackageInfo packageInfo)
|
||||
{
|
||||
if (packageInfo is null)
|
||||
return null;
|
||||
if (!_packageIdGroups.TryGetValue(packageInfo.Id, out var packageGroup)
|
||||
|| packageGroup.IsDefaultOrEmpty)
|
||||
return null;
|
||||
if (packageGroup.Length == 1)
|
||||
return packageGroup[0];
|
||||
|
||||
bool nameGood = !packageInfo.Name.IsNullOrWhiteSpace();
|
||||
|
||||
// try by enabled
|
||||
var prev = packageGroup;
|
||||
|
||||
var packList = packageGroup;
|
||||
using (_packageSetsLock.AcquireReaderLock().GetAwaiter().GetResult())
|
||||
{
|
||||
packList = packList
|
||||
.Where(p => p is not null && _enabledPackages.Contains(p))
|
||||
.ToImmutableArray();
|
||||
}
|
||||
|
||||
if (ReturnValue())
|
||||
return packList[0];
|
||||
|
||||
// try by steam id
|
||||
if (packageInfo.SteamWorkshopId != 0)
|
||||
{
|
||||
packList = packList
|
||||
.Where(p => p.TryExtractSteamWorkshopId(out var sId) && sId.Value == packageInfo.SteamWorkshopId)
|
||||
.ToImmutableArray();
|
||||
|
||||
if (ReturnValue())
|
||||
return packList[0];
|
||||
}
|
||||
|
||||
// try by name
|
||||
if (nameGood)
|
||||
{
|
||||
packList = packList
|
||||
.Where(p => p.Name == packageInfo.Name)
|
||||
.ToImmutableArray();
|
||||
|
||||
if (ReturnValue())
|
||||
return packList[0];
|
||||
}
|
||||
|
||||
// try by localmods
|
||||
packList = packList.Where(p => p.Path.ToLowerInvariant().Contains("localmods"))
|
||||
.ToImmutableArray();
|
||||
|
||||
if (ReturnValue())
|
||||
return packList[0];
|
||||
|
||||
// get the first in the list
|
||||
return packList.First();
|
||||
|
||||
bool ReturnValue()
|
||||
{
|
||||
if (packList.IsDefaultOrEmpty)
|
||||
packList = prev;
|
||||
else if (packList.Length == 1)
|
||||
return true;
|
||||
else
|
||||
prev = packList;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SyncPackagesLists(IReadOnlyList<ContentPackage> enabledPackages,
|
||||
IReadOnlyList<ContentPackage> allPackages)
|
||||
{
|
||||
if (enabledPackages is null || allPackages is null)
|
||||
return;
|
||||
|
||||
// take all locks
|
||||
using var l1 = await _packageIdGroupsLock.AcquireWriterLock();
|
||||
using var l2 = await _packageSetsLock.AcquireWriterLock();
|
||||
|
||||
// calc diffs
|
||||
var toAddAll = allPackages.Except(_allPackages).ToHashSet();
|
||||
var toAddEnabled = enabledPackages.Except(_enabledPackages).ToHashSet();
|
||||
var toRemoveAll = _allPackages.Except(allPackages).ToHashSet();
|
||||
var toRemoveEnabled = _enabledPackages.Except(enabledPackages).ToHashSet();
|
||||
|
||||
// remove old
|
||||
if (toRemoveAll.Any())
|
||||
{
|
||||
foreach (var package in toRemoveAll)
|
||||
{
|
||||
if (package is null)
|
||||
continue;
|
||||
|
||||
_allPackages.Remove(package);
|
||||
|
||||
// try to find id lookup
|
||||
if (!_reversePackageIdGroups.TryGetValue(package, out var idGroup))
|
||||
continue;
|
||||
|
||||
// found packs
|
||||
if (!idGroup.IsDefaultOrEmpty)
|
||||
{
|
||||
foreach (var id in idGroup)
|
||||
{
|
||||
if (!_packageIdGroups.TryGetValue(id, out var packageGroup)
|
||||
|| packageGroup.IsDefaultOrEmpty)
|
||||
continue;
|
||||
_packageIdGroups[id] = packageGroup.RemoveAll(p => toRemoveAll.Contains(p));
|
||||
}
|
||||
}
|
||||
|
||||
// remove ref
|
||||
_reversePackageIdGroups.Remove(package, out _);
|
||||
}
|
||||
}
|
||||
|
||||
if (toRemoveEnabled.Any())
|
||||
{
|
||||
foreach (var package in toRemoveEnabled)
|
||||
{
|
||||
if (package is null)
|
||||
continue;
|
||||
_enabledPackages.Remove(package);
|
||||
}
|
||||
}
|
||||
|
||||
// add new
|
||||
if (toAddAll.Any())
|
||||
{
|
||||
foreach (var package in toAddAll)
|
||||
{
|
||||
if (package is null)
|
||||
continue;
|
||||
|
||||
_allPackages.Add(package);
|
||||
|
||||
var steamId = package.TryExtractSteamWorkshopId(out var id) ? id.Value : 0;
|
||||
IPackageInfo packageInfo;
|
||||
Queue<uint> idListsToAdd = new();
|
||||
if (!package.Name.IsNullOrWhiteSpace() && steamId > 0)
|
||||
{
|
||||
// combined key
|
||||
packageInfo = GetOrCreateInfoForMap(package, (package.Name, steamId));
|
||||
AddToPackageIdGroups(packageInfo.Id, package);
|
||||
// string key
|
||||
packageInfo = GetOrCreateInfoForMap(package, package.Name);
|
||||
AddToPackageIdGroups(packageInfo.Id, package);
|
||||
// steamId key
|
||||
packageInfo = GetOrCreateInfoForMap(package, steamId);
|
||||
AddToPackageIdGroups(packageInfo.Id, package);
|
||||
}
|
||||
|
||||
// try find in the existing list, or make a new one
|
||||
IPackageInfo GetOrCreateInfoForMap(ContentPackage package, OneOf.OneOf<string, ulong, (string, ulong)> infoKey)
|
||||
{
|
||||
return _packageInfoMap.TryGetValue(infoKey, out var pInfo)
|
||||
? pInfo
|
||||
: new PackageInfo(package, GetNextId(), GetBestMatchPackage);
|
||||
}
|
||||
|
||||
// add to package lookups
|
||||
void AddToPackageIdGroups(uint id, ContentPackage package)
|
||||
{
|
||||
if (_packageIdGroups.TryGetValue(id, out var packageGroup))
|
||||
{
|
||||
if (!packageGroup.Contains(package))
|
||||
_packageIdGroups[id] = packageGroup.Add(package);
|
||||
}
|
||||
else
|
||||
_packageIdGroups[id] = new[] { package }.ToImmutableArray();
|
||||
|
||||
if (_reversePackageIdGroups.TryGetValue(package, out var idGroup))
|
||||
{
|
||||
if (!idGroup.Contains(id))
|
||||
_reversePackageIdGroups[package] = idGroup.Add(id);
|
||||
}
|
||||
else
|
||||
_reversePackageIdGroups[package] = new[] { id }.ToImmutableArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toAddEnabled.Any())
|
||||
{
|
||||
foreach (var package in toAddEnabled)
|
||||
{
|
||||
if (package is null)
|
||||
continue;
|
||||
_enabledPackages.Add(package);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Result<IPackageInfo>> LookupInternal(OneOf.OneOf<string, ulong, (string, ulong)> infoKey)
|
||||
{
|
||||
using (await _packageIdGroupsLock.AcquireReaderLock())
|
||||
{
|
||||
if (_packageInfoMap.TryGetValue(infoKey, out var packageInfo))
|
||||
return FluentResults.Result.Ok(packageInfo);
|
||||
}
|
||||
|
||||
// change to write lock
|
||||
using (await _packageIdGroupsLock.AcquireWriterLock())
|
||||
{
|
||||
// create one
|
||||
var packageInfo = infoKey.Match<IPackageInfo>(
|
||||
sPackName => new PackageInfo(sPackName, GetNextId(), GetBestMatchPackage),
|
||||
uSteamId => new PackageInfo(uSteamId, GetNextId(), GetBestMatchPackage),
|
||||
cKey => new PackageInfo(cKey.Item1, cKey.Item2, GetNextId(), GetBestMatchPackage)
|
||||
);
|
||||
_packageInfoMap[infoKey] = packageInfo;
|
||||
// empty array
|
||||
_packageIdGroups[packageInfo.Id] = ImmutableArray<ContentPackage>.Empty;
|
||||
return FluentResults.Result.Ok(packageInfo);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public ContentPackageInfoLookup(IEventService eventService, IPackageListRetrievalService packageListRetrievalService)
|
||||
{
|
||||
_eventService = eventService ?? throw new ArgumentNullException(
|
||||
$"{nameof(ContentPackageInfoLookup)}: {nameof(eventService)} cannot be null.");
|
||||
_packageListRetrievalService = packageListRetrievalService ?? throw new ArgumentNullException(nameof(packageListRetrievalService));
|
||||
this._enabledPackages = new HashSet<ContentPackage>();
|
||||
this._allPackages = new HashSet<ContentPackage>();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsDisposed = true;
|
||||
// locks
|
||||
using var l1 = _packageIdGroupsLock.AcquireWriterLock().GetAwaiter().GetResult();
|
||||
using var l2 = _packageSetsLock.AcquireWriterLock().GetAwaiter().GetResult();
|
||||
|
||||
_eventService.Unsubscribe<IEventEnabledPackageListChanged>(this);
|
||||
_eventService.Unsubscribe<IEventAllPackageListChanged>(this);
|
||||
|
||||
_packageIdGroups.Clear();
|
||||
_packageInfoMap.Clear();
|
||||
_reversePackageIdGroups.Clear();
|
||||
}
|
||||
|
||||
public bool IsDisposed
|
||||
{
|
||||
get => ModUtils.Threading.GetBool(ref _isDisposed);
|
||||
private set => ModUtils.Threading.SetBool(ref _isDisposed, value);
|
||||
}
|
||||
|
||||
public FluentResults.Result Reset()
|
||||
{
|
||||
if (IsDisposed)
|
||||
return FluentResults.Result.Fail($"Service is disposed.");
|
||||
|
||||
using var l1 = _packageIdGroupsLock.AcquireWriterLock().GetAwaiter().GetResult();
|
||||
using var l2 = _packageSetsLock.AcquireWriterLock().GetAwaiter().GetResult();
|
||||
|
||||
_packageIdGroups.Clear();
|
||||
_packageInfoMap.Clear();
|
||||
_reversePackageIdGroups.Clear();
|
||||
|
||||
RefreshPackageLists();
|
||||
|
||||
return FluentResults.Result.Ok();
|
||||
}
|
||||
|
||||
public void OnEnabledPackageListChanged(CorePackage package, IEnumerable<RegularPackage> regularPackages)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
SyncPackagesLists(
|
||||
regularPackages.Select(p => (ContentPackage)p).ToImmutableArray().Add(package),
|
||||
_allPackages.ToImmutableArray())
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public void OnAllPackageListChanged(IEnumerable<CorePackage> corePackages, IEnumerable<RegularPackage> regularPackages)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
SyncPackagesLists(
|
||||
_enabledPackages.ToImmutableArray(),
|
||||
regularPackages.Select(p => p as ContentPackage)
|
||||
.Union(corePackages.Select(p => p as ContentPackage))
|
||||
.ToImmutableArray()
|
||||
).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task<Result<IPackageInfo>> Lookup(string packageName)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
if(packageName.IsNullOrWhiteSpace())
|
||||
return FluentResults.Result.Fail($"Name is null or empty.");
|
||||
return await LookupInternal(packageName);
|
||||
}
|
||||
|
||||
public async Task<Result<IPackageInfo>> Lookup(string packageName, ulong steamWorkshopId)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
if (packageName.IsNullOrWhiteSpace() || steamWorkshopId == 0)
|
||||
return FluentResults.Result.Fail($"Name or steam id is null or empty.");
|
||||
return await LookupInternal((packageName, steamWorkshopId));
|
||||
}
|
||||
|
||||
public async Task<Result<IPackageInfo>> Lookup(ulong steamWorkshopId)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
if (steamWorkshopId is 0)
|
||||
return FluentResults.Result.Fail($"SteamId is 0.");
|
||||
return await LookupInternal(steamWorkshopId);
|
||||
}
|
||||
|
||||
public async Task<Result<IPackageInfo>> Lookup(ContentPackage package)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
if (package is null)
|
||||
return FluentResults.Result.Fail($"Package is null.");
|
||||
|
||||
if (package.TryExtractSteamWorkshopId(out var steamWorkshopId) && steamWorkshopId.Value != 0)
|
||||
{
|
||||
if (!package.Name.IsNullOrWhiteSpace())
|
||||
return await LookupInternal((package.Name, steamWorkshopId.Value));
|
||||
else
|
||||
return await LookupInternal(steamWorkshopId.Value);
|
||||
}
|
||||
|
||||
if (!package.Name.IsNullOrWhiteSpace())
|
||||
return await LookupInternal(package.Name);
|
||||
|
||||
return FluentResults.Result.Fail($"Package name is null and steamid is 0.");
|
||||
}
|
||||
|
||||
public void RefreshPackageLists()
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
if (Thread.CurrentThread != GameMain.MainThread)
|
||||
throw new InvalidOperationException($"{nameof(ContentPackageInfoLookup)}: {nameof(RefreshPackageLists)} must be run on the main thread.");
|
||||
var enabledPackages = _packageListRetrievalService.GetEnabledContentPackages().ToImmutableArray();
|
||||
var allPackages = _packageListRetrievalService.GetAllContentPackages().ToImmutableArray();
|
||||
SyncPackagesLists(enabledPackages, allPackages).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,12 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.Specialized;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.LuaCs.Events;
|
||||
using Barotrauma.LuaCs.Services.Compatibility;
|
||||
using Barotrauma.LuaCs.Services.Safe;
|
||||
using Dynamitey;
|
||||
using FluentResults;
|
||||
using FluentResults.LuaCs;
|
||||
using HarmonyLib;
|
||||
using ImpromptuInterface;
|
||||
using OneOf;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface LocalizationService
|
||||
{
|
||||
|
||||
}
|
||||
@@ -272,8 +272,9 @@ namespace Barotrauma.LuaCs.Services
|
||||
|
||||
public LuaGame()
|
||||
{
|
||||
LuaUserData.MakeFieldAccessible(UserData.RegisterType(typeof(GameSettings)), "currentConfig");
|
||||
Settings = UserData.CreateStatic(typeof(GameSettings));
|
||||
throw new NotImplementedException();
|
||||
/*LuaUserData.MakeFieldAccessible(UserData.RegisterType(typeof(GameSettings)), "currentConfig");
|
||||
Settings = UserData.CreateStatic(typeof(GameSettings));*/
|
||||
}
|
||||
|
||||
public void OverrideTraitors(bool o)
|
||||
|
||||
@@ -3,7 +3,7 @@ using Barotrauma.Networking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma.LuaCs.Networking;
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
internal partial class NetworkingService : INetworkingService
|
||||
{
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public sealed class PackageListRetrievalService : IPackageListRetrievalService
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
// stateless service
|
||||
return;
|
||||
}
|
||||
|
||||
public void CheckDisposed()
|
||||
{
|
||||
// stateless service
|
||||
return;
|
||||
}
|
||||
|
||||
public bool IsDisposed => false;
|
||||
|
||||
public IEnumerable<ContentPackage> GetEnabledContentPackages()
|
||||
{
|
||||
return ContentPackageManager.EnabledPackages.All;
|
||||
}
|
||||
|
||||
public IEnumerable<ContentPackage> GetAllContentPackages()
|
||||
{
|
||||
return ContentPackageManager.AllPackages;
|
||||
}
|
||||
}
|
||||
+379
-1
@@ -1,13 +1,391 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.LuaCs.Services.Processing;
|
||||
using Barotrauma.Steam;
|
||||
using FluentResults;
|
||||
using OneOf;
|
||||
|
||||
// ReSharper disable UseCollectionExpression
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public class PackageManagementService : IPackageManagementService
|
||||
public partial class PackageManagementService : IPackageManagementService
|
||||
{
|
||||
private int _isDisposed;
|
||||
private readonly ConcurrentDictionary<ContentPackage, IModConfigInfo> _modInfos = new();
|
||||
// lookup caches
|
||||
private readonly IPackageInfoLookupService _packageInfoLookupService;
|
||||
// processors
|
||||
private readonly IConverterServiceAsync<ContentPackage, IModConfigInfo> _modConfigParserService;
|
||||
private readonly IProcessorService<IReadOnlyList<IAssemblyResourceInfo>, IAssembliesResourcesInfo> _assemblyInfoConverter;
|
||||
private readonly IProcessorService<IReadOnlyList<IConfigResourceInfo>, IConfigsResourcesInfo> _configsInfoConverter;
|
||||
private readonly IProcessorService<IReadOnlyList<IConfigProfileResourceInfo>, IConfigProfilesResourcesInfo> _configProfilesConverter;
|
||||
private readonly IProcessorService<IReadOnlyList<ILocalizationResourceInfo>, ILocalizationsResourcesInfo> _localizationsConverter;
|
||||
private readonly IProcessorService<IReadOnlyList<ILuaScriptResourceInfo>, ILuaScriptsResourcesInfo> _luaScriptsConverter;
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsDisposed = true;
|
||||
_modInfos.Clear();
|
||||
}
|
||||
|
||||
public bool IsDisposed
|
||||
{
|
||||
get => ModUtils.Threading.GetBool(ref _isDisposed);
|
||||
private set => ModUtils.Threading.SetBool(ref _isDisposed, value);
|
||||
}
|
||||
|
||||
public FluentResults.Result Reset()
|
||||
{
|
||||
try
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
_modInfos.Clear();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return FluentResults.Result.Fail(new ExceptionalError(e));
|
||||
}
|
||||
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
|
||||
: _modInfos.SelectMany(kvp => kvp.Value.Configs).ToImmutableArray();
|
||||
public ImmutableArray<IConfigProfileResourceInfo> ConfigProfiles => _modInfos.IsEmpty ? ImmutableArray<IConfigProfileResourceInfo>.Empty
|
||||
: _modInfos.SelectMany(kvp => kvp.Value.ConfigProfiles).ToImmutableArray();
|
||||
public ImmutableArray<ILuaScriptResourceInfo> LuaScripts => _modInfos.IsEmpty ? ImmutableArray<ILuaScriptResourceInfo>.Empty
|
||||
: _modInfos.SelectMany(kvp => kvp.Value.LuaScripts).ToImmutableArray();
|
||||
public ImmutableArray<IAssemblyResourceInfo> Assemblies => _modInfos.IsEmpty ? ImmutableArray<IAssemblyResourceInfo>.Empty
|
||||
: _modInfos.SelectMany(kvp => kvp.Value.Assemblies).ToImmutableArray();
|
||||
|
||||
|
||||
public async Task<FluentResults.Result> LoadPackageInfosAsync(ContentPackage package)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
if (package is null)
|
||||
return FluentResults.Result.Fail(new ExceptionalError(new NullReferenceException($"{nameof(LoadPackageInfosAsync)}: ContentPackage is null.")));
|
||||
var result = await _modConfigParserService.TryParseResourceAsync(package);
|
||||
if (result.IsFailed)
|
||||
return FluentResults.Result.Fail($"$Could not parse package mod config.").WithErrors(result.Errors);
|
||||
if (!_modInfos.TryAdd(package, result.Value))
|
||||
return FluentResults.Result.Fail($"Failed to add ModInfo for {package.Name}.");
|
||||
return FluentResults.Result.Ok();
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<(ContentPackage, FluentResults.Result)>> LoadPackagesInfosAsync(IReadOnlyList<ContentPackage> packages)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
if (packages is null || packages.Count == 0)
|
||||
throw new ArgumentNullException(nameof(LoadPackagesInfosAsync));
|
||||
ConcurrentQueue<(ContentPackage, FluentResults.Result)> results = new();
|
||||
await packages.ParallelForEachAsync(async package =>
|
||||
{
|
||||
var res = await LoadPackageInfosAsync(package);
|
||||
results.Enqueue((package, res));
|
||||
}, Environment.ProcessorCount);
|
||||
return results.ToImmutableArray();
|
||||
}
|
||||
|
||||
public IReadOnlyList<ContentPackage> GetAllLoadedPackages()
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
return _modInfos.IsEmpty ? ImmutableArray<ContentPackage>.Empty
|
||||
: _modInfos.Select(kvp => kvp.Key).ToImmutableArray();
|
||||
}
|
||||
|
||||
public void DisposePackageInfos(ContentPackage package)
|
||||
{
|
||||
_modInfos.TryRemove(package, out _);
|
||||
}
|
||||
|
||||
public void DisposePackagesInfos(IReadOnlyList<ContentPackage> packages)
|
||||
{
|
||||
if (packages is null || packages.Count == 0)
|
||||
return;
|
||||
|
||||
foreach (var package in packages)
|
||||
{
|
||||
DisposePackageInfos(package);
|
||||
}
|
||||
}
|
||||
|
||||
public Result<IPackageDependency> GetPackageDependencyInfo(ContentPackage ownerPackage, string packageName,
|
||||
ulong steamWorkshopId)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
|
||||
if (ownerPackage is null)
|
||||
return FluentResults.Result.Fail($"OwnerPackage is null.");
|
||||
var nameGood = !packageName.IsNullOrWhiteSpace();
|
||||
|
||||
if (!nameGood && steamWorkshopId == 0)
|
||||
FluentResults.Result.Fail($"PackageName and SteamId cannot both be invalid.");
|
||||
|
||||
IPackageInfo depInfo = null;
|
||||
|
||||
// complex key
|
||||
if (nameGood && steamWorkshopId != 0
|
||||
&& _packageInfoLookupService.Lookup(packageName, steamWorkshopId).GetAwaiter().GetResult() is
|
||||
{ IsSuccess: true, Value: {} dep1 })
|
||||
{
|
||||
depInfo = dep1;
|
||||
}
|
||||
// name key
|
||||
else if (nameGood && _packageInfoLookupService.Lookup(packageName).GetAwaiter().GetResult() is
|
||||
{ IsSuccess: true, Value: { } dep2 })
|
||||
{
|
||||
depInfo = dep2;
|
||||
}
|
||||
// steamid key
|
||||
else if (_packageInfoLookupService.Lookup(steamWorkshopId).GetAwaiter().GetResult() is
|
||||
{ IsSuccess: true, Value: { } dep3 })
|
||||
{
|
||||
depInfo = dep3;
|
||||
}
|
||||
// this should never be null so we return an exception
|
||||
else
|
||||
{
|
||||
return FluentResults.Result.Fail($"Package Dependency for {ownerPackage.Name} was not found.");
|
||||
}
|
||||
|
||||
return FluentResults.Result.Ok<IPackageDependency>(new PackageDependency(ownerPackage, depInfo, ownerPackage.Name));
|
||||
}
|
||||
|
||||
public Result<IAssembliesResourcesInfo> GetAssembliesInfos(ContentPackage package, bool onlySupportedResources = true)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
if (package is null)
|
||||
return FluentResults.Result.Fail($"{nameof(GetAssembliesInfos)}: ContentPackage is null.");
|
||||
if (_modInfos.TryGetValue(package, out var result))
|
||||
return FluentResults.Result.Ok<IAssembliesResourcesInfo>(_assemblyInfoConverter.Process(onlySupportedResources?
|
||||
result.Assemblies.Where(r =>
|
||||
(r.SupportedPlatforms & ModUtils.Environment.CurrentPlatform) > 0
|
||||
&& (r.SupportedTargets & ModUtils.Environment.CurrentTarget) > 0).ToImmutableArray()
|
||||
: result.Assemblies
|
||||
));
|
||||
return FluentResults.Result.Fail(
|
||||
$"{nameof(GetAssembliesInfos)}: ContentPackage {package.Name} is not registered.");
|
||||
}
|
||||
|
||||
public Result<IConfigsResourcesInfo> GetConfigsInfos(ContentPackage package, bool onlySupportedResources = true)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
if (package is null)
|
||||
return FluentResults.Result.Fail($"{nameof(GetConfigsInfos)}: ContentPackage is null.");
|
||||
|
||||
if (_modInfos.TryGetValue(package, out var result))
|
||||
{
|
||||
return FluentResults.Result.Ok<IConfigsResourcesInfo>(_configsInfoConverter.Process(onlySupportedResources?
|
||||
result.Configs.Where(r =>
|
||||
(r.SupportedPlatforms & ModUtils.Environment.CurrentPlatform) > 0
|
||||
&& (r.SupportedTargets & ModUtils.Environment.CurrentTarget) > 0).ToImmutableArray()
|
||||
: result.Configs
|
||||
));
|
||||
}
|
||||
|
||||
return FluentResults.Result.Fail(
|
||||
$"{nameof(GetConfigsInfos)}: ContentPackage {package.Name} is not registered.");
|
||||
}
|
||||
|
||||
public Result<IConfigProfilesResourcesInfo> GetConfigProfilesInfos(ContentPackage package, bool onlySupportedResources = true)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
if (package is null)
|
||||
return FluentResults.Result.Fail($"{nameof(GetConfigProfilesInfos)}: ContentPackage is null.");
|
||||
|
||||
if (_modInfos.TryGetValue(package, out var result))
|
||||
{
|
||||
return FluentResults.Result.Ok<IConfigProfilesResourcesInfo>(_configProfilesConverter.Process(onlySupportedResources?
|
||||
result.ConfigProfiles.Where(r =>
|
||||
(r.SupportedPlatforms & ModUtils.Environment.CurrentPlatform) > 0
|
||||
&& (r.SupportedTargets & ModUtils.Environment.CurrentTarget) > 0).ToImmutableArray()
|
||||
: result.ConfigProfiles
|
||||
));
|
||||
}
|
||||
|
||||
return FluentResults.Result.Fail(
|
||||
$"{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)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
if (package is null)
|
||||
return FluentResults.Result.Fail($"{nameof(GetLuaScriptsInfos)}: ContentPackage is null.");
|
||||
|
||||
if (_modInfos.TryGetValue(package, out var result))
|
||||
{
|
||||
return FluentResults.Result.Ok<ILuaScriptsResourcesInfo>(_luaScriptsConverter.Process(onlySupportedResources?
|
||||
result.LuaScripts.Where(r =>
|
||||
(r.SupportedPlatforms & ModUtils.Environment.CurrentPlatform) > 0
|
||||
&& (r.SupportedTargets & ModUtils.Environment.CurrentTarget) > 0).ToImmutableArray()
|
||||
: result.LuaScripts
|
||||
));
|
||||
}
|
||||
|
||||
return FluentResults.Result.Fail(
|
||||
$"{nameof(GetLuaScriptsInfos)}: ContentPackage {package.Name} is not registered.");
|
||||
}
|
||||
|
||||
public Result<IAssembliesResourcesInfo> GetAssembliesInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
if (packages is null || packages.Count == 0)
|
||||
return FluentResults.Result.Fail($"{nameof(GetAssembliesInfos)}: ContentPackage list is null or empty.");
|
||||
var builder = ImmutableArray.CreateBuilder<IAssemblyResourceInfo>();
|
||||
foreach (var package in packages)
|
||||
{
|
||||
if (_modInfos.TryGetValue(package, out var result) && result.Assemblies is { IsEmpty: false })
|
||||
{
|
||||
builder.AddRange(onlySupportedResources?
|
||||
result.Assemblies.Where(r =>
|
||||
(r.SupportedPlatforms & ModUtils.Environment.CurrentPlatform) > 0
|
||||
&& (r.SupportedTargets & ModUtils.Environment.CurrentTarget) > 0).ToImmutableArray()
|
||||
: result.Assemblies);
|
||||
}
|
||||
}
|
||||
|
||||
return FluentResults.Result.Ok(_assemblyInfoConverter.Process(builder.MoveToImmutable()));
|
||||
}
|
||||
|
||||
public Result<IConfigsResourcesInfo> GetConfigsInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
if (packages is null || packages.Count == 0)
|
||||
return FluentResults.Result.Fail($"{nameof(GetConfigsInfos)}: ContentPackage list is null or empty.");
|
||||
var builder = ImmutableArray.CreateBuilder<IConfigResourceInfo>();
|
||||
foreach (var package in packages)
|
||||
{
|
||||
if (_modInfos.TryGetValue(package, out var result) && result.Configs is { IsEmpty: false })
|
||||
{
|
||||
builder.AddRange(onlySupportedResources?
|
||||
result.Configs.Where(r =>
|
||||
(r.SupportedPlatforms & ModUtils.Environment.CurrentPlatform) > 0
|
||||
&& (r.SupportedTargets & ModUtils.Environment.CurrentTarget) > 0).ToImmutableArray()
|
||||
: result.Configs);
|
||||
}
|
||||
}
|
||||
|
||||
return FluentResults.Result.Ok(_configsInfoConverter.Process(builder.MoveToImmutable()));
|
||||
}
|
||||
|
||||
public Result<IConfigProfilesResourcesInfo> GetConfigProfilesInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
if (packages is null || packages.Count == 0)
|
||||
return FluentResults.Result.Fail($"{nameof(GetConfigProfilesInfos)}: ContentPackage list is null or empty.");
|
||||
var builder = ImmutableArray.CreateBuilder<IConfigProfileResourceInfo>();
|
||||
foreach (var package in packages)
|
||||
{
|
||||
if (_modInfos.TryGetValue(package, out var result) && result.ConfigProfiles is { IsEmpty: false })
|
||||
{
|
||||
builder.AddRange(onlySupportedResources?
|
||||
result.ConfigProfiles.Where(r =>
|
||||
(r.SupportedPlatforms & ModUtils.Environment.CurrentPlatform) > 0
|
||||
&& (r.SupportedTargets & ModUtils.Environment.CurrentTarget) > 0).ToImmutableArray()
|
||||
: result.ConfigProfiles);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
if (packages is null || packages.Count == 0)
|
||||
return FluentResults.Result.Fail($"{nameof(GetLuaScriptsInfos)}: ContentPackage list is null or empty.");
|
||||
var builder = ImmutableArray.CreateBuilder<ILuaScriptResourceInfo>();
|
||||
foreach (var package in packages)
|
||||
{
|
||||
if (_modInfos.TryGetValue(package, out var result) && result.LuaScripts is { IsEmpty: false })
|
||||
{
|
||||
builder.AddRange(onlySupportedResources?
|
||||
result.LuaScripts.Where(r =>
|
||||
(r.SupportedPlatforms & ModUtils.Environment.CurrentPlatform) > 0
|
||||
&& (r.SupportedTargets & ModUtils.Environment.CurrentTarget) > 0).ToImmutableArray()
|
||||
: result.LuaScripts);
|
||||
}
|
||||
}
|
||||
|
||||
return FluentResults.Result.Ok(_luaScriptsConverter.Process(builder.MoveToImmutable()));
|
||||
}
|
||||
|
||||
public async Task<Result<IAssembliesResourcesInfo>> GetAssembliesInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true)
|
||||
{
|
||||
return await Task.Run(() => GetAssembliesInfos(packages, onlySupportedResources));
|
||||
}
|
||||
|
||||
public async Task<Result<IConfigsResourcesInfo>> GetConfigsInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true)
|
||||
{
|
||||
return await Task.Run(() => GetConfigsInfos(packages, onlySupportedResources));
|
||||
}
|
||||
|
||||
public async Task<Result<IConfigProfilesResourcesInfo>> GetConfigProfilesInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return await Task.Run(() => GetLuaScriptsInfos(packages, onlySupportedResources));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+14
-7
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
@@ -6,18 +7,24 @@ using FluentResults;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services.Processing;
|
||||
|
||||
#region TypeDef
|
||||
|
||||
public interface IConverterService<in TSrc, TOut> : IReusableService
|
||||
public interface IConverterService<in TSrc, TOut> : IService
|
||||
{
|
||||
Result<TOut> TryParseResource(TSrc src);
|
||||
Result<TOut> TryParseResources(IEnumerable<TSrc> sources);
|
||||
ImmutableArray<Result<TOut>> TryParseResources(IEnumerable<TSrc> sources);
|
||||
}
|
||||
|
||||
public interface IConverterServiceAsync<in TSrc, TOut> : IReusableService
|
||||
public interface IConverterServiceAsync<in TSrc, TOut> : IService
|
||||
{
|
||||
Task<Result<TOut>> TryParseResourceAsync(TSrc src);
|
||||
Task<Result<TOut>> TryParseResourcesAsync(IEnumerable<TSrc> sources);
|
||||
Task<ImmutableArray<Result<TOut>>> TryParseResourcesAsync(IEnumerable<TSrc> sources);
|
||||
}
|
||||
|
||||
#endregion
|
||||
public interface IProcessorService<in TSrc, TOut> : IService
|
||||
{
|
||||
TOut Process(TSrc src);
|
||||
}
|
||||
|
||||
public interface IProcessorServiceAsync<in TSrc, TOut> : IService
|
||||
{
|
||||
Task<TOut> ProcessAsync(TSrc src);
|
||||
}
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
using Barotrauma.LuaCs.Data;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services.Processing;
|
||||
|
||||
public interface IModConfigCreatorService : IService
|
||||
{
|
||||
FluentResults.Result<IModConfigInfo> BuildConfigForPackage(ContentPackage package);
|
||||
FluentResults.Result<IModConfigInfo> BuildConfigFromManifest(string manifestPath);
|
||||
}
|
||||
+661
@@ -0,0 +1,661 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using FluentResults;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services.Processing;
|
||||
|
||||
public partial class ModConfigService : IConverterServiceAsync<ContentPackage, IModConfigInfo>, IConverterService<ContentPackage, IModConfigInfo>
|
||||
{
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly Lazy<IPackageManagementService> _packageManagementService;
|
||||
private int _isDisposed;
|
||||
|
||||
private const string ModConfigFileName = "ModConfig.xml";
|
||||
private const string ModConfigRootName = "ModConfig";
|
||||
|
||||
public ModConfigService(IStorageService storageService, Lazy<IPackageManagementService> pms)
|
||||
{
|
||||
_storageService = storageService;
|
||||
_packageManagementService = pms;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsDisposed
|
||||
{
|
||||
get => ModUtils.Threading.GetBool(ref _isDisposed);
|
||||
private set => ModUtils.Threading.SetBool(ref _isDisposed, value);
|
||||
}
|
||||
|
||||
public async Task<Result<IModConfigInfo>> TryParseResourceAsync(ContentPackage src)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
|
||||
// validate package
|
||||
if (src is null)
|
||||
return FluentResults.Result.Fail<IModConfigInfo>("ContentPackage is null");
|
||||
if (_storageService.DirectoryExists(src.Path) is { } res && (res.IsFailed || !res.Value))
|
||||
return FluentResults.Result.Fail<IModConfigInfo>($"ContentPackage does not exist or cannot be accessed: {src.Path}");
|
||||
|
||||
// find ModConfig.xml or deep scan on fail (legacy)
|
||||
if (await _storageService.LoadPackageXmlAsync(src, ModConfigFileName) is
|
||||
{ IsSuccess: true, Value: var modConfigXml }
|
||||
&& modConfigXml.Root is { Name.LocalName: ModConfigRootName } root)
|
||||
{
|
||||
return await GetModConfigInfoAsync(src, root);
|
||||
}
|
||||
|
||||
// legacy mode
|
||||
try
|
||||
{
|
||||
// we only supported assemblies and lua scripts
|
||||
var asm = GetAssembliesLegacy(src);
|
||||
var lua = GetLuaScriptsLegacy(src);
|
||||
|
||||
return new ModConfigInfo()
|
||||
{
|
||||
Assemblies = asm,
|
||||
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)
|
||||
{
|
||||
return FluentResults.Result.Fail<IModConfigInfo>($"Unable to parse legacy content package: {src.Name}: {src.Path}");
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<IAssemblyResourceInfo>();
|
||||
var elementsList = elements.ToImmutableArray();
|
||||
|
||||
if (GetFilesList(src, elementsList, "Assembly", "*.dll")
|
||||
is not { IsSuccess: true, Value: { } xmlFiles })
|
||||
return ImmutableArray<IAssemblyResourceInfo>.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 AssemblyResourceInfo()
|
||||
{
|
||||
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,
|
||||
FriendlyName = file.Item1.GetAttributeString("Name", info.Name),
|
||||
IsScript = false,
|
||||
LazyLoad = !file.Item1.GetAttributeBool("RunFile", true)
|
||||
});
|
||||
}
|
||||
|
||||
if (GetFilesList(src, elementsList, "Assembly", "*.cs")
|
||||
is not { IsSuccess: true, Value: { } xmlFiles2 })
|
||||
return ImmutableArray<IAssemblyResourceInfo>.Empty;
|
||||
|
||||
foreach (var file in xmlFiles2)
|
||||
{
|
||||
// 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 AssemblyResourceInfo()
|
||||
{
|
||||
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,
|
||||
FriendlyName = file.Item1.GetAttributeString("Name", info.Name),
|
||||
IsScript = true,
|
||||
LazyLoad = !file.Item1.GetAttributeBool("RunFile", true)
|
||||
});
|
||||
}
|
||||
|
||||
return builder.Count > 0
|
||||
? builder.ToImmutable()
|
||||
: ImmutableArray<IAssemblyResourceInfo>.Empty;
|
||||
}
|
||||
|
||||
private ImmutableArray<IConfigResourceInfo> GetConfigs(ContentPackage src, IEnumerable<XElement> elements)
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<IConfigResourceInfo>();
|
||||
if (GetXmlFilesList(src, elements, "Config")
|
||||
is not { IsSuccess: true, Value: { } xmlFiles })
|
||||
return ImmutableArray<IConfigResourceInfo>.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 ConfigResourceInfo()
|
||||
{
|
||||
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<IConfigResourceInfo>.Empty;
|
||||
}
|
||||
|
||||
private ImmutableArray<IConfigProfileResourceInfo> GetConfigProfiles(ContentPackage src, IEnumerable<XElement> elements)
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<IConfigProfileResourceInfo>();
|
||||
if (GetXmlFilesList(src, elements, "Config")
|
||||
is not { IsSuccess: true, Value: { } xmlFiles })
|
||||
return ImmutableArray<IConfigProfileResourceInfo>.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 ConfigProfileResourceInfo()
|
||||
{
|
||||
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<IConfigProfileResourceInfo>.Empty;
|
||||
}
|
||||
|
||||
private ImmutableArray<ILuaScriptResourceInfo> GetLuaScripts(ContentPackage src, IEnumerable<XElement> elements)
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<ILuaScriptResourceInfo>();
|
||||
if (GetXmlFilesList(src, elements, "Config")
|
||||
is not { IsSuccess: true, Value: { } xmlFiles })
|
||||
return ImmutableArray<ILuaScriptResourceInfo>.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 LuaScriptScriptResourceInfo()
|
||||
{
|
||||
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,
|
||||
IsAutorun = file.Item1.GetAttributeBool("RunFile", true)
|
||||
});
|
||||
}
|
||||
|
||||
return builder.Count > 0
|
||||
? builder.ToImmutable()
|
||||
: ImmutableArray<ILuaScriptResourceInfo>.Empty;
|
||||
}
|
||||
|
||||
private Result<ImmutableArray<(XElement, ImmutableArray<string>)>> GetXmlFilesList(ContentPackage src,
|
||||
IEnumerable<XElement> elements, string elementNameCheck) =>
|
||||
GetFilesList(src, elements, elementNameCheck, "*.xml");
|
||||
|
||||
private Result<ImmutableArray<(XElement, ImmutableArray<string>)>> GetFilesList(ContentPackage src,
|
||||
IEnumerable<XElement> elements, string elementNameCheck, string filter)
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<(XElement, ImmutableArray<string>)>();
|
||||
|
||||
if (elementNameCheck.IsNullOrWhiteSpace())
|
||||
throw new ArgumentNullException($"{nameof(GetXmlFilesList)}: The element check is null.");
|
||||
|
||||
foreach (var element in elements)
|
||||
{
|
||||
if (element.Name.LocalName != elementNameCheck)
|
||||
throw new ArgumentException("Element is not a Localization element");
|
||||
|
||||
if (element.GetAttributeString("Folder", string.Empty) is { } str
|
||||
&& !string.IsNullOrWhiteSpace(str))
|
||||
{
|
||||
if (_storageService.FindFilesInPackage(src, str, filter, true)
|
||||
is not { IsSuccess: true, Value: var fpList } || !fpList.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var fileP in fpList)
|
||||
builder.Add((element, fpList.ToImmutableArray()));
|
||||
}
|
||||
else if (element.GetAttributeString("File", string.Empty) is { } fileStr
|
||||
&& !string.IsNullOrWhiteSpace(fileStr)
|
||||
&& _storageService.GetAbsFromPackage(src, fileStr) is { IsSuccess: true, Value: var fp }
|
||||
&& _storageService.FileExists(fp) is { IsSuccess: true, Value: true })
|
||||
{
|
||||
builder.Add((element, new [] { fileStr }.ToImmutableArray()));
|
||||
}
|
||||
}
|
||||
|
||||
return builder.Count > 0
|
||||
? FluentResults.Result.Ok(builder.ToImmutable())
|
||||
: FluentResults.Result.Fail($"No files found");
|
||||
}
|
||||
|
||||
private ResourceAdditionalInfo GetElementsAttributesData(XElement element, string localPath)
|
||||
{
|
||||
return new ResourceAdditionalInfo(
|
||||
element.GetAttributeString("Name", localPath),
|
||||
GetSupportedPlatforms(element.GetAttributeString("Platform", "any")),
|
||||
GetSupportedTargets(element.GetAttributeString("Target", "any")),
|
||||
GetSupportedCultures(element),
|
||||
element.GetAttributeBool("Optional", false),
|
||||
element.GetAttributeInt("Priority", 0));
|
||||
|
||||
Platform GetSupportedPlatforms(string platformName) => platformName.ToLowerInvariant().Trim() switch
|
||||
{
|
||||
"windows" => Platform.Windows,
|
||||
"linux" => Platform.Linux,
|
||||
"osx" => Platform.OSX,
|
||||
_ => Platform.Windows | Platform.Linux | Platform.OSX
|
||||
};
|
||||
|
||||
Target GetSupportedTargets(string targetName) => targetName.ToLowerInvariant().Trim() switch
|
||||
{
|
||||
"client" => Target.Client,
|
||||
"server" => Target.Server,
|
||||
_ => Target.Client | Target.Server,
|
||||
};
|
||||
|
||||
ImmutableArray<CultureInfo> GetSupportedCultures(XElement element)
|
||||
{
|
||||
var culture = element.GetAttributeString("Culture", string.Empty);
|
||||
if (string.IsNullOrWhiteSpace(culture))
|
||||
return new[] { CultureInfo.InvariantCulture }.ToImmutableArray();
|
||||
var builder = ImmutableArray.CreateBuilder<CultureInfo>();
|
||||
var arr = culture.Split(',');
|
||||
if (arr.Length == 0)
|
||||
return new[] { CultureInfo.InvariantCulture }.ToImmutableArray();
|
||||
foreach (var culstr in arr)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(culstr))
|
||||
continue;
|
||||
try
|
||||
{
|
||||
builder.Add(
|
||||
culstr.ToLowerInvariant().Trim() == "default"
|
||||
? CultureInfo.InvariantCulture
|
||||
: CultureInfo.GetCultureInfo(culstr));
|
||||
}
|
||||
catch (CultureNotFoundException e)
|
||||
{
|
||||
// This is the case if a culture is specified by the package that is not supported by the OS/.NET ENV.
|
||||
// We ignore it since we can never use it.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return builder.Count > 0
|
||||
? builder.ToImmutable()
|
||||
: new[] { CultureInfo.InvariantCulture }.ToImmutableArray();
|
||||
}
|
||||
}
|
||||
|
||||
private ImmutableArray<IPackageDependency> GetElementsDependenciesData(XElement element, ContentPackage src)
|
||||
{
|
||||
if (element.GetChildElement("Dependencies") is not {} dependencies
|
||||
|| dependencies.GetChildElements("Dependency").ToImmutableArray() is not { Length: >0 } depsList)
|
||||
return ImmutableArray<IPackageDependency>.Empty;
|
||||
var builder = ImmutableArray.CreateBuilder<IPackageDependency>();
|
||||
foreach (var dep in depsList)
|
||||
{
|
||||
var packName = dep.GetAttributeString("PackageName", string.Empty);
|
||||
var packId = dep.GetAttributeUInt64("PackageId", 0);
|
||||
|
||||
// invalid entry
|
||||
if (packName.IsNullOrWhiteSpace() && packId == 0)
|
||||
continue;
|
||||
|
||||
if (_packageManagementService.Value.GetPackageDependencyInfo(src, packName, packId) is
|
||||
{ IsSuccess: true, Value: { } depsInfo })
|
||||
{
|
||||
builder.Add(depsInfo);
|
||||
}
|
||||
}
|
||||
return builder.ToImmutable();
|
||||
}
|
||||
|
||||
private ImmutableArray<IAssemblyResourceInfo> GetAssembliesLegacy(ContentPackage src)
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<IAssemblyResourceInfo>();
|
||||
// server, linux
|
||||
if (_storageService.FindFilesInPackage(src, "bin/Server/Linux", "*.dll", true)
|
||||
is { IsSuccess: true, Value: { IsDefaultOrEmpty: false} filesSrvLin})
|
||||
{
|
||||
builder.Add(new AssemblyResourceInfo()
|
||||
{
|
||||
Dependencies = ImmutableArray<IPackageDependency>.Empty,
|
||||
FilePaths = filesSrvLin,
|
||||
FriendlyName = "AssembliesServerLinux",
|
||||
InternalName = "AssembliesServerLinux",
|
||||
IsScript = false,
|
||||
LazyLoad = false,
|
||||
LoadPriority = 1,
|
||||
Optional = false,
|
||||
OwnerPackage = src,
|
||||
SupportedCultures = new CultureInfo[]{ CultureInfo.InvariantCulture }.ToImmutableArray(),
|
||||
SupportedPlatforms = Platform.Linux,
|
||||
SupportedTargets = Target.Server
|
||||
});
|
||||
}
|
||||
|
||||
// server, osx
|
||||
if (_storageService.FindFilesInPackage(src, "bin/Server/OSX", "*.dll", true)
|
||||
is { IsSuccess: true, Value: { IsDefaultOrEmpty: false} filesSrvOsx})
|
||||
{
|
||||
builder.Add(new AssemblyResourceInfo()
|
||||
{
|
||||
Dependencies = ImmutableArray<IPackageDependency>.Empty,
|
||||
FilePaths = filesSrvOsx,
|
||||
FriendlyName = "AssembliesServerOSX",
|
||||
InternalName = "AssembliesServerOSX",
|
||||
IsScript = false,
|
||||
LazyLoad = false,
|
||||
LoadPriority = 1,
|
||||
Optional = false,
|
||||
OwnerPackage = src,
|
||||
SupportedCultures = new CultureInfo[]{ CultureInfo.InvariantCulture }.ToImmutableArray(),
|
||||
SupportedPlatforms = Platform.OSX,
|
||||
SupportedTargets = Target.Server
|
||||
});
|
||||
}
|
||||
|
||||
// server, osx
|
||||
if (_storageService.FindFilesInPackage(src, "bin/Server/Windows", "*.dll", true)
|
||||
is { IsSuccess: true, Value: { IsDefaultOrEmpty: false} filesSrvWin})
|
||||
{
|
||||
builder.Add(new AssemblyResourceInfo()
|
||||
{
|
||||
Dependencies = ImmutableArray<IPackageDependency>.Empty,
|
||||
FilePaths = filesSrvWin,
|
||||
FriendlyName = "AssembliesServerWin",
|
||||
InternalName = "AssembliesServerWin",
|
||||
IsScript = false,
|
||||
LazyLoad = false,
|
||||
LoadPriority = 1,
|
||||
Optional = false,
|
||||
OwnerPackage = src,
|
||||
SupportedCultures = new CultureInfo[]{ CultureInfo.InvariantCulture }.ToImmutableArray(),
|
||||
SupportedPlatforms = Platform.Windows,
|
||||
SupportedTargets = Target.Server
|
||||
});
|
||||
}
|
||||
|
||||
// client, linux
|
||||
if (_storageService.FindFilesInPackage(src, "bin/Client/Linux", "*.dll", true)
|
||||
is { IsSuccess: true, Value: { IsDefaultOrEmpty: false} filesCliLin})
|
||||
{
|
||||
builder.Add(new AssemblyResourceInfo()
|
||||
{
|
||||
Dependencies = ImmutableArray<IPackageDependency>.Empty,
|
||||
FilePaths = filesCliLin,
|
||||
FriendlyName = "AssembliesClientLinux",
|
||||
InternalName = "AssembliesClientLinux",
|
||||
IsScript = false,
|
||||
LazyLoad = false,
|
||||
LoadPriority = 1,
|
||||
Optional = false,
|
||||
OwnerPackage = src,
|
||||
SupportedCultures = new CultureInfo[]{ CultureInfo.InvariantCulture }.ToImmutableArray(),
|
||||
SupportedPlatforms = Platform.Linux,
|
||||
SupportedTargets = Target.Client
|
||||
});
|
||||
}
|
||||
|
||||
// server, osx
|
||||
if (_storageService.FindFilesInPackage(src, "bin/Client/OSX", "*.dll", true)
|
||||
is { IsSuccess: true, Value: { IsDefaultOrEmpty: false} filesCliOsx})
|
||||
{
|
||||
builder.Add(new AssemblyResourceInfo()
|
||||
{
|
||||
Dependencies = ImmutableArray<IPackageDependency>.Empty,
|
||||
FilePaths = filesCliOsx,
|
||||
FriendlyName = "AssembliesClientOSX",
|
||||
InternalName = "AssembliesClientOSX",
|
||||
IsScript = false,
|
||||
LazyLoad = false,
|
||||
LoadPriority = 1,
|
||||
Optional = false,
|
||||
OwnerPackage = src,
|
||||
SupportedCultures = new CultureInfo[]{ CultureInfo.InvariantCulture }.ToImmutableArray(),
|
||||
SupportedPlatforms = Platform.OSX,
|
||||
SupportedTargets = Target.Client
|
||||
});
|
||||
}
|
||||
|
||||
// server, osx
|
||||
if (_storageService.FindFilesInPackage(src, "bin/Client/Windows", "*.dll", true)
|
||||
is { IsSuccess: true, Value: { IsDefaultOrEmpty: false} filesCliWin})
|
||||
{
|
||||
builder.Add(new AssemblyResourceInfo()
|
||||
{
|
||||
Dependencies = ImmutableArray<IPackageDependency>.Empty,
|
||||
FilePaths = filesCliWin,
|
||||
FriendlyName = "AssembliesClientWin",
|
||||
InternalName = "AssembliesClientWin",
|
||||
IsScript = false,
|
||||
LazyLoad = false,
|
||||
LoadPriority = 1,
|
||||
Optional = false,
|
||||
OwnerPackage = src,
|
||||
SupportedCultures = new CultureInfo[]{ CultureInfo.InvariantCulture }.ToImmutableArray(),
|
||||
SupportedPlatforms = Platform.Windows,
|
||||
SupportedTargets = Target.Client
|
||||
});
|
||||
}
|
||||
|
||||
var sharedFound = _storageService.FindFilesInPackage(src, "CSharp/Shared", "*.cs", true)
|
||||
is { IsSuccess: true, Value: { IsDefaultOrEmpty: false } filesCssShared };
|
||||
|
||||
// source files legacy: server
|
||||
if (_storageService.FindFilesInPackage(src, "CSharp/Server", "*.cs", true)
|
||||
is { IsSuccess: true, Value: { IsDefaultOrEmpty: false} filesCssServer})
|
||||
{
|
||||
builder.Add(new AssemblyResourceInfo()
|
||||
{
|
||||
Dependencies = ImmutableArray<IPackageDependency>.Empty,
|
||||
FilePaths = sharedFound ? filesCssServer.Concat(filesCssShared).ToImmutableArray() : filesCssServer,
|
||||
FriendlyName = "CssServer",
|
||||
InternalName = "CssServer",
|
||||
IsScript = true,
|
||||
LazyLoad = false,
|
||||
LoadPriority = 1,
|
||||
Optional = false,
|
||||
OwnerPackage = src,
|
||||
SupportedCultures = new CultureInfo[]{ CultureInfo.InvariantCulture }.ToImmutableArray(),
|
||||
SupportedPlatforms = Platform.Linux | Platform.OSX | Platform.Windows,
|
||||
SupportedTargets = Target.Server
|
||||
});
|
||||
}
|
||||
|
||||
// source files legacy: client
|
||||
if (_storageService.FindFilesInPackage(src, "CSharp/Client", "*.cs", true)
|
||||
is { IsSuccess: true, Value: { IsDefaultOrEmpty: false} filesCssClient})
|
||||
{
|
||||
builder.Add(new AssemblyResourceInfo()
|
||||
{
|
||||
Dependencies = ImmutableArray<IPackageDependency>.Empty,
|
||||
FilePaths = sharedFound ? filesCssClient.Concat(filesCssShared).ToImmutableArray() : filesCssClient,
|
||||
FriendlyName = "CssClient",
|
||||
InternalName = "CssClient",
|
||||
IsScript = true,
|
||||
LazyLoad = false,
|
||||
LoadPriority = 1,
|
||||
Optional = false,
|
||||
OwnerPackage = src,
|
||||
SupportedCultures = new CultureInfo[]{ CultureInfo.InvariantCulture }.ToImmutableArray(),
|
||||
SupportedPlatforms = Platform.Linux | Platform.OSX | Platform.Windows,
|
||||
SupportedTargets = Target.Client
|
||||
});
|
||||
}
|
||||
|
||||
return builder.MoveToImmutable();
|
||||
}
|
||||
private ImmutableArray<ILuaScriptResourceInfo> GetLuaScriptsLegacy(ContentPackage src)
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<ILuaScriptResourceInfo>();
|
||||
|
||||
if (_storageService.FindFilesInPackage(src, "Lua", "*.lua", true)
|
||||
is { IsSuccess: true, Value: { IsDefaultOrEmpty: false } fileAll })
|
||||
{
|
||||
builder.Add(new LuaScriptScriptResourceInfo()
|
||||
{
|
||||
Dependencies = ImmutableArray<IPackageDependency>.Empty,
|
||||
FilePaths = fileAll.Where(path => !path.Contains("Autorun")).ToImmutableArray(),
|
||||
InternalName = "LuaScriptsNormal",
|
||||
Optional = false,
|
||||
IsAutorun = false,
|
||||
OwnerPackage = src,
|
||||
SupportedCultures = new CultureInfo[]{ CultureInfo.InvariantCulture }.ToImmutableArray(),
|
||||
SupportedPlatforms = Platform.Linux | Platform.OSX | Platform.Windows,
|
||||
SupportedTargets = Target.Client | Target.Server
|
||||
});
|
||||
|
||||
builder.Add(new LuaScriptScriptResourceInfo()
|
||||
{
|
||||
Dependencies = ImmutableArray<IPackageDependency>.Empty,
|
||||
FilePaths = fileAll.Where(path => path.Contains("Autorun")).ToImmutableArray(),
|
||||
InternalName = "LuaScriptsAutorun",
|
||||
Optional = false,
|
||||
IsAutorun = true,
|
||||
OwnerPackage = src,
|
||||
SupportedCultures = new CultureInfo[]{ CultureInfo.InvariantCulture }.ToImmutableArray(),
|
||||
SupportedPlatforms = Platform.Linux | Platform.OSX | Platform.Windows,
|
||||
SupportedTargets = Target.Client | Target.Server
|
||||
});
|
||||
}
|
||||
|
||||
return builder.MoveToImmutable();
|
||||
}
|
||||
|
||||
public async Task<ImmutableArray<Result<IModConfigInfo>>> TryParseResourcesAsync(IEnumerable<ContentPackage> sources)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
|
||||
var srcs = sources.ToImmutableArray();
|
||||
var results = new AsyncLocal<ConcurrentQueue<Result<IModConfigInfo>>>();
|
||||
await srcs.ParallelForEachAsync(async pkg =>
|
||||
{
|
||||
try
|
||||
{
|
||||
results.Value.Enqueue(await TryParseResourceAsync(pkg));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// this should never happen but this is to stop partial execution exit.
|
||||
results.Value.Enqueue(
|
||||
FluentResults.Result.Fail<IModConfigInfo>($"Failed to parse package {pkg?.Name}: {e.Message}"));
|
||||
}
|
||||
});
|
||||
return results.Value.ToImmutableArray();
|
||||
}
|
||||
|
||||
public Result<IModConfigInfo> TryParseResource(ContentPackage src) =>
|
||||
TryParseResourceAsync(src).GetAwaiter().GetResult();
|
||||
public ImmutableArray<Result<IModConfigInfo>> TryParseResources(IEnumerable<ContentPackage> sources) =>
|
||||
TryParseResourcesAsync(sources.ToImmutableArray()).GetAwaiter().GetResult();
|
||||
|
||||
private record ResourceAdditionalInfo(
|
||||
string Name,
|
||||
Platform SupportedPlatforms,
|
||||
Target SupportedTargets,
|
||||
ImmutableArray<CultureInfo> SupportedCultures,
|
||||
bool IsOptional,
|
||||
int LoadPriority);
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services.Processing;
|
||||
|
||||
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;
|
||||
public IAssembliesResourcesInfo Process(IReadOnlyList<IAssemblyResourceInfo> src)
|
||||
{
|
||||
return new AssemblyResourcesInfo(src.ToImmutableArray());
|
||||
}
|
||||
|
||||
public IConfigsResourcesInfo Process(IReadOnlyList<IConfigResourceInfo> src)
|
||||
{
|
||||
return new ConfigResourcesInfo(src.ToImmutableArray());
|
||||
}
|
||||
|
||||
public IConfigProfilesResourcesInfo Process(IReadOnlyList<IConfigProfileResourceInfo> src)
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Stateless class
|
||||
GC.SuppressFinalize(this);
|
||||
IsDisposed = true;
|
||||
}
|
||||
|
||||
public bool IsDisposed
|
||||
{
|
||||
get => _isDisposed;
|
||||
set => _isDisposed = value;
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,11 @@ using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.LuaCs.Configuration;
|
||||
using Barotrauma.LuaCs.Networking;
|
||||
using Barotrauma.LuaCs.Services;
|
||||
using Barotrauma.Steam;
|
||||
using FluentResults;
|
||||
using FluentResults.LuaCs;
|
||||
@@ -293,12 +294,31 @@ public class StorageService : IStorageService
|
||||
});
|
||||
}
|
||||
|
||||
public FluentResults.Result<bool> DirectoryExists(string directoryPath)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
try
|
||||
{
|
||||
var di = new DirectoryInfo(directoryPath);
|
||||
return di.Exists;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new FluentResults.Result<bool>().WithError(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<FluentResults.Result<XDocument>> TryLoadXmlAsync(string filePath, Encoding encoding = null)
|
||||
{
|
||||
var r = await TryLoadTextAsync(filePath, encoding);
|
||||
if (r is { IsSuccess: true, Value: {} value } && !value.IsNullOrWhiteSpace())
|
||||
return XDocument.Parse(value);
|
||||
return FluentResults.Result.Fail<XDocument>(GetGeneralError(nameof(TryLoadXml), filePath));
|
||||
try
|
||||
{
|
||||
await using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
|
||||
return await XDocument.LoadAsync(fs, LoadOptions.PreserveWhitespace, CancellationToken.None);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return FluentResults.Result.Fail<XDocument>(GetGeneralError(nameof(TryLoadXmlAsync), filePath));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<FluentResults.Result<string>> TryLoadTextAsync(string filePath, Encoding encoding = null)
|
||||
@@ -601,7 +621,7 @@ public class StorageService : IStorageService
|
||||
localFilePath)));
|
||||
}
|
||||
|
||||
private FluentResults.Result<string> GetAbsFromPackage(ContentPackage package, string localFilePath)
|
||||
public FluentResults.Result<string> GetAbsFromPackage(ContentPackage package, string localFilePath)
|
||||
{
|
||||
if (package is null)
|
||||
{
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.LuaCs.Configuration;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.LuaCs.Networking;
|
||||
using Barotrauma.LuaCs.Services;
|
||||
using Barotrauma.LuaCs.Services.Safe;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.LuaCs.Networking;
|
||||
using Barotrauma.LuaCs.Services;
|
||||
using Barotrauma.LuaCs.Services.Compatibility;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
internal delegate void NetMessageReceived(IReadMessage netMessage);
|
||||
|
||||
internal interface INetworkingService : IReusableService, ILuaCsNetworking
|
||||
internal partial interface INetworkingService : IReusableService, ILuaCsNetworking
|
||||
{
|
||||
bool IsActive { get; }
|
||||
bool IsSynchronized { get; }
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.LuaCs.Events;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface IPackageInfoLookupService : IReusableService
|
||||
{
|
||||
Task<FluentResults.Result<IPackageInfo>> Lookup(string packageName);
|
||||
Task<FluentResults.Result<IPackageInfo>> Lookup(string packageName, ulong steamWorkshopId);
|
||||
Task<FluentResults.Result<IPackageInfo>> Lookup(ulong steamWorkshopId);
|
||||
Task<FluentResults.Result<IPackageInfo>> Lookup(ContentPackage package);
|
||||
void RefreshPackageLists();
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface IPackageListRetrievalService : IService
|
||||
{
|
||||
IEnumerable<ContentPackage> GetEnabledContentPackages();
|
||||
IEnumerable<ContentPackage> GetAllContentPackages();
|
||||
}
|
||||
+6
-4
@@ -17,20 +17,22 @@ public interface IPackageManagementService : IReusableService, ILocalizationsRes
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads and parses the provided <see cref="ContentPackage"/> for <see cref="IResourceInfo"/> supported by the current runtime environment.
|
||||
/// Will overwrite any existing package data.
|
||||
/// </summary>
|
||||
/// <param name="packages"></param>
|
||||
/// <param name="packages">Package to load.</param>
|
||||
/// <returns></returns>
|
||||
Task<FluentResults.Result> LoadPackageInfosAsync(ContentPackage packages);
|
||||
Task<FluentResults.Result> LoadPackageInfosAsync(ContentPackage package);
|
||||
/// <summary>
|
||||
/// Loads and parses the provided <see cref="ContentPackage"/> collection for <see cref="IResourceInfo"/> supported by the current runtime environment.
|
||||
/// Will overwrite any existing package data.
|
||||
/// </summary>
|
||||
/// <param name="packages"></param>
|
||||
/// <param name="packages">List of packages to load.</param>
|
||||
/// <returns></returns>
|
||||
Task<IReadOnlyList<(ContentPackage, FluentResults.Result)>> LoadPackagesInfosAsync(IReadOnlyList<ContentPackage> packages);
|
||||
IReadOnlyList<ContentPackage> GetAllLoadedPackages();
|
||||
void DisposePackageInfos(ContentPackage package);
|
||||
void DisposePackagesInfos(IReadOnlyList<ContentPackage> packages);
|
||||
void DisposeAllPackagesInfos();
|
||||
FluentResults.Result<IPackageDependency> GetPackageDependencyInfo(ContentPackage ownerPackage, string packageName, ulong steamWorkshopId);
|
||||
|
||||
// single
|
||||
FluentResults.Result<IAssembliesResourcesInfo> GetAssembliesInfos(ContentPackage package, bool onlySupportedResources = true);
|
||||
|
||||
@@ -18,6 +18,7 @@ public interface IReusableService : IService
|
||||
/// <summary>
|
||||
/// Base interface inherited by all services.
|
||||
/// </summary>
|
||||
/// <exception cref="ObjectDisposedException">Throws exception if `IsDisposed` return true.</exception>
|
||||
public interface IService : IDisposable
|
||||
{
|
||||
bool IsDisposed { get; }
|
||||
|
||||
@@ -32,6 +32,7 @@ public interface IStorageService : IService
|
||||
ImmutableArray<(string, FluentResults.Result<byte[]>)> LoadPackageBinaryFiles(ContentPackage package, ImmutableArray<string> localFilePaths);
|
||||
ImmutableArray<(string, FluentResults.Result<string>)> LoadPackageTextFiles(ContentPackage package, ImmutableArray<string> localFilePaths);
|
||||
FluentResults.Result<ImmutableArray<string>> FindFilesInPackage(ContentPackage package, string localSubfolder, string regexFilter, bool searchRecursively);
|
||||
FluentResults.Result<string> GetAbsFromPackage(ContentPackage package, string localFilePath);
|
||||
// async
|
||||
// singles
|
||||
Task<FluentResults.Result<XDocument>> LoadPackageXmlAsync(ContentPackage package, string localFilePath);
|
||||
@@ -50,6 +51,8 @@ public interface IStorageService : IService
|
||||
FluentResults.Result TrySaveText(string filePath, in string text, Encoding encoding = null);
|
||||
FluentResults.Result TrySaveBinary(string filePath, in byte[] bytes);
|
||||
FluentResults.Result<bool> FileExists(string filePath);
|
||||
FluentResults.Result<bool> DirectoryExists(string directoryPath);
|
||||
|
||||
//async
|
||||
Task<FluentResults.Result<XDocument>> TryLoadXmlAsync(string filePath, Encoding encoding = null);
|
||||
Task<FluentResults.Result<string>> TryLoadTextAsync(string filePath, Encoding encoding = null);
|
||||
|
||||
Reference in New Issue
Block a user