[Milestone] PackageManagementService completed.

- ContentPackageInfoLookup Service completed.
- Implemented ModConfigService.cs
- Implemented some of the resource processors.
This commit is contained in:
MapleWheels
2025-02-26 12:48:34 -05:00
committed by Maplewheels
parent cb88d215fa
commit 52d920d969
78 changed files with 2331 additions and 422 deletions

View File

@@ -16,6 +16,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Barotrauma.LuaCs.Events;
using static Barotrauma.FabricationRecipe;
namespace Barotrauma
@@ -665,8 +666,6 @@ namespace Barotrauma
bool.TryParse(args[3], out luaCsEnabled);
}
if (luaCsEnabled) { GameMain.LuaCs.Initialize(); }
GameMain.MainMenuScreen.QuickStart(fixedSeed: false, subName, difficulty, levelGenerationParams);
}, getValidArgs: () => new[] { SubmarineInfo.SavedSubmarines.Select(s => s.Name).Distinct().OrderBy(s => s).ToArray() }));
@@ -4226,7 +4225,8 @@ namespace Barotrauma
commands.Add(new Command("cl_lua", $"cl_lua: Runs a string on the client.", (string[] args) =>
{
if (GameMain.Client != null && !GameMain.Client.HasPermission(ClientPermissions.ConsoleCommands))
throw new NotImplementedException();
/*if (GameMain.Client != null && !GameMain.Client.HasPermission(ClientPermissions.ConsoleCommands))
{
ThrowError("Command not permitted.");
return;
@@ -4245,12 +4245,12 @@ namespace Barotrauma
catch(Exception ex)
{
LuaCsLogger.HandleException(ex, LuaCsMessageOrigin.LuaMod);
}
}*/
}));
commands.Add(new Command("cl_reloadlua|cl_reloadcs|cl_reloadluacs", "Re-initializes the LuaCs environment.", (string[] args) =>
{
GameMain.LuaCs.Initialize();
GameMain.LuaCs.EventService.PublishEvent<IEventReloadAllPackages>(sub => sub.OnReloadAllPackages());
}));
commands.Add(new Command("cl_toggleluadebug", "Toggles the MoonSharp Debug Server.", (string[] args) =>
@@ -4262,7 +4262,8 @@ namespace Barotrauma
int.TryParse(args[0], out port);
}
GameMain.LuaCs.ToggleDebugger(port);
throw new NotImplementedException();
//GameMain.LuaCs.ToggleDebugger(port);
}));
}

View File

@@ -1299,6 +1299,19 @@ namespace Barotrauma
{
IsExiting = true;
CreatureMetrics.Save();
try
{
if (_luaCs is not null)
{
_luaCs.Dispose();
_luaCs = null;
}
}
catch (Exception e)
{
DebugConsole.ThrowError($"Error while disposing of LuaCsForBarotrauma: {e.Message} | {e.StackTrace}");
}
DebugConsole.NewMessage("Exiting...");
Client?.Quit();
SteamManager.ShutDown();

View File

@@ -1,18 +0,0 @@
using Microsoft.Xna.Framework;
namespace Barotrauma.LuaCs.Configuration;
public record DisplayableData : IDisplayableData
{
public string InternalName { get; init; }
public ContentPackage OwnerPackage { get; init; }
public string FallbackPackageName { get; init; }
public string DisplayName { get; init; }
public string DisplayModName { get; init; }
public string DisplayCategory { get; init; }
public string Tooltip { get; init; }
public string ImageIcon { get; init; }
public Point IconResolution { get; init; }
public bool ShowWhenNotLoaded { get; init; }
public string Description { get; init; }
}

View File

@@ -1,6 +0,0 @@
using Barotrauma.LuaCs.Configuration;
using Barotrauma.LuaCs.Data;
namespace Barotrauma.LuaCs.Configuration;
public partial interface IConfigBase : IDisplayableData, IDisplayableInitialize { }

View File

@@ -1,63 +0,0 @@
using System.Numerics;
using Barotrauma.LuaCs.Data;
using Microsoft.Xna.Framework;
namespace Barotrauma.LuaCs.Configuration;
/// <summary>
/// Contains the Display Data for use with Menus.
/// </summary>
public interface IDisplayableData : IDataInfo
{
/// <summary>
/// The name to display in GUIs and Menus.
/// </summary>
string DisplayName { get; }
/// <summary>
/// The mod name to display in GUIs and Menus.
/// </summary>
string DisplayModName { get; }
/// <summary>
/// Category this instance falls under. Used by menus when filtering by category.
/// </summary>
string DisplayCategory { get; }
/// <summary>
/// The tooltip shown on hover.
/// </summary>
string Tooltip { get; }
/// <summary>
/// The fully qualified filepath to the image icon for this config.
/// </summary>
string ImageIcon { get; }
/// <summary>
/// Required if ImageIcon is set. X,Y resolution of the image.
/// </summary>
Point IconResolution { get; }
/// <summary>
/// Whether to show the entry in the menu when not loaded.
/// </summary>
bool ShowWhenNotLoaded { get; }
/// <summary>
/// What does this setting do?
/// </summary>
string Description { get; }
}
public interface IDisplayableInitialize
{
void Initialize(IDisplayableData values);
// copy this as needed
/*public void Initialize(IDisplayableData values)
{
this.InternalName = values.InternalName;
this.OwnerPackage = values.OwnerPackage;
this.DisplayName = values.DisplayName;
this.DisplayModName = values.DisplayModName;
this.DisplayCategory = values.DisplayCategory;
this.Tooltip = values.Tooltip;
this.ImageIcon = values.ImageIcon;
this.IconResolution = values.IconResolution;
this.ShowWhenNotLoaded = values.ShowWhenNotLoaded;
}*/
}

View File

@@ -5,7 +5,7 @@ namespace Barotrauma.LuaCs.Data;
public partial record ModConfigInfo : IModConfigInfo
{
public ImmutableArray<IStylesResourceInfo> StylesResourceInfos { get; init; }
public ImmutableArray<IStylesResourceInfo> Styles { get; init; }
}
public record StylesResourceInfo : IStylesResourceInfo
@@ -18,6 +18,6 @@ public record StylesResourceInfo : IStylesResourceInfo
public ImmutableArray<CultureInfo> SupportedCultures { get; init; }
public string InternalName { get; init; }
public ContentPackage OwnerPackage { get; init; }
public string FallbackPackageName { get; }
public ImmutableArray<IPackageDependencyInfo> Dependencies { get; init; }
public string FallbackPackageName { get; init; }
public ImmutableArray<IPackageDependency> Dependencies { get; init; }
}

View File

@@ -2,4 +2,18 @@ using Barotrauma.LuaCs.Configuration;
namespace Barotrauma.LuaCs.Data;
public partial interface IConfigInfo : IDisplayableData { }
public partial interface IConfigInfo
{
/// <summary>
/// Should this config be displayed in end-user menus.
/// </summary>
bool ShowInMenus { get; }
/// <summary>
/// User-friendly on-hover tooltip text or Localization Token.
/// </summary>
string Tooltip { get; }
/// <summary>
/// Icon for display in menus, if available.
/// </summary>
string ImageIconPath { get; }
}

View File

@@ -4,12 +4,12 @@ namespace Barotrauma.LuaCs.Data;
public partial interface IModConfigInfo : IStylesResourcesInfo { }
public interface IStylesResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageInfo, IPackageDependenciesInfo { }
public interface IStylesResourceInfo : IResourceInfo, IResourceCultureInfo, IDataInfo, IPackageDependenciesInfo { }
public interface IStylesResourcesInfo
{
/// <summary>
/// Collection of loadable styles data.
/// </summary>
ImmutableArray<IStylesResourceInfo> StylesResourceInfos { get; }
ImmutableArray<IStylesResourceInfo> Styles { get; }
}

View File

@@ -57,6 +57,9 @@ namespace Barotrauma
public static void CheckUpdate()
{
throw new NotImplementedException();
/*// TODO: Rewrite this to not rely on LuaCsSetup.
if (!File.Exists(LuaCsSetup.VersionFile)) { return; }
ContentPackage luaPackage = LuaCsSetup.GetPackage(new SteamWorkshopId(GameMain.LuaCs.LuaForBarotraumaSteamId?.Value ?? 0));
@@ -116,6 +119,7 @@ namespace Barotrauma
msg.Close();
return true;
};
*/
}
}

View File

@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using Barotrauma.LuaCs.Configuration;
using Barotrauma.LuaCs.Networking;
using Barotrauma.LuaCs.Services;
using Barotrauma.Networking;
namespace Barotrauma.LuaCs.Services;

View File

@@ -0,0 +1,8 @@
using Barotrauma.Networking;
namespace Barotrauma.LuaCs.Services;
internal partial interface INetworkingService : IReusableService
{
void NetMessageReceived(IReadMessage message, ServerPacketHeader header);
}

View File

@@ -6,7 +6,7 @@ namespace Barotrauma.LuaCs.Services;
/// <summary>
/// Loads XML Style assets from the given content package.
/// </summary>
public interface IStylesService : IReusableService
public interface IStylesService : IService
{
/// <summary>
/// Tries to load the styles file for the given <see cref="ContentPackage"/> and path into a new <see cref="UIStyleProcessor"/> instance.

View File

@@ -3,9 +3,9 @@ using Barotrauma.Networking;
using System;
using System.Collections.Generic;
namespace Barotrauma.LuaCs.Networking;
namespace Barotrauma.LuaCs.Services;
partial class NetworkingService
partial class NetworkingService : INetworkingService
{
private Dictionary<ushort, Queue<IReadMessage>> receiveQueue = new Dictionary<ushort, Queue<IReadMessage>>();
@@ -44,6 +44,11 @@ partial class NetworkingService
}
}
public void NetMessageReceived(IReadMessage message, ServerPacketHeader header)
{
throw new NotImplementedException();
}
public INetWriteMessage Start(Guid netId)
{
var message = new WriteOnlyMessage();

View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs.Services.Processing;
using FluentResults;
// ReSharper disable UseCollectionExpression
namespace Barotrauma.LuaCs.Services;
public partial class PackageManagementService : IPackageManagementService
{
public PackageManagementService(
IConverterServiceAsync<ContentPackage, IModConfigInfo> modConfigParserService,
IProcessorService<IReadOnlyList<IAssemblyResourceInfo>, IAssembliesResourcesInfo> assemblyInfoConverter,
IProcessorService<IReadOnlyList<IConfigResourceInfo>, IConfigsResourcesInfo> configsInfoConverter,
IProcessorService<IReadOnlyList<IConfigProfileResourceInfo>, IConfigProfilesResourcesInfo> configProfilesConverter,
IProcessorService<IReadOnlyList<ILocalizationResourceInfo>, ILocalizationsResourcesInfo> localizationsConverter,
IProcessorService<IReadOnlyList<ILuaScriptResourceInfo>, ILuaScriptsResourcesInfo> luaScriptsConverter,
IPackageInfoLookupService packageInfoLookupService, Func<IReadOnlyList<IStylesResourceInfo>, IStylesResourcesInfo> stylesInfoConverter)
{
_stylesInfoConverter = stylesInfoConverter;
_modConfigParserService = modConfigParserService;
_assemblyInfoConverter = assemblyInfoConverter;
_configsInfoConverter = configsInfoConverter;
_configProfilesConverter = configProfilesConverter;
_localizationsConverter = localizationsConverter;
_luaScriptsConverter = luaScriptsConverter;
_packageInfoLookupService = packageInfoLookupService;
}
private readonly Func<IReadOnlyList<IStylesResourceInfo>, IStylesResourcesInfo> _stylesInfoConverter;
public ImmutableArray<IStylesResourceInfo> Styles => _modInfos.IsEmpty ? ImmutableArray<IStylesResourceInfo>.Empty
: _modInfos.SelectMany(kvp => kvp.Value.Styles).ToImmutableArray();
public Result<IStylesResourcesInfo> GetStylesInfos(ContentPackage package, bool onlySupportedResources = true)
{
((IService)this).CheckDisposed();
if (package is null)
return FluentResults.Result.Fail($"{nameof(GetStylesInfos)}: ContentPackage is null.");
if (_modInfos.TryGetValue(package, out var result))
return FluentResults.Result.Ok<IStylesResourcesInfo>(_stylesInfoConverter(onlySupportedResources?
result.Styles.Where(r =>
(r.SupportedPlatforms & ModUtils.Environment.CurrentPlatform) > 0
&& (r.SupportedTargets & ModUtils.Environment.CurrentTarget) > 0).ToImmutableArray()
: result.Styles
));
return FluentResults.Result.Fail(
$"{nameof(GetStylesInfos)}: ContentPackage {package.Name} is not registered.");
}
public Result<IStylesResourcesInfo> GetStylesInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true)
{
((IService)this).CheckDisposed();
if (packages is null || packages.Count == 0)
return FluentResults.Result.Fail($"{nameof(GetStylesInfos)}: ContentPackage list is null or empty.");
var builder = ImmutableArray.CreateBuilder<IStylesResourceInfo>();
foreach (var package in packages)
{
if (_modInfos.TryGetValue(package, out var result) && result.Styles is { IsEmpty: false })
{
builder.AddRange(onlySupportedResources?
result.Styles.Where(r =>
(r.SupportedPlatforms & ModUtils.Environment.CurrentPlatform) > 0
&& (r.SupportedTargets & ModUtils.Environment.CurrentTarget) > 0).ToImmutableArray()
: result.Styles);
}
}
return FluentResults.Result.Ok(_stylesInfoConverter(builder.MoveToImmutable()));
}
public async Task<Result<IStylesResourcesInfo>> GetStylesInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true)
{
return await Task.Run(() => GetStylesInfos(packages, onlySupportedResources));
}
}

View File

@@ -1,9 +0,0 @@
using System.Xml.Linq;
using Barotrauma.LuaCs.Data;
namespace Barotrauma.LuaCs.Services.Processing;
#region XmlToResourceParsers
public interface IXmlStylesToResConverterService : IXmlResourceConverterService<IStylesResourceInfo> { }
#endregion

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
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<Result<IModConfigInfo>> GetModConfigInfoAsync(ContentPackage package, XElement root)
{
var asm = root.GetChildElements("Assembly").ToImmutableArray();
var loc = root.GetChildElements("Localization").ToImmutableArray();
var cfg = root.GetChildElements("Config").ToImmutableArray();
var lua = root.GetChildElements("Lua").ToImmutableArray();
var stl = root.GetChildElements("Style").ToImmutableArray();
return FluentResults.Result.Ok<IModConfigInfo>(new ModConfigInfo()
{
Package = package,
PackageName = package.Name,
Assemblies = asm.Any() ? GetAssemblies(package, asm) : ImmutableArray<IAssemblyResourceInfo>.Empty,
Localizations = loc.Any() ? GetLocalizations(package, loc) : ImmutableArray<ILocalizationResourceInfo>.Empty,
Configs = cfg.Any() ? GetConfigs(package, cfg) : ImmutableArray<IConfigResourceInfo>.Empty,
ConfigProfiles = cfg.Any() ? GetConfigProfiles(package, cfg) : ImmutableArray<IConfigProfileResourceInfo>.Empty,
LuaScripts = lua.Any() ? GetLuaScripts(package, lua) : ImmutableArray<ILuaScriptResourceInfo>.Empty,
Styles = stl.Any() ? GetStyles(package, stl) : ImmutableArray<IStylesResourceInfo>.Empty
});
}
private ImmutableArray<IStylesResourceInfo> GetStyles(ContentPackage src, IEnumerable<XElement> elements)
{
throw new NotImplementedException();
}
}

View File

@@ -116,10 +116,5 @@ public class StylesService : IStylesService
GC.SuppressFinalize(this);
}
public FluentResults.Result Reset()
{
return UnloadAllStyles();
}
public bool IsDisposed { get; private set; }
}

View File

@@ -2905,8 +2905,6 @@ namespace Barotrauma.Networking
public void Quit()
{
GameMain.LuaCs.Stop();
ClientPeer?.Close(PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
GUIMessageBox.MessageBoxes.RemoveAll(c => c?.UserData is RoundSummary);

View File

@@ -542,8 +542,10 @@ namespace Barotrauma
}
};
string version = File.Exists(LuaCsSetup.VersionFile) ? File.ReadAllText(LuaCsSetup.VersionFile) : "Github";
// TODO: Implement version reading.
//string version = File.Exists(LuaCsSetup.VersionFile) ? File.ReadAllText(LuaCsSetup.VersionFile) : "Github";
string version = "NOT_IMPLEMENTED";
new GUITextBlock(new RectTransform(new Point(300, 30), Frame.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(10, 10) }, $"Using LuaCsForBarotrauma revision {AssemblyInfo.GitRevision} version {version}", Color.Red)
{
IgnoreLayoutGroups = false
@@ -703,8 +705,6 @@ namespace Barotrauma
#region Selection
public override void Select()
{
GameMain.LuaCs.Stop();
ResetModUpdateButton();
if (WorkshopItemsToUpdate.Any())
@@ -1314,8 +1314,6 @@ namespace Barotrauma
return;
}
GameMain.LuaCs.CheckInitialize();
selectedSub = new SubmarineInfo(Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"));
GameMain.GameSession = new GameSession(selectedSub, Option.None, CampaignDataPath.CreateRegular(savePath), GameModePreset.SinglePlayerCampaign, settings, mapSeed);
@@ -1331,8 +1329,6 @@ namespace Barotrauma
{
if (string.IsNullOrWhiteSpace(path)) return;
GameMain.LuaCs.CheckInitialize();
try
{
CampaignDataPath dataPath =

View File

@@ -815,7 +815,7 @@ namespace Barotrauma
var tempBuffer = new ReadWriteMessage();
WriteStatus(tempBuffer, forceAfflictionData: true);
if (msgLengthBeforeStatus + tempBuffer.LengthBytes >= 255 && restrictMessageSize && GameMain.LuaCs.Networking.RestrictMessageSize)
if (msgLengthBeforeStatus + tempBuffer.LengthBytes >= 255 && restrictMessageSize && (GameMain.LuaCs.RestrictMessageSize?.Value ?? false))
{
msg.WriteBoolean(false);
if (msgLengthBeforeStatus < 255)

View File

@@ -10,6 +10,7 @@ using System.Linq;
using System.Text;
using Barotrauma.Steam;
using Barotrauma.Extensions;
using Barotrauma.LuaCs.Events;
namespace Barotrauma
{
@@ -1292,7 +1293,8 @@ namespace Barotrauma
{
try
{
GameMain.LuaCs.Lua.DoString(string.Join(" ", args));
throw new NotImplementedException();
//GameMain.LuaCs.Lua.DoString(string.Join(" ", args));
}
catch (Exception ex)
{
@@ -1302,7 +1304,8 @@ namespace Barotrauma
commands.Add(new Command("reloadlua|reloadcs|reloadluacs", "Re-initializes the LuaCs environment.", (string[] args) =>
{
GameMain.LuaCs.Initialize();
//GameMain.LuaCs.Initialize();
GameMain.LuaCs.EventService.PublishEvent<IEventReloadAllPackages>(sub => sub.OnReloadAllPackages());
}));
commands.Add(new Command("toggleluadebug", "Toggles the MoonSharp Debug Server.", (string[] args) =>
@@ -1314,7 +1317,8 @@ namespace Barotrauma
int.TryParse(args[0], out port);
}
GameMain.LuaCs.ToggleDebugger(port);
throw new NotImplementedException();
//GameMain.LuaCs.ToggleDebugger(port);
}));
commands.Add(new Command("install_cl_lua|install_cl|install_cl_cs|install_cl_luacs", "Installs Client-Side LuaCs into your client.", (string[] args) =>

View File

@@ -132,8 +132,6 @@ namespace Barotrauma
NetLobbyScreen = new NetLobbyScreen();
CheckContentPackage();
LuaCs = new LuaCsSetup();
}
@@ -454,7 +452,18 @@ namespace Barotrauma
public void Exit()
{
ShouldRun = false;
GameMain.LuaCs.Dispose();
try
{
if (_luaCs is not null)
{
_luaCs.Dispose();
_luaCs = null;
}
}
catch (Exception e)
{
DebugConsole.ThrowError($"Error while disposing of LuaCsForBarotrauma: {e.Message} | {e.StackTrace}");
}
}
}
}

View File

@@ -9,7 +9,9 @@ namespace Barotrauma
{
public static void Install()
{
ContentPackage luaPackage = LuaCsSetup.GetPackage(LuaCsSetup.LuaForBarotraumaId);
throw new NotImplementedException();
// TODO: Refactor the installer to not be dependent on LuaCsSetup.
/*ContentPackage luaPackage = LuaCsSetup.GetPackage();
if (luaPackage == null)
{
@@ -64,7 +66,7 @@ namespace Barotrauma
return;
}
GameMain.Server.SendChatMessage("Client-Side LuaCs installed, restart your game to apply changes.", ChatMessageType.ServerMessageBox);
GameMain.Server.SendChatMessage("Client-Side LuaCs installed, restart your game to apply changes.", ChatMessageType.ServerMessageBox);*/
}
}
}

View File

@@ -0,0 +1,9 @@
using Barotrauma.Networking;
namespace Barotrauma.LuaCs.Services;
internal partial interface INetworkingService : IReusableService
{
void NetMessageReceived(IReadMessage message, ClientPacketHeader header, Client client);
}

View File

@@ -4,9 +4,10 @@ using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma.LuaCs.Networking;
// ReSharper disable once CheckNamespace
namespace Barotrauma.LuaCs.Services;
partial class NetworkingService
partial class NetworkingService : INetworkingService
{
private const int MaxRegisterPerClient = 1000;

View File

@@ -0,0 +1,26 @@
using System.Collections.Generic;
using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs.Services.Processing;
namespace Barotrauma.LuaCs.Services;
public partial class PackageManagementService
{
public PackageManagementService(
IConverterServiceAsync<ContentPackage, IModConfigInfo> modConfigParserService,
IProcessorService<IReadOnlyList<IAssemblyResourceInfo>, IAssembliesResourcesInfo> assemblyInfoConverter,
IProcessorService<IReadOnlyList<IConfigResourceInfo>, IConfigsResourcesInfo> configsInfoConverter,
IProcessorService<IReadOnlyList<IConfigProfileResourceInfo>, IConfigProfilesResourcesInfo> configProfilesConverter,
IProcessorService<IReadOnlyList<ILocalizationResourceInfo>, ILocalizationsResourcesInfo> localizationsConverter,
IProcessorService<IReadOnlyList<ILuaScriptResourceInfo>, ILuaScriptsResourcesInfo> luaScriptsConverter,
IPackageInfoLookupService packageInfoLookupService)
{
_modConfigParserService = modConfigParserService;
_assemblyInfoConverter = assemblyInfoConverter;
_configsInfoConverter = configsInfoConverter;
_configProfilesConverter = configProfilesConverter;
_localizationsConverter = localizationsConverter;
_luaScriptsConverter = luaScriptsConverter;
_packageInfoLookupService = packageInfoLookupService;
}
}

View File

@@ -0,0 +1,33 @@
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<Result<IModConfigInfo>> GetModConfigInfoAsync(ContentPackage package, XElement root)
{
var asm = root.GetChildElements("Assembly").ToImmutableArray();
var loc = root.GetChildElements("Localization").ToImmutableArray();
var cfg = root.GetChildElements("Config").ToImmutableArray();
var lua = root.GetChildElements("Lua").ToImmutableArray();
return FluentResults.Result.Ok<IModConfigInfo>(new ModConfigInfo()
{
Package = package,
PackageName = package.Name,
Assemblies = asm.Any() ? GetAssemblies(package, asm) : ImmutableArray<IAssemblyResourceInfo>.Empty,
Localizations = loc.Any() ? GetLocalizations(package, loc) : ImmutableArray<ILocalizationResourceInfo>.Empty,
Configs = cfg.Any() ? GetConfigs(package, cfg) : ImmutableArray<IConfigResourceInfo>.Empty,
ConfigProfiles = cfg.Any() ? GetConfigProfiles(package, cfg) : ImmutableArray<IConfigProfileResourceInfo>.Empty,
LuaScripts = lua.Any() ? GetLuaScripts(package, lua) : ImmutableArray<ILuaScriptResourceInfo>.Empty
});
}
}

View File

@@ -245,7 +245,6 @@ namespace Barotrauma.Networking
VoipServer = new VoipServer(serverPeer);
GameMain.LuaCs.Initialize();
Log("Server started", ServerLog.MessageType.ServerMessage);
GameMain.NetLobbyScreen.Select();
@@ -838,7 +837,7 @@ namespace Barotrauma.Networking
ClientPacketHeader header = (ClientPacketHeader)inc.ReadByte();
GameMain.LuaCs.Networking.NetMessageReceived(inc, header, connectedClient);
GameMain.LuaCs.NetworkingService.NetMessageReceived(inc, header, connectedClient);
switch (header)
{

View File

@@ -12,6 +12,7 @@ using Barotrauma.IO;
using Barotrauma.LuaCs.Events;
using Barotrauma.Steam;
using Microsoft.Xna.Framework;
using OneOf.Types;
namespace Barotrauma
{
@@ -339,10 +340,7 @@ namespace Barotrauma
}
GameMain.LuaCs.EventService.PublishEvent<IEventAllPackageListChanged>(sub =>
sub.OnAllPackageListChanged(corePackages
.Select((ContentPackage p) => p)
.Union(regularPackages.Select((ContentPackage p) => p))
.ToImmutableArray()));
sub.OnAllPackageListChanged(ContentPackageManager.CorePackages, ContentPackageManager.RegularPackages));
}
private readonly string directory;

View File

@@ -0,0 +1,76 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Barotrauma.LuaCs;
// taken from: <https://gist.github.com/cajuncoding/a88f0d00847dcfc241ae80d1c7bafb1e?permalink_comment_id=4498792>
public sealed class AsyncReaderWriterLock : IDisposable
{
readonly SemaphoreSlim _readSemaphore = new SemaphoreSlim(1, 1);
readonly SemaphoreSlim _writeSemaphore = new SemaphoreSlim(1, 1);
int _readerCount;
public async Task<IDisposable> AcquireWriterLock(CancellationToken token = default)
{
await _writeSemaphore.WaitAsync(token).ConfigureAwait(false);
try
{
await _readSemaphore.WaitAsync(token).ConfigureAwait(false);
}
catch
{
_writeSemaphore.Release();
throw;
}
return new LockToken(ReleaseWriterLock);
}
private void ReleaseWriterLock()
{
_readSemaphore.Release();
_writeSemaphore.Release();
}
public async Task<IDisposable> AcquireReaderLock(CancellationToken token = default)
{
await _writeSemaphore.WaitAsync(token).ConfigureAwait(false);
if (Interlocked.Increment(ref _readerCount) == 1)
{
try
{
await _readSemaphore.WaitAsync(token).ConfigureAwait(false);
}
catch
{
Interlocked.Decrement(ref _readerCount);
_writeSemaphore.Release();
throw;
}
}
_writeSemaphore.Release();
return new LockToken(ReleaseReaderLock);
}
private void ReleaseReaderLock()
{
if (Interlocked.Decrement(ref _readerCount) == 0)
_readSemaphore.Release();
}
public void Dispose()
{
_writeSemaphore.Dispose();
_readSemaphore.Dispose();
}
private sealed class LockToken : IDisposable
{
private readonly Action _action;
public LockToken(Action action) => _action = action;
public void Dispose() => _action?.Invoke();
}
}

View File

@@ -1,5 +1,5 @@
using System;
using Barotrauma.LuaCs.Networking;
using Barotrauma.LuaCs.Services;
namespace Barotrauma.LuaCs.Configuration;

View File

@@ -1,4 +1,4 @@
using Barotrauma.LuaCs.Networking;
using Barotrauma.LuaCs.Services;
namespace Barotrauma.LuaCs.Configuration;

View File

@@ -5,6 +5,7 @@ using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Barotrauma.Steam;
namespace Barotrauma.LuaCs.Data;
@@ -14,9 +15,6 @@ public partial record ModConfigInfo : IModConfigInfo
{
public ContentPackage Package { get; init; }
public string PackageName { get; init; }
public TargetRunMode RunModes { get; init; }
public ImmutableArray<CultureInfo> SupportedCultures { get; init; }
public ImmutableArray<IAssemblyResourceInfo> Assemblies { get; init; }
public ImmutableArray<ILocalizationResourceInfo> Localizations { get; init; }
public ImmutableArray<ILuaScriptResourceInfo> LuaScripts { get; init; }
@@ -28,10 +26,15 @@ public partial record ModConfigInfo : IModConfigInfo
#region DataContracts
public record AssemblyResourceInfo : IAssemblyResourceInfo
public record AssemblyResourcesInfo(ImmutableArray<IAssemblyResourceInfo> Assemblies) : IAssembliesResourcesInfo;
public record LocalizationResourcesInfo(ImmutableArray<ILocalizationResourceInfo> Localizations) : ILocalizationsResourcesInfo;
public record LuaScriptsResourcesInfo(ImmutableArray<ILuaScriptResourceInfo> LuaScripts) : ILuaScriptsResourcesInfo;
public record ConfigResourcesInfo(ImmutableArray<IConfigResourceInfo> Configs) : IConfigsResourcesInfo;
public record ConfigProfilesResourcesInfo(ImmutableArray<IConfigProfileResourceInfo> ConfigProfiles) : IConfigProfilesResourcesInfo;
public record AssemblyResourceInfo : IAssemblyResourceInfo
{
public ContentPackage OwnerPackage { get; init; }
public string FallbackPackageName { get; init; }
public string FriendlyName { get; init; }
public bool IsScript { get; init; }
public string InternalName { get; init; }
@@ -41,138 +44,148 @@ public record AssemblyResourceInfo : IAssemblyResourceInfo
public int LoadPriority { get; init; }
public ImmutableArray<string> FilePaths { get; init; }
public ImmutableArray<CultureInfo> SupportedCultures { get; init; }
public ImmutableArray<IPackageDependencyInfo> Dependencies { get; init; }
public ImmutableArray<IPackageDependency> Dependencies { get; init; }
public bool Optional { get; init; }
}
public record DependencyInfo : IPackageDependencyInfo
public record PackageDependency : IPackageDependency
{
public PackageDependency(ContentPackage package, IPackageInfo dependencyInfo, string internalName)
{
Dependency = dependencyInfo ?? throw new ArgumentNullException(nameof(dependencyInfo));
OwnerPackage = package ?? throw new ArgumentNullException(nameof(package));
InternalName = internalName ?? throw new ArgumentNullException(nameof(internalName));
}
public string InternalName { get; init; }
public ContentPackage OwnerPackage { get; init; }
public string FolderPath { get; init; }
public string FallbackPackageName { get; init; }
public ulong SteamWorkshopId { get; init; }
public ContentPackage DependencyPackage { get; init; }
public bool IsMissing { get; init; }
public bool IsWorkshopInstallation { get; init; }
public virtual bool Equals(DependencyInfo other) => Equals(this, other);
public override int GetHashCode()
{
if (DependencyPackage is not null)
return DependencyPackage.GetHashCode();
if (SteamWorkshopId != 0)
return SteamWorkshopId.GetHashCode();
if (!FallbackPackageName.IsNullOrWhiteSpace() && !FolderPath.IsNullOrWhiteSpace())
return string.Concat(FallbackPackageName, FolderPath).GetHashCode();
if (!InternalName.IsNullOrWhiteSpace() && !FolderPath.IsNullOrWhiteSpace())
return string.Concat(InternalName, FolderPath).GetHashCode();
return base.GetHashCode();
}
bool IEqualityComparer<IPackageDependencyInfo>.Equals(IPackageDependencyInfo x, IPackageDependencyInfo y) => DependencyInfo.Equals(x, y);
public IPackageInfo Dependency { get; init; }
public override int GetHashCode() => Dependency.GetHashCode();
public static bool operator ==(IPackageDependencyInfo x, DependencyInfo y) => y?.Equals(x) ?? false;
public static bool operator !=(IPackageDependencyInfo x, DependencyInfo y) => y?.Equals(x) ?? false;
public static bool Equals(IPackageDependencyInfo x, IPackageDependencyInfo y)
{
if (x is null)
return false;
if (y is null)
return false;
if (x == y)
return true;
if (x.DependencyPackage is not null && y.DependencyPackage is not null)
return y.DependencyPackage == x.DependencyPackage;
if (!x.FolderPath.IsNullOrWhiteSpace()
&& !y.FolderPath.IsNullOrWhiteSpace()
&& y.FolderPath == x.FolderPath)
return true;
if (!x.FolderPath.IsNullOrWhiteSpace() != !y.FolderPath.IsNullOrWhiteSpace())
return false;
if (!x.FallbackPackageName.IsNullOrWhiteSpace()
&& !y.FallbackPackageName.IsNullOrWhiteSpace()
&& y.FallbackPackageName == x.FallbackPackageName)
return true;
if (x.SteamWorkshopId != 0 && y.SteamWorkshopId == x.SteamWorkshopId)
return true;
}
return false;
}
public record PackageInfo : IPackageInfo
{
public string Name { get; private set; }
public ulong SteamWorkshopId { get; private set; }
public uint Id { get; private set; }
/// <summary>
/// Returns the hash code unique for the package reference.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
/// <remarks>The hash should only be collision-free when referring to different packages.</remarks>
public int GetHashCode(IPackageDependencyInfo obj)
{
int hashCode = Seed;
hashCode = ApplyHashString(hashCode, obj.FallbackPackageName);
hashCode = ApplyHashString(hashCode, obj.InternalName);
if (obj.SteamWorkshopId > 0)
hashCode ^= (int)obj.SteamWorkshopId;
private readonly Func<IPackageInfo, ContentPackage> _getPackage;
int ApplyHashString(int currentValue, string str)
public ContentPackage GetPackage() => _getPackage?.Invoke(this) ?? null;
public void UpdateInfo(string name, ulong steamId, uint packageId)
{
if (name.IsNullOrWhiteSpace() || steamId == 0 || packageId == 0)
{
try
{
if (str is null || str.Length < 1)
return currentValue;
byte[] b = Encoding.UTF8.GetBytes(str);
for (int i = 0; i < Math.Min(24, b.Length-1); i++)
currentValue ^= b[i];
return currentValue;
}
catch
{
return currentValue;
}
throw new ArgumentException(
$"{nameof(PackageInfo)}: You cannot update a package with an invalid name or steam id with a valid id, or vice-versa.");
}
return hashCode;
Name = name;
SteamWorkshopId = steamId;
Id = packageId;
}
public PackageInfo(ContentPackage package, uint id, Func<IPackageInfo, ContentPackage> getPackage)
{
if (package is null)
throw new ArgumentNullException($"{nameof(PackageInfo)}: package is null");
if (id == 0)
throw new ArgumentNullException($"{nameof(PackageInfo)}: id is zero.");
this.Name = package.Name;
this.SteamWorkshopId = package.TryExtractSteamWorkshopId(out var sId) ? sId.Value : 0;
this.Id = id;
this._getPackage = getPackage;
}
private static readonly int Seed = new Random().Next(436457, int.MaxValue-900);
public PackageInfo(string name, ulong steamWorkshopId, uint id, Func<IPackageInfo, ContentPackage> getPackage)
{
Name = !name.IsNullOrWhiteSpace() ? name : throw new ArgumentNullException($"{nameof(PackageInfo)}: name cannot be null or empty.");
SteamWorkshopId = steamWorkshopId != 0 ? steamWorkshopId : throw new ArgumentNullException($"{nameof(PackageInfo)}: steam id cannot be 0.");
this.Id = id;
this._getPackage = getPackage;
}
public PackageInfo(string name, uint id, Func<IPackageInfo, ContentPackage> getPackage)
{
Name = name ?? throw new ArgumentNullException($"{nameof(PackageInfo)}: name cannot be null or empty.");
this.SteamWorkshopId = 0;
this.Id = id;
this._getPackage = getPackage;
}
public PackageInfo(ulong steamWorkshopId, uint id, Func<IPackageInfo, ContentPackage> getPackage)
{
SteamWorkshopId = steamWorkshopId != 0 ? steamWorkshopId : throw new ArgumentNullException($"{nameof(PackageInfo)}: steamid cannot be 0.");
this.Id = id;
this._getPackage = getPackage;
}
public override int GetHashCode()
{
return (int)Id;
}
public virtual bool Equals(PackageInfo other)
{
return ((IEquatable<IPackageInfo>)this).Equals(other);
}
}
public record ConfigResourceInfo : IConfigResourceInfo
{
public Platform SupportedPlatforms { get; init; }
public Target SupportedTargets { get; init; }
public int LoadPriority { get; init; }
public ImmutableArray<string> FilePaths { get; init; }
public bool Optional { get; init; }
public ImmutableArray<CultureInfo> SupportedCultures { get; init; }
public ImmutableArray<IPackageDependency> Dependencies { get; init; }
public string InternalName { get; init; }
public ContentPackage OwnerPackage { get; init; }
}
public record ConfigProfileResourceInfo : IConfigProfileResourceInfo
{
public Platform SupportedPlatforms { get; init; }
public Target SupportedTargets { get; init; }
public int LoadPriority { get; init; }
public ImmutableArray<string> FilePaths { get; init; }
public bool Optional { get; init; }
public ImmutableArray<CultureInfo> SupportedCultures { get; init; }
public ImmutableArray<IPackageDependency> Dependencies { get; init; }
public string InternalName { get; init; }
public ContentPackage OwnerPackage { get; init; }
}
public record LocalizationResourceInfo : ILocalizationResourceInfo
{
public string InternalName { get; init; }
public ContentPackage OwnerPackage { get; init; }
public string FallbackPackageName { get; init; }
public CultureInfo TargetCulture { get; init; }
public Platform SupportedPlatforms { get; init; }
public Target SupportedTargets { get; init; }
public int LoadPriority { get; init; }
public ImmutableArray<string> FilePaths { get; init; }
public ImmutableArray<CultureInfo> SupportedCultures { get; init; }
public ImmutableArray<IPackageDependencyInfo> Dependencies { get; init; }
public ImmutableArray<IPackageDependency> Dependencies { get; init; }
public bool Optional { get; init; }
}
public readonly struct LuaScriptScriptResourceInfo : ILuaScriptResourceInfo
{
public ContentPackage OwnerPackage { get; init; }
public string FallbackPackageName { get; init; }
public Platform SupportedPlatforms { get; init; }
public Target SupportedTargets { get; init; }
public int LoadPriority { get; init; }
public ImmutableArray<string> FilePaths { get; init; }
public ImmutableArray<CultureInfo> SupportedCultures { get; init; }
public ImmutableArray<IPackageDependencyInfo> Dependencies { get; init; }
public ImmutableArray<IPackageDependency> Dependencies { get; init; }
public bool Optional { get; init; }
public string InternalName { get; init; }
public bool LazyLoad { get; init; }
public bool IsAutorun { get; init; }
}
#endregion

View File

@@ -7,7 +7,7 @@ namespace Barotrauma.LuaCs.Data;
public enum Platform
{
Linux=0x1,
MacOS=0x2,
OSX=0x2,
Windows=0x4
}
@@ -17,12 +17,3 @@ public enum Target
Client=0x1,
Server=0x2
}
[Flags]
public enum TargetRunMode
{
ClientEnabled = 0x1,
ClientAlways = 0x2,
ServerEnabled = 0x4,
ServerAlways = 0x8
}

View File

@@ -21,11 +21,6 @@ public interface IPlatformInfo
Target SupportedTargets { get; }
}
/// <summary>
/// All info we should have on a package for a given resource.
/// </summary>
public interface IPackageInfo : IDataInfo { }
/// <summary>
/// ResourceInfos contain metadata about a resource.

View File

@@ -1,5 +1,5 @@
using System;
using Barotrauma.LuaCs.Networking;
using Barotrauma.LuaCs.Services;
using Barotrauma.Networking;
namespace Barotrauma.LuaCs.Data;
@@ -11,13 +11,41 @@ public partial interface IConfigInfo : IDataInfo
/// Specifies the data type this should be initialized to (ie. string, int, vector, etc.)
/// Custom types can be registered by mods.
/// </summary>
string DataType { get; }
Type DataType { get; }
/// <summary>
/// String version of the default value.
/// </summary>
string DefaultValue { get; }
/// <summary>
/// The value the last time this config was saved, if found in /data/.
/// </summary>
string StoredValue { get; }
/// <summary>
/// Custom data storage for other type-specific information needed. IE. Used to store the min,
/// max and step values for the <b>IConfigRangeEntry(T)</b>.
/// </summary>
string CustomParameters { get; }
/// <summary>
/// <b>[Multiplayer]</b><br/>
/// What permissions do clients require to change this setting.
/// </summary>
ClientPermissions RequiredPermissions { get; }
/// <summary>
/// Whether a value can be changed at runtime.
/// In what <see cref="RunState"/>s is this config editable.
/// <br/>
/// Note: Setting this to value lower than 'Configuration` will render this config read-only.
/// </summary>
RunState CanEditStates { get; }
/// <summary>
/// Network synchronization rules for this config.
/// </summary>
bool IsReadOnly { get; }
NetSync NetSync { get; }
/// <summary>
/// User friendly name or Localization Token.
/// </summary>
string DisplayName { get; }
/// <summary>
/// User friendly description or Localization Token.
/// </summary>
string Description { get; }
}

View File

@@ -16,10 +16,6 @@ public interface IDataInfo : IEqualityComparer<IDataInfo>, IEquatable<IDataInfo>
/// The package this information belongs to.
/// </summary>
ContentPackage OwnerPackage { get; }
/// <summary>
/// Used in place of the package data when the OwnerPackage is missing.
/// </summary>
string FallbackPackageName { get; }
bool IEqualityComparer<IDataInfo>.Equals(IDataInfo x, IDataInfo y)
{

View File

@@ -2,13 +2,11 @@
namespace Barotrauma.LuaCs.Data;
public partial interface IModConfigInfo : IResourceCultureInfo, IAssembliesResourcesInfo,
public partial interface IModConfigInfo : IAssembliesResourcesInfo,
ILocalizationsResourcesInfo, ILuaScriptsResourcesInfo, IConfigsResourcesInfo,
IConfigProfilesResourcesInfo
{
// package info
ContentPackage Package { get; }
string PackageName { get; }
// configuration
TargetRunMode RunModes { get; }
}

View File

@@ -0,0 +1,66 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using Barotrauma;
using Barotrauma.LuaCs.Data;
namespace Barotrauma.LuaCs.Data;
public interface IPackageDependency : IDataInfo, IEquatable<IPackageDependency>
{
public IPackageInfo Dependency { get; }
bool IEquatable<IPackageDependency>.Equals(IPackageDependency other)
{
return other is not null && Dependency.Equals(other.Dependency);
}
}
public interface IPackageInfo : IEquatable<IPackageInfo>
{
/// <summary>
/// Name of the content package.
/// </summary>
public string Name { get; }
/// <summary>
/// Steam ID of the package.
/// </summary>
public ulong SteamWorkshopId { get; }
/// <summary>
/// The Guid for the runtime instance of the package.
/// </summary>
public uint Id { get; }
/// <summary>
/// Gets the reference to the best-match target ContentPackage that meets the requirement.
/// </summary>
/// <returns>The <see cref="ContentPackage"/> reference, or null if none was found.</returns>
public ContentPackage GetPackage();
/// <summary>
/// Tries to retrieve the current best <see cref="ContentPackage"/> and returns true if none was found.
/// </summary>
public bool IsMissing => GetPackage() is null;
bool IEquatable<IPackageInfo>.Equals(IPackageInfo other)
{
if (other is null)
return false;
if (ReferenceEquals(other, this))
return true;
if (!this.IsMissing && !other.IsMissing && ReferenceEquals(other.GetPackage, this.GetPackage))
return true;
if (this.SteamWorkshopId != 0 && other.SteamWorkshopId == this.SteamWorkshopId)
return true;
return this.Name == other.Name;
}
}
public interface IPackageDependenciesInfo
{
/// <summary>
/// List of required packages.
/// </summary>
ImmutableArray<IPackageDependency> Dependencies { get; }
}

View File

@@ -1,40 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
namespace Barotrauma.LuaCs.Data;
public interface IPackageDependencyInfo : IPackageInfo,
IEqualityComparer<IPackageDependencyInfo>
{
/// <summary>
/// Root folder of the content package.
/// </summary>
public string FolderPath { get; }
/// <summary>
/// Steam ID of the package.
/// </summary>
public ulong SteamWorkshopId { get; }
/// <summary>
/// The dependency package, if found in the ALL Packages List.
/// </summary>
public ContentPackage DependencyPackage { get; }
/// <summary>
/// This dependency was not found.
/// </summary>
public bool IsMissing { get; }
/// <summary>
/// Whether the package is installed from the workshop. False means installation is from local mods.
/// </summary>
public bool IsWorkshopInstallation { get; }
}
public interface IPackageDependenciesInfo
{
/// <summary>
/// List of required packages.
/// </summary>
ImmutableArray<IPackageDependencyInfo> Dependencies { get; }
}

View File

@@ -4,14 +4,22 @@ using System.Globalization;
namespace Barotrauma.LuaCs.Data;
public interface IConfigResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IPackageInfo { }
public interface IConfigProfileResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IPackageInfo { }
public interface ILocalizationResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IPackageInfo { }
public interface IConfigResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IDataInfo { }
public interface IConfigProfileResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IDataInfo { }
public interface ILocalizationResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IDataInfo { }
/// <summary>
/// Represents loadable Lua files.
/// </summary>
public interface ILuaScriptResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IPackageInfo { }
public interface IAssemblyResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IPackageInfo
public interface ILuaScriptResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IDataInfo
{
/// <summary>
/// Should this script be run automatically.
/// </summary>
public bool IsAutorun { get; }
}
public interface IAssemblyResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IDataInfo
{
/// <summary>
/// The friendly name of the assembly. Script files belonging to the same assembly should all have the same name.

View File

@@ -55,6 +55,10 @@ internal interface IEventEnabledPackageListChanged : IEvent<IEventEnabledPackage
void OnEnabledPackageListChanged(CorePackage package, IEnumerable<RegularPackage> regularPackages);
}
internal interface IEventReloadAllPackages : IEvent<IEventReloadAllPackages>
{
void OnReloadAllPackages();
}
#endregion

View File

@@ -59,8 +59,8 @@ namespace Barotrauma
{
public object GetComponentString(string component)
{
Type type = LuaUserData.GetType("Barotrauma.Items.Components." + component);
Type type = GameMain.LuaCs.PluginManagementService.GetType("Barotrauma.Items.Components." + component);
if (type == null)
{
return null;

View File

@@ -1,24 +1,15 @@
using MoonSharp.Interpreter;
using MoonSharp.Interpreter.Interop;
/*
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using MoonSharp.Interpreter;
using MoonSharp.Interpreter.Interop;
namespace Barotrauma
{
partial class LuaUserData
{
public static ReadOnlyDictionary<string, IUserDataDescriptor> Descriptors => new ReadOnlyDictionary<string, IUserDataDescriptor>(descriptors);
private static ConcurrentDictionary<string, IUserDataDescriptor> descriptors = new ConcurrentDictionary<string, IUserDataDescriptor>();
public IUserDataDescriptor this[string index]
{
get => Descriptors.GetValueOrDefault(index);
}
public static Type GetType(string typeName) => LuaCsSetup.GetType(typeName);
public static IUserDataDescriptor RegisterType(string typeName)
@@ -30,15 +21,7 @@ namespace Barotrauma
throw new ScriptRuntimeException($"tried to register a type that doesn't exist: {typeName}.");
}
var descriptor = UserData.RegisterType(type);
descriptors.TryAdd(typeName, descriptor);
return descriptor;
}
public static IUserDataDescriptor RegisterTypeBarotrauma(string typeName)
{
return RegisterType($"Barotrauma.{typeName}");
return UserData.RegisterType(type);
}
public static void RegisterExtensionType(string typeName)
@@ -120,9 +103,7 @@ namespace Barotrauma
MethodInfo method = typeof(UserData).GetMethod(nameof(UserData.CreateStatic), 1, new Type[0]);
MethodInfo generic = method.MakeGenericMethod(type);
var result = generic.Invoke(null, null);
return result;
return generic.Invoke(null, null);
}
public static object CreateEnumTable(string typeName)
@@ -379,13 +360,6 @@ namespace Barotrauma
descriptor ??= new StandardUserDataDescriptor(desiredType, InteropAccessMode.Default);
return CreateUserDataFromDescriptor(scriptObject, descriptor);
}
public static void AddCallMetaTable(object userdata) { }
public static void Clear()
{
descriptors.Clear();
}
}
}
*/

View File

@@ -1,4 +1,5 @@
using System;
/*
using System;
using MoonSharp.Interpreter;
using Microsoft.Xna.Framework;
using FarseerPhysics.Dynamics;
@@ -391,3 +392,4 @@ namespace Barotrauma
}
}
}
*/

View File

@@ -840,7 +840,7 @@ namespace Barotrauma
private static MethodBase ResolveMethod(string className, string methodName, string[] parameters)
{
var classType = LuaUserData.GetType(className);
var classType = GameMain.LuaCs.PluginManagementService.GetType(className);
if (classType == null) throw new ScriptRuntimeException($"invalid class name '{className}'");
const BindingFlags BINDING_FLAGS = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
@@ -855,7 +855,7 @@ namespace Barotrauma
for (int i = 0; i < parameters.Length; i++)
{
Type type = LuaUserData.GetType(parameters[i]);
Type type = GameMain.LuaCs.PluginManagementService.GetType(parameters[i]);
if (type == null)
{
throw new ScriptRuntimeException($"invalid parameter type '{parameters[i]}'");

View File

@@ -131,20 +131,24 @@ namespace Barotrauma
}
}
throw new NotImplementedException();
string responseBody = await response.Content.ReadAsStringAsync();
GameMain.LuaCs.Timer.Wait((object[] par) =>
/*GameMain.LuaCs.Timer.Wait((object[] par) =>
{
callback(responseBody, (int)response.StatusCode, response.Headers);
}, 0);
}, 0);*/
}
catch (HttpRequestException e)
{
GameMain.LuaCs.Timer.Wait((object[] par) => { callback(e.Message, e.StatusCode, null); }, 0);
throw new NotImplementedException();
//GameMain.LuaCs.Timer.Wait((object[] par) => { callback(e.Message, e.StatusCode, null); }, 0);
}
catch (Exception e)
{
GameMain.LuaCs.Timer.Wait((object[] par) => { callback(e.Message, null, null); }, 0);
throw new NotImplementedException();
//GameMain.LuaCs.Timer.Wait((object[] par) => { callback(e.Message, null, null); }, 0);
}
}

View File

@@ -77,5 +77,6 @@ namespace Barotrauma
}
public void Dispose() { }
public bool IsDisposed { get; }
}
}

View File

@@ -22,7 +22,7 @@ namespace Barotrauma
internal delegate void LuaCsErrorHandler(Exception ex, LuaCsMessageOrigin origin);
internal delegate void LuaCsExceptionHandler(Exception ex, LuaCsMessageOrigin origin);
partial class LuaCsSetup : IDisposable, IEventScreenSelected, IEventAllPackageListChanged, IEventEnabledPackageListChanged
partial class LuaCsSetup : IDisposable, IEventScreenSelected, IEventAllPackageListChanged, IEventEnabledPackageListChanged, IEventReloadAllPackages
{
public LuaCsSetup()
{
@@ -35,9 +35,11 @@ namespace Barotrauma
return;
// == end
// == helpers
// == sub processes
void RegisterServices()
{
_servicesProvider.RegisterServiceType<IPackageListRetrievalService, PackageListRetrievalService>(ServiceLifetime.Transient);
_servicesProvider.RegisterServiceType<IPackageInfoLookupService, ContentPackageInfoLookup>(ServiceLifetime.Singleton);
_servicesProvider.RegisterServiceType<ILoggerService, LoggerService>(ServiceLifetime.Singleton);
_servicesProvider.RegisterServiceType<PerformanceCounterService, PerformanceCounterService>(ServiceLifetime.Singleton);
_servicesProvider.RegisterServiceType<IStorageService, StorageService>(ServiceLifetime.Transient);
@@ -55,13 +57,27 @@ namespace Barotrauma
// TODO: INetworkingService
// TODO: [Resource Converter/Parser Services]
// IResourceInfo wrappers and mutators.
_servicesProvider.RegisterServiceType<IProcessorService<IReadOnlyList<IAssemblyResourceInfo>, IAssembliesResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient);
_servicesProvider.RegisterServiceType<IProcessorService<IReadOnlyList<IConfigResourceInfo>, IConfigsResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient);
_servicesProvider.RegisterServiceType<IProcessorService<IReadOnlyList<IConfigProfileResourceInfo>, IConfigProfilesResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient);
_servicesProvider.RegisterServiceType<IProcessorService<IReadOnlyList<ILocalizationResourceInfo>, ILocalizationsResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient);
_servicesProvider.RegisterServiceType<IProcessorService<IReadOnlyList<ILuaScriptResourceInfo>, ILuaScriptsResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient);
_servicesProvider.RegisterServiceType<IConverterService<ContentPackage, IModConfigInfo>, ModConfigService>(ServiceLifetime.Transient);
_servicesProvider.RegisterServiceType<IConverterServiceAsync<ContentPackage, IModConfigInfo>, ModConfigService>(ServiceLifetime.Transient);
_servicesProvider.Compile();
}
// Validates LuaCs assets in /Content are valid and ready to use.
void ValidateLuaCsContent()
{
throw new NotImplementedException();
// check if /Content/Lua/ModConfig.xml exists
// if not, try to copy it from the Workshop Mod (ie. installation mode)
// if that fails, throw an error and exit.
}
}
@@ -70,6 +86,7 @@ namespace Barotrauma
EventService.Subscribe<IEventScreenSelected>(this); // game state hook in
EventService.Subscribe<IEventAllPackageListChanged>(this);
EventService.Subscribe<IEventEnabledPackageListChanged>(this);
EventService.Subscribe<IEventReloadAllPackages>(this);
}
#region CONST_DEF
@@ -157,6 +174,11 @@ namespace Barotrauma
/// Intended for development use, or when packages are expected to change outside of External Updates (ie. Steam Workshop).
/// </summary>
public IConfigEntry<bool> ReloadPackagesOnLobbyStart { get; private set; }
/// <summary>
/// TODO: @evilfactory@users.noreply.github.com
/// </summary>
public IConfigEntry<bool> RestrictMessageSize { get; private set; }
/**
* == Ops Vars
@@ -301,6 +323,22 @@ namespace Barotrauma
{
UpdateLoadedPackagesList();
}
public void OnReloadAllPackages()
{
if (CurrentRunState <= RunState.Unloaded)
return;
var state = CurrentRunState;
SetRunState(RunState.Unloaded);
SetRunState(CurrentRunState);
}
public void ForceRunState(RunState newState)
{
if (CurrentRunState == newState)
return;
SetRunState(newState);
}
private void UpdateLoadedPackagesList()
{
@@ -489,6 +527,11 @@ namespace Barotrauma
?? throw new NullReferenceException($"{nameof(LuaForBarotraumaSteamId)} cannot be loaded.");
CsForBarotraumaSteamId = ConfigService.GetConfig<IConfigEntry<ulong>>(ContentPackageManager.VanillaCorePackage, "CsForBarotraumaSteamId")
?? throw new NullReferenceException($"{nameof(CsForBarotraumaSteamId)} cannot be loaded.");
RestrictMessageSize = ConfigService.GetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "RestrictMessageSize")
?? throw new NullReferenceException($"{nameof(RestrictMessageSize)} cannot be loaded.");
ReloadPackagesOnLobbyStart = ConfigService.GetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "ReloadPackagesOnLobbyStart")
?? throw new NullReferenceException($"{nameof(ReloadPackagesOnLobbyStart)} cannot be loaded.");
}
void DisposeLuaCsConfig()
@@ -499,6 +542,8 @@ namespace Barotrauma
HideUserNamesInLogs = null;
LuaForBarotraumaSteamId = null;
CsForBarotraumaSteamId = null;
RestrictMessageSize = null;
ReloadPackagesOnLobbyStart = null;
}
async Task LoadStaticAssetsAsync(IReadOnlyList<ContentPackage> packages)
@@ -557,7 +602,7 @@ namespace Barotrauma
{
var res = await PackageManagementService.GetStylesInfosAsync(packages);
if (res.IsSuccess)
styleRes = res.Value.StylesResourceInfos;
styleRes = res.Value.Styles;
if (res.Errors.Any())
ThreadPool.QueueUserWorkItem(state => Logger.LogResults((FluentResults.Result)state),
res.ToResult());
@@ -726,6 +771,8 @@ namespace Barotrauma
_runState = RunState.Configuration;
}
}
}
/// <summary>

View File

@@ -8,6 +8,7 @@ using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Serialization;
using Barotrauma;
using Barotrauma.Items.Components;
@@ -17,6 +18,7 @@ using Microsoft.CodeAnalysis;
using Microsoft.Xna.Framework;
using OneOf;
using Platform = Barotrauma.LuaCs.Data.Platform;
// ReSharper disable ConvertClosureToMethodGroup
// This file is cursed, we put everything in it, and I'm not sorry about it.
namespace Barotrauma.LuaCs
@@ -435,7 +437,7 @@ namespace Barotrauma.LuaCs
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool GetBool(ref int var) => Interlocked.CompareExchange(ref var, 1, 1) > 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetBool(ref int var, bool value)
{
@@ -455,7 +457,7 @@ namespace Barotrauma.LuaCs
/// <param name="var"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool CheckClearAndSetBool(ref int var)
public static bool CheckIfClearAndSetBool(ref int var)
{
return Interlocked.CompareExchange(ref var, 1, 0) < 1;
}
@@ -466,7 +468,7 @@ namespace Barotrauma.LuaCs
/// <param name="var"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool CheckSetAndClearBool(ref int var)
public static bool CheckIfSetAndClearBool(ref int var)
{
return Interlocked.CompareExchange(ref var, 0, 1) > 0;
}
@@ -521,8 +523,43 @@ namespace Barotrauma.LuaCs
}
}
}
public static class CollectionExtensions
{
/// <summary>
/// Executes a series of asynchronous tasks with limited parallelism to maintain execution efficiency.
/// </summary>
/// <param name="source"></param>
/// <param name="funcBody"></param>
/// <param name="maxDegreeOfParallelism"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static Task ParallelForEachAsync<T>(this IEnumerable<T> source, Func<T, Task> funcBody, int maxDegreeOfParallelism = 4)
{
async Task AwaitParallelLimit(IEnumerator<T> partition)
{
using (partition)
{
while (partition.MoveNext())
{
await Task.Yield(); // prevents a sync/hot thread hangup
await funcBody(partition.Current);
}
}
}
return Task.WhenAll(
Partitioner
.Create(source)
.GetPartitions(maxDegreeOfParallelism)
.AsParallel()
.Select(p => AwaitParallelLimit(p)));
}
}
}
#region ExceptionData
namespace FluentResults.LuaCs

View File

@@ -1,6 +1,6 @@
using System;
namespace Barotrauma.LuaCs.Networking;
namespace Barotrauma.LuaCs.Services;
public partial interface INetCallback
{

View File

@@ -1,10 +1,10 @@
using System;
using Barotrauma.LuaCs.Configuration;
using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs.Networking;
using Barotrauma.LuaCs.Services;
using Barotrauma.Networking;
namespace Barotrauma.LuaCs.Networking;
namespace Barotrauma.LuaCs.Services;
public interface INetVar : IVarId
{

View File

@@ -1,7 +1,7 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
namespace Barotrauma.LuaCs.Networking;
namespace Barotrauma.LuaCs.Services;
#region Wrapper-IWriteMessage

View File

@@ -0,0 +1,6 @@
namespace Barotrauma.LuaCs.Services;
public class ConfigService : IConfigService
{
}

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -0,0 +1,6 @@
namespace Barotrauma.LuaCs.Services;
public interface LocalizationService
{
}

View File

@@ -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)

View File

@@ -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
{

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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; }

View File

@@ -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();
}

View File

@@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Barotrauma.LuaCs.Services;
public interface IPackageListRetrievalService : IService
{
IEnumerable<ContentPackage> GetEnabledContentPackages();
IEnumerable<ContentPackage> GetAllContentPackages();
}

View File

@@ -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);

View File

@@ -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; }

View File

@@ -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);

View File

@@ -19,7 +19,7 @@ using MonoMod.Utils;
namespace Barotrauma;
public sealed class CsPackageManager : IDisposable
/*public sealed class CsPackageManager : IDisposable
{
#region PRIVATE_FUNCDATA
@@ -632,8 +632,6 @@ public sealed class CsPackageManager : IDisposable
bool ShouldRunPackage(ContentPackage package, RunConfig config)
{
throw new NotImplementedException();
/*return (!_luaCsSetup.Config.TreatForcedModsAsNormal && config.IsForced())
|| (ContentPackageManager.EnabledPackages.All.Contains(package) && config.IsForcedOrStandard());*/
}
void UpdatePackagesToDisable(ref HashSet<ContentPackage> set,
@@ -1098,3 +1096,4 @@ public sealed class CsPackageManager : IDisposable
#endregion
}
*/

View File

@@ -1,4 +1,5 @@
using System;
/*
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
@@ -339,3 +340,4 @@ public class MemoryFileAssemblyContextLoader : AssemblyLoadContext
this.IsDisposed = true;
}
}
*/

View File

@@ -73,7 +73,8 @@ namespace TestProject.LuaCs
LuaCsHook.HookMethodType.After => "Hook.HookMethodType.After",
_ => throw new NotImplementedException(),
});
return luaCs.Lua.DoString($"return Hook.Patch({string.Join(", ", args)})");
throw new NotImplementedException();
//return luaCs.Lua.DoString($"return Hook.Patch({string.Join(", ", args)})");
}
private static DynValue DoHookRemovePatch(
@@ -91,7 +92,8 @@ namespace TestProject.LuaCs
LuaCsHook.HookMethodType.After => "Hook.HookMethodType.After",
_ => throw new NotImplementedException(),
});
return luaCs.Lua.DoString($"return Hook.RemovePatch({string.Join(", ", args)})");
throw new NotImplementedException();
//return luaCs.Lua.DoString($"return Hook.RemovePatch({string.Join(", ", args)})");
}
public static PatchHandle AddPrefix<T>(this LuaCsSetup luaCs, string body, string methodName, string[]? parameters = null, string? patchId = null)

View File

@@ -1,4 +1,5 @@
extern alias Client;
/*
extern alias Client;
using Client::Barotrauma;
using Microsoft.Xna.Framework;
@@ -7,6 +8,8 @@ using System;
using Xunit;
using Xunit.Abstractions;
// TODO: Rewrite all of this.
namespace TestProject.LuaCs
{
[Collection("LuaCs")]
@@ -16,6 +19,7 @@ namespace TestProject.LuaCs
public HookPatchTests(LuaCsFixture luaCsFixture, ITestOutputHelper output)
{
// XXX: we can't have multiple instances of LuaCs patching the
// same methods, otherwise we get script ownership exceptions.
luaCs = luaCsFixture.LuaCs;
@@ -36,10 +40,12 @@ namespace TestProject.LuaCs
UserData.RegisterType<PatchTargetAmbiguous>();
UserData.RegisterType<PatchTargetConstructor>();
UserData.RegisterType<PatchTargetNumbers>();
luaCs.Initialize();
luaCs.Lua.Globals["TestValueType"] = UserData.CreateStatic<TestValueType>();
luaCs.Lua.Globals["InterfaceImplementingType"] = UserData.CreateStatic<InterfaceImplementingType>();
luaCs.ForceRunState(RunState.Running);
throw new NotImplementedException();
//luaCs.Initialize();
//luaCs.Lua.Globals["TestValueType"] = UserData.CreateStatic<TestValueType>();
//luaCs.Lua.Globals["InterfaceImplementingType"] = UserData.CreateStatic<InterfaceImplementingType>();
}
private class PatchTargetSimple
@@ -664,3 +670,4 @@ namespace TestProject.LuaCs
}
}
}
*/

View File

@@ -1,9 +1,12 @@
extern alias Client;
/*
extern alias Client;
using Client::Barotrauma;
using System;
using System.Runtime.ExceptionServices;
// TODO: Rewrite all of this.
namespace TestProject.LuaCs
{
/// <summary>
@@ -31,3 +34,4 @@ namespace TestProject.LuaCs
void IDisposable.Dispose() => LuaCs.Stop();
}
}
*/