From 52d920d969c54be9a190777f36a734b35cbe541d Mon Sep 17 00:00:00 2001 From: MapleWheels Date: Wed, 26 Feb 2025 12:48:34 -0500 Subject: [PATCH] [Milestone] PackageManagementService completed. - ContentPackageInfoLookup Service completed. - Implemented ModConfigService.cs - Implemented some of the resource processors. --- .../ClientSource/DebugConsole.cs | 13 +- .../BarotraumaClient/ClientSource/GameMain.cs | 13 + .../LuaCs/Configuration/DisplayableData.cs | 18 - .../IConfigDisplayDefinitions.cs | 6 - .../LuaCs/Configuration/IDisplayables.cs | 63 -- .../LuaCs/Data/DataInterfaceDefinitions.cs | 6 +- .../ClientSource/LuaCs/Data/IConfigInfo.cs | 16 +- .../LuaCs/Data/IResourceInfoDeclarations.cs | 4 +- .../ClientSource/LuaCs/LuaCsInstaller.cs | 4 + .../LuaCs/Services/IConfigService.cs | 2 +- .../LuaCs/Services/INetworkingService.cs | 8 + .../LuaCs/Services/IStylesService.cs | 2 +- .../LuaCs/Services/NetworkingService.cs | 9 +- .../Services/PackageManagementService.cs | 81 +++ .../Processing/IClientParserDefinitions.cs | 9 - .../Services/Processing/ModConfigService.cs | 39 ++ .../LuaCs/Services/StylesService.cs | 5 - .../ClientSource/Networking/GameClient.cs | 2 - .../Screens/MainMenuScreen/MainMenuScreen.cs | 12 +- .../Characters/CharacterNetworking.cs | 2 +- .../ServerSource/DebugConsole.cs | 10 +- .../BarotraumaServer/ServerSource/GameMain.cs | 15 +- .../ServerSource/LuaCs/LuaCsInstaller.cs | 6 +- .../LuaCs/Services/INetworkingService.cs | 9 + .../LuaCs/Services/NetworkingService.cs | 5 +- .../Services/PackageManagementService.cs | 26 + .../Services/Processing/ModConfigService.cs | 33 + .../ServerSource/Networking/GameServer.cs | 3 +- .../ContentPackageManager.cs | 6 +- .../LuaCs/AsyncReaderWriterLock.cs | 76 ++ .../LuaCs/Configuration/IConfigEntry.cs | 2 +- .../LuaCs/Configuration/IConfigList.cs | 2 +- .../LuaCs/Data/DataInterfaceDefinitions.cs | 209 +++--- .../LuaCs/Data/EPlatformsTargets.cs | 11 +- .../LuaCs/Data/IBaseInfoDefinitions.cs | 5 - .../SharedSource/LuaCs/Data/IConfigInfo.cs | 36 +- .../SharedSource/LuaCs/Data/IDataInfo.cs | 4 - .../SharedSource/LuaCs/Data/IModConfigInfo.cs | 4 +- .../LuaCs/Data/IPackageDependency.cs | 66 ++ .../LuaCs/Data/IPackageDependencyInfo.cs | 40 -- .../LuaCs/Data/IResourceInfoDeclarations.cs | 18 +- .../SharedSource/LuaCs/IEvents.cs | 4 + .../LuaCs/Lua/LuaBarotraumaAdditions.cs | 4 +- .../LuaCs/Lua/LuaClasses/LuaUserData.cs | 38 +- .../SharedSource/LuaCs/Lua/LuaConverters.cs | 4 +- .../SharedSource/LuaCs/LuaCsHook.cs | 4 +- .../SharedSource/LuaCs/LuaCsNetworking.cs | 12 +- .../LuaCs/LuaCsPerformanceCounter.cs | 1 + .../SharedSource/LuaCs/LuaCsSetup.cs | 55 +- .../SharedSource/LuaCs/ModUtils.cs | 43 +- .../LuaCs/Networking/INetCallback.cs | 2 +- .../SharedSource/LuaCs/Networking/INetVar.cs | 4 +- .../LuaCs/Networking/NetInterfaceCompat.cs | 2 +- .../LuaCs/Services/ConfigService.cs | 6 + .../Services/ContentPackageInfoLookup.cs | 383 ++++++++++ .../LuaCs/Services/EventService.cs | 10 - .../LuaCs/Services/LocalizationService.cs | 6 + .../SharedSource/LuaCs/Services/LuaGame.cs | 5 +- .../LuaCs/Services/NetworkingService.cs | 2 +- .../Services/PackageListRetrievalService.cs | 30 + .../Services/PackageManagementService.cs | 380 +++++++++- .../IConverterServiceDefinitions.cs | 21 +- .../Processing/IModConfigCreatorService.cs | 9 - .../Services/Processing/ModConfigService.cs | 661 ++++++++++++++++++ .../Processing/ResourceInfoProcessors.cs | 53 ++ .../LuaCs/Services/StorageService.cs | 32 +- .../Services/_Interfaces/IConfigService.cs | 2 +- .../_Interfaces/INetworkingService.cs | 4 +- .../_Interfaces/IPackageInfoLookupService.cs | 15 + .../IPackageListRetrievalService.cs | 9 + .../_Interfaces/IPackageManagementService.cs | 10 +- .../LuaCs/Services/_Interfaces/IService.cs | 1 + .../Services/_Interfaces/IStorageService.cs | 3 + .../LuaCs/_Plugins/CsPackageManager.cs | 5 +- .../MemoryFileAssemblyContextLoader.cs | 4 +- .../BarotraumaTest/LuaCs/HookPatchHelpers.cs | 6 +- .../BarotraumaTest/LuaCs/HookPatchTests.cs | 17 +- .../BarotraumaTest/LuaCs/LuaCsFixture.cs | 6 +- 78 files changed, 2331 insertions(+), 422 deletions(-) delete mode 100644 Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/DisplayableData.cs delete mode 100644 Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/IConfigDisplayDefinitions.cs delete mode 100644 Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/IDisplayables.cs create mode 100644 Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/INetworkingService.cs create mode 100644 Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/PackageManagementService.cs delete mode 100644 Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/Processing/IClientParserDefinitions.cs create mode 100644 Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/Processing/ModConfigService.cs create mode 100644 Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/INetworkingService.cs create mode 100644 Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/PackageManagementService.cs create mode 100644 Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/Processing/ModConfigService.cs create mode 100644 Barotrauma/BarotraumaShared/SharedSource/LuaCs/AsyncReaderWriterLock.cs create mode 100644 Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IPackageDependency.cs delete mode 100644 Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IPackageDependencyInfo.cs create mode 100644 Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/ConfigService.cs create mode 100644 Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/ContentPackageInfoLookup.cs create mode 100644 Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/LocalizationService.cs create mode 100644 Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/PackageListRetrievalService.cs delete mode 100644 Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/IModConfigCreatorService.cs create mode 100644 Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/ModConfigService.cs create mode 100644 Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/ResourceInfoProcessors.cs create mode 100644 Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IPackageInfoLookupService.cs create mode 100644 Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IPackageListRetrievalService.cs diff --git a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs index 33f058b9f..fbd680b86 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs @@ -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(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); })); } diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs index 1126a166a..92b024ed0 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs @@ -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(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/DisplayableData.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/DisplayableData.cs deleted file mode 100644 index 59df05404..000000000 --- a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/DisplayableData.cs +++ /dev/null @@ -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; } -} diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/IConfigDisplayDefinitions.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/IConfigDisplayDefinitions.cs deleted file mode 100644 index 3c5fbc5f7..000000000 --- a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/IConfigDisplayDefinitions.cs +++ /dev/null @@ -1,6 +0,0 @@ -using Barotrauma.LuaCs.Configuration; -using Barotrauma.LuaCs.Data; - -namespace Barotrauma.LuaCs.Configuration; - -public partial interface IConfigBase : IDisplayableData, IDisplayableInitialize { } diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/IDisplayables.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/IDisplayables.cs deleted file mode 100644 index 0422fb91c..000000000 --- a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Configuration/IDisplayables.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Numerics; -using Barotrauma.LuaCs.Data; -using Microsoft.Xna.Framework; - -namespace Barotrauma.LuaCs.Configuration; - -/// -/// Contains the Display Data for use with Menus. -/// -public interface IDisplayableData : IDataInfo -{ - /// - /// The name to display in GUIs and Menus. - /// - string DisplayName { get; } - /// - /// The mod name to display in GUIs and Menus. - /// - string DisplayModName { get; } - /// - /// Category this instance falls under. Used by menus when filtering by category. - /// - string DisplayCategory { get; } - /// - /// The tooltip shown on hover. - /// - string Tooltip { get; } - /// - /// The fully qualified filepath to the image icon for this config. - /// - string ImageIcon { get; } - /// - /// Required if ImageIcon is set. X,Y resolution of the image. - /// - Point IconResolution { get; } - /// - /// Whether to show the entry in the menu when not loaded. - /// - bool ShowWhenNotLoaded { get; } - /// - /// What does this setting do? - /// - 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; - }*/ -} diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Data/DataInterfaceDefinitions.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Data/DataInterfaceDefinitions.cs index 60e4ec227..570f48f9b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Data/DataInterfaceDefinitions.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Data/DataInterfaceDefinitions.cs @@ -5,7 +5,7 @@ namespace Barotrauma.LuaCs.Data; public partial record ModConfigInfo : IModConfigInfo { - public ImmutableArray StylesResourceInfos { get; init; } + public ImmutableArray Styles { get; init; } } public record StylesResourceInfo : IStylesResourceInfo @@ -18,6 +18,6 @@ public record StylesResourceInfo : IStylesResourceInfo public ImmutableArray SupportedCultures { get; init; } public string InternalName { get; init; } public ContentPackage OwnerPackage { get; init; } - public string FallbackPackageName { get; } - public ImmutableArray Dependencies { get; init; } + public string FallbackPackageName { get; init; } + public ImmutableArray Dependencies { get; init; } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Data/IConfigInfo.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Data/IConfigInfo.cs index 45d563fa7..576d18118 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Data/IConfigInfo.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Data/IConfigInfo.cs @@ -2,4 +2,18 @@ using Barotrauma.LuaCs.Configuration; namespace Barotrauma.LuaCs.Data; -public partial interface IConfigInfo : IDisplayableData { } +public partial interface IConfigInfo +{ + /// + /// Should this config be displayed in end-user menus. + /// + bool ShowInMenus { get; } + /// + /// User-friendly on-hover tooltip text or Localization Token. + /// + string Tooltip { get; } + /// + /// Icon for display in menus, if available. + /// + string ImageIconPath { get; } +} diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Data/IResourceInfoDeclarations.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Data/IResourceInfoDeclarations.cs index 8624189ed..de47ef225 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Data/IResourceInfoDeclarations.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Data/IResourceInfoDeclarations.cs @@ -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 { /// /// Collection of loadable styles data. /// - ImmutableArray StylesResourceInfos { get; } + ImmutableArray Styles { get; } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/LuaCsInstaller.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/LuaCsInstaller.cs index c18660d89..490ce38d1 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/LuaCsInstaller.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/LuaCsInstaller.cs @@ -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; }; + */ } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/IConfigService.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/IConfigService.cs index 6d817456c..89b2316a3 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/IConfigService.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/IConfigService.cs @@ -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; diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/INetworkingService.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/INetworkingService.cs new file mode 100644 index 000000000..d79efba19 --- /dev/null +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/INetworkingService.cs @@ -0,0 +1,8 @@ +using Barotrauma.Networking; + +namespace Barotrauma.LuaCs.Services; + +internal partial interface INetworkingService : IReusableService +{ + void NetMessageReceived(IReadMessage message, ServerPacketHeader header); +} diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/IStylesService.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/IStylesService.cs index 6b07dbc21..26c8b2404 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/IStylesService.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/IStylesService.cs @@ -6,7 +6,7 @@ namespace Barotrauma.LuaCs.Services; /// /// Loads XML Style assets from the given content package. /// -public interface IStylesService : IReusableService +public interface IStylesService : IService { /// /// Tries to load the styles file for the given and path into a new instance. diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/NetworkingService.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/NetworkingService.cs index 8d9c0f0c0..597709cc5 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/NetworkingService.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/NetworkingService.cs @@ -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> receiveQueue = new Dictionary>(); @@ -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(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/PackageManagementService.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/PackageManagementService.cs new file mode 100644 index 000000000..7b722751a --- /dev/null +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/PackageManagementService.cs @@ -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 modConfigParserService, + IProcessorService, IAssembliesResourcesInfo> assemblyInfoConverter, + IProcessorService, IConfigsResourcesInfo> configsInfoConverter, + IProcessorService, IConfigProfilesResourcesInfo> configProfilesConverter, + IProcessorService, ILocalizationsResourcesInfo> localizationsConverter, + IProcessorService, ILuaScriptsResourcesInfo> luaScriptsConverter, + IPackageInfoLookupService packageInfoLookupService, Func, IStylesResourcesInfo> stylesInfoConverter) + { + _stylesInfoConverter = stylesInfoConverter; + _modConfigParserService = modConfigParserService; + _assemblyInfoConverter = assemblyInfoConverter; + _configsInfoConverter = configsInfoConverter; + _configProfilesConverter = configProfilesConverter; + _localizationsConverter = localizationsConverter; + _luaScriptsConverter = luaScriptsConverter; + _packageInfoLookupService = packageInfoLookupService; + } + + private readonly Func, IStylesResourcesInfo> _stylesInfoConverter; + + public ImmutableArray Styles => _modInfos.IsEmpty ? ImmutableArray.Empty + : _modInfos.SelectMany(kvp => kvp.Value.Styles).ToImmutableArray(); + + public Result 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(_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 GetStylesInfos(IReadOnlyList 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(); + 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> GetStylesInfosAsync(IReadOnlyList packages, bool onlySupportedResources = true) + { + return await Task.Run(() => GetStylesInfos(packages, onlySupportedResources)); + + } +} diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/Processing/IClientParserDefinitions.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/Processing/IClientParserDefinitions.cs deleted file mode 100644 index 8c81c7fb5..000000000 --- a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/Processing/IClientParserDefinitions.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Xml.Linq; -using Barotrauma.LuaCs.Data; - -namespace Barotrauma.LuaCs.Services.Processing; - -#region XmlToResourceParsers -public interface IXmlStylesToResConverterService : IXmlResourceConverterService { } - -#endregion diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/Processing/ModConfigService.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/Processing/ModConfigService.cs new file mode 100644 index 000000000..386a66940 --- /dev/null +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/Processing/ModConfigService.cs @@ -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> 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(new ModConfigInfo() + { + Package = package, + PackageName = package.Name, + Assemblies = asm.Any() ? GetAssemblies(package, asm) : ImmutableArray.Empty, + Localizations = loc.Any() ? GetLocalizations(package, loc) : ImmutableArray.Empty, + Configs = cfg.Any() ? GetConfigs(package, cfg) : ImmutableArray.Empty, + ConfigProfiles = cfg.Any() ? GetConfigProfiles(package, cfg) : ImmutableArray.Empty, + LuaScripts = lua.Any() ? GetLuaScripts(package, lua) : ImmutableArray.Empty, + Styles = stl.Any() ? GetStyles(package, stl) : ImmutableArray.Empty + }); + } + + private ImmutableArray GetStyles(ContentPackage src, IEnumerable elements) + { + throw new NotImplementedException(); + } +} diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/StylesService.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/StylesService.cs index 29cb65429..6e608a5fc 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/StylesService.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/Services/StylesService.cs @@ -116,10 +116,5 @@ public class StylesService : IStylesService GC.SuppressFinalize(this); } - public FluentResults.Result Reset() - { - return UnloadAllStyles(); - } - public bool IsDisposed { get; private set; } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs index 33b4a8aec..5b908beef 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs @@ -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); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen/MainMenuScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen/MainMenuScreen.cs index 31bfd266b..233f6b666 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen/MainMenuScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen/MainMenuScreen.cs @@ -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 = diff --git a/Barotrauma/BarotraumaServer/ServerSource/Characters/CharacterNetworking.cs b/Barotrauma/BarotraumaServer/ServerSource/Characters/CharacterNetworking.cs index 59c91a4a5..ea7816fba 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Characters/CharacterNetworking.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Characters/CharacterNetworking.cs @@ -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) diff --git a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs index b5ba08e21..555d790d3 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs @@ -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(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) => diff --git a/Barotrauma/BarotraumaServer/ServerSource/GameMain.cs b/Barotrauma/BarotraumaServer/ServerSource/GameMain.cs index 18bce7393..66d8ab350 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/GameMain.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/GameMain.cs @@ -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}"); + } } } } diff --git a/Barotrauma/BarotraumaServer/ServerSource/LuaCs/LuaCsInstaller.cs b/Barotrauma/BarotraumaServer/ServerSource/LuaCs/LuaCsInstaller.cs index cd54fe591..a75ada38e 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/LuaCs/LuaCsInstaller.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/LuaCs/LuaCsInstaller.cs @@ -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);*/ } } } diff --git a/Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/INetworkingService.cs b/Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/INetworkingService.cs new file mode 100644 index 000000000..43e7e2f95 --- /dev/null +++ b/Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/INetworkingService.cs @@ -0,0 +1,9 @@ + +using Barotrauma.Networking; + +namespace Barotrauma.LuaCs.Services; + +internal partial interface INetworkingService : IReusableService +{ + void NetMessageReceived(IReadMessage message, ClientPacketHeader header, Client client); +} diff --git a/Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/NetworkingService.cs b/Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/NetworkingService.cs index 5cdcff59a..683558d35 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/NetworkingService.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/NetworkingService.cs @@ -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; diff --git a/Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/PackageManagementService.cs b/Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/PackageManagementService.cs new file mode 100644 index 000000000..435facc4d --- /dev/null +++ b/Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/PackageManagementService.cs @@ -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 modConfigParserService, + IProcessorService, IAssembliesResourcesInfo> assemblyInfoConverter, + IProcessorService, IConfigsResourcesInfo> configsInfoConverter, + IProcessorService, IConfigProfilesResourcesInfo> configProfilesConverter, + IProcessorService, ILocalizationsResourcesInfo> localizationsConverter, + IProcessorService, ILuaScriptsResourcesInfo> luaScriptsConverter, + IPackageInfoLookupService packageInfoLookupService) + { + _modConfigParserService = modConfigParserService; + _assemblyInfoConverter = assemblyInfoConverter; + _configsInfoConverter = configsInfoConverter; + _configProfilesConverter = configProfilesConverter; + _localizationsConverter = localizationsConverter; + _luaScriptsConverter = luaScriptsConverter; + _packageInfoLookupService = packageInfoLookupService; + } +} diff --git a/Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/Processing/ModConfigService.cs b/Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/Processing/ModConfigService.cs new file mode 100644 index 000000000..ee93eccf5 --- /dev/null +++ b/Barotrauma/BarotraumaServer/ServerSource/LuaCs/Services/Processing/ModConfigService.cs @@ -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> 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(new ModConfigInfo() + { + Package = package, + PackageName = package.Name, + Assemblies = asm.Any() ? GetAssemblies(package, asm) : ImmutableArray.Empty, + Localizations = loc.Any() ? GetLocalizations(package, loc) : ImmutableArray.Empty, + Configs = cfg.Any() ? GetConfigs(package, cfg) : ImmutableArray.Empty, + ConfigProfiles = cfg.Any() ? GetConfigProfiles(package, cfg) : ImmutableArray.Empty, + LuaScripts = lua.Any() ? GetLuaScripts(package, lua) : ImmutableArray.Empty + }); + } +} diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs index d3aa1d893..8a734eec6 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs @@ -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) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs index 20f0a7970..3a1f8d0f0 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs @@ -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(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; diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/AsyncReaderWriterLock.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/AsyncReaderWriterLock.cs new file mode 100644 index 000000000..074b04c2e --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/AsyncReaderWriterLock.cs @@ -0,0 +1,76 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Barotrauma.LuaCs; + + +// taken from: +public sealed class AsyncReaderWriterLock : IDisposable +{ + readonly SemaphoreSlim _readSemaphore = new SemaphoreSlim(1, 1); + readonly SemaphoreSlim _writeSemaphore = new SemaphoreSlim(1, 1); + int _readerCount; + + public async Task 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 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(); + } +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Configuration/IConfigEntry.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Configuration/IConfigEntry.cs index 30ba129c6..a39032258 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Configuration/IConfigEntry.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Configuration/IConfigEntry.cs @@ -1,5 +1,5 @@ using System; -using Barotrauma.LuaCs.Networking; +using Barotrauma.LuaCs.Services; namespace Barotrauma.LuaCs.Configuration; diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Configuration/IConfigList.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Configuration/IConfigList.cs index 226ddbe08..0d291f215 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Configuration/IConfigList.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Configuration/IConfigList.cs @@ -1,4 +1,4 @@ -using Barotrauma.LuaCs.Networking; +using Barotrauma.LuaCs.Services; namespace Barotrauma.LuaCs.Configuration; diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/DataInterfaceDefinitions.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/DataInterfaceDefinitions.cs index f5e169005..6dd98c625 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/DataInterfaceDefinitions.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/DataInterfaceDefinitions.cs @@ -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 SupportedCultures { get; init; } public ImmutableArray Assemblies { get; init; } public ImmutableArray Localizations { get; init; } public ImmutableArray LuaScripts { get; init; } @@ -28,10 +26,15 @@ public partial record ModConfigInfo : IModConfigInfo #region DataContracts -public record AssemblyResourceInfo : IAssemblyResourceInfo +public record AssemblyResourcesInfo(ImmutableArray Assemblies) : IAssembliesResourcesInfo; +public record LocalizationResourcesInfo(ImmutableArray Localizations) : ILocalizationsResourcesInfo; +public record LuaScriptsResourcesInfo(ImmutableArray LuaScripts) : ILuaScriptsResourcesInfo; +public record ConfigResourcesInfo(ImmutableArray Configs) : IConfigsResourcesInfo; +public record ConfigProfilesResourcesInfo(ImmutableArray 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 FilePaths { get; init; } public ImmutableArray SupportedCultures { get; init; } - public ImmutableArray Dependencies { get; init; } + public ImmutableArray 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.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; } - /// - /// Returns the hash code unique for the package reference. - /// - /// - /// - /// The hash should only be collision-free when referring to different packages. - 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 _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 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 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 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 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)this).Equals(other); + } +} + + + +public record ConfigResourceInfo : IConfigResourceInfo +{ + public Platform SupportedPlatforms { get; init; } + public Target SupportedTargets { get; init; } + public int LoadPriority { get; init; } + public ImmutableArray FilePaths { get; init; } + public bool Optional { get; init; } + public ImmutableArray SupportedCultures { get; init; } + public ImmutableArray 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 FilePaths { get; init; } + public bool Optional { get; init; } + public ImmutableArray SupportedCultures { get; init; } + public ImmutableArray 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 FilePaths { get; init; } public ImmutableArray SupportedCultures { get; init; } - public ImmutableArray Dependencies { get; init; } + public ImmutableArray 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 FilePaths { get; init; } public ImmutableArray SupportedCultures { get; init; } - public ImmutableArray Dependencies { get; init; } + public ImmutableArray Dependencies { get; init; } public bool Optional { get; init; } public string InternalName { get; init; } - public bool LazyLoad { get; init; } + public bool IsAutorun { get; init; } } #endregion diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/EPlatformsTargets.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/EPlatformsTargets.cs index bdf4188dc..aaf289f8e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/EPlatformsTargets.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/EPlatformsTargets.cs @@ -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 -} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IBaseInfoDefinitions.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IBaseInfoDefinitions.cs index 4b269abe4..cc9387175 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IBaseInfoDefinitions.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IBaseInfoDefinitions.cs @@ -21,11 +21,6 @@ public interface IPlatformInfo Target SupportedTargets { get; } } -/// -/// All info we should have on a package for a given resource. -/// -public interface IPackageInfo : IDataInfo { } - /// /// ResourceInfos contain metadata about a resource. diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IConfigInfo.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IConfigInfo.cs index fa1c487b7..cfa46a629 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IConfigInfo.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IConfigInfo.cs @@ -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. /// - string DataType { get; } + Type DataType { get; } + /// + /// String version of the default value. + /// string DefaultValue { get; } + /// + /// The value the last time this config was saved, if found in /data/. + /// string StoredValue { get; } + /// + /// Custom data storage for other type-specific information needed. IE. Used to store the min, + /// max and step values for the IConfigRangeEntry(T). + /// + string CustomParameters { get; } + /// + /// [Multiplayer]
+ /// What permissions do clients require to change this setting. + ///
ClientPermissions RequiredPermissions { get; } /// - /// Whether a value can be changed at runtime. + /// In what s is this config editable. + ///
+ /// Note: Setting this to value lower than 'Configuration` will render this config read-only. + ///
+ RunState CanEditStates { get; } + /// + /// Network synchronization rules for this config. /// - bool IsReadOnly { get; } NetSync NetSync { get; } + /// + /// User friendly name or Localization Token. + /// + string DisplayName { get; } + /// + /// User friendly description or Localization Token. + /// + string Description { get; } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IDataInfo.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IDataInfo.cs index 34933f7d1..d49be249b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IDataInfo.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IDataInfo.cs @@ -16,10 +16,6 @@ public interface IDataInfo : IEqualityComparer, IEquatable /// The package this information belongs to. ///
ContentPackage OwnerPackage { get; } - /// - /// Used in place of the package data when the OwnerPackage is missing. - /// - string FallbackPackageName { get; } bool IEqualityComparer.Equals(IDataInfo x, IDataInfo y) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IModConfigInfo.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IModConfigInfo.cs index 8a324f870..7d60bf562 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IModConfigInfo.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IModConfigInfo.cs @@ -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; } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IPackageDependency.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IPackageDependency.cs new file mode 100644 index 000000000..a094d122f --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IPackageDependency.cs @@ -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 +{ + public IPackageInfo Dependency { get; } + + bool IEquatable.Equals(IPackageDependency other) + { + return other is not null && Dependency.Equals(other.Dependency); + } +} + +public interface IPackageInfo : IEquatable +{ + /// + /// Name of the content package. + /// + public string Name { get; } + /// + /// Steam ID of the package. + /// + public ulong SteamWorkshopId { get; } + /// + /// The Guid for the runtime instance of the package. + /// + public uint Id { get; } + + /// + /// Gets the reference to the best-match target ContentPackage that meets the requirement. + /// + /// The reference, or null if none was found. + public ContentPackage GetPackage(); + + /// + /// Tries to retrieve the current best and returns true if none was found. + /// + public bool IsMissing => GetPackage() is null; + + bool IEquatable.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 +{ + /// + /// List of required packages. + /// + ImmutableArray Dependencies { get; } +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IPackageDependencyInfo.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IPackageDependencyInfo.cs deleted file mode 100644 index 7da431927..000000000 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IPackageDependencyInfo.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using System.Collections.Immutable; - -namespace Barotrauma.LuaCs.Data; - -public interface IPackageDependencyInfo : IPackageInfo, - IEqualityComparer -{ - /// - /// Root folder of the content package. - /// - public string FolderPath { get; } - /// - /// Steam ID of the package. - /// - public ulong SteamWorkshopId { get; } - /// - /// The dependency package, if found in the ALL Packages List. - /// - public ContentPackage DependencyPackage { get; } - - /// - /// This dependency was not found. - /// - public bool IsMissing { get; } - - /// - /// Whether the package is installed from the workshop. False means installation is from local mods. - /// - public bool IsWorkshopInstallation { get; } -} - -public interface IPackageDependenciesInfo -{ - /// - /// List of required packages. - /// - ImmutableArray Dependencies { get; } -} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IResourceInfoDeclarations.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IResourceInfoDeclarations.cs index 281b20b04..ad6bb45d3 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IResourceInfoDeclarations.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Data/IResourceInfoDeclarations.cs @@ -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 { } + /// /// Represents loadable Lua files. /// -public interface ILuaScriptResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IPackageInfo { } -public interface IAssemblyResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IPackageInfo +public interface ILuaScriptResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IDataInfo +{ + /// + /// Should this script be run automatically. + /// + public bool IsAutorun { get; } +} + +public interface IAssemblyResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IDataInfo { /// /// The friendly name of the assembly. Script files belonging to the same assembly should all have the same name. diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/IEvents.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/IEvents.cs index 1e7bc2620..36d8d9b82 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/IEvents.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/IEvents.cs @@ -55,6 +55,10 @@ internal interface IEventEnabledPackageListChanged : IEvent regularPackages); } +internal interface IEventReloadAllPackages : IEvent +{ + void OnReloadAllPackages(); +} #endregion diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaBarotraumaAdditions.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaBarotraumaAdditions.cs index 357c02bb2..cacdf5bf9 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaBarotraumaAdditions.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaBarotraumaAdditions.cs @@ -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; diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaUserData.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaUserData.cs index 959ee31a9..dd5ae0469 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaUserData.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaUserData.cs @@ -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 Descriptors => new ReadOnlyDictionary(descriptors); - private static ConcurrentDictionary descriptors = new ConcurrentDictionary(); - - 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(); - } } } +*/ diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaConverters.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaConverters.cs index e2c038ebe..c7a0ba351 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaConverters.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaConverters.cs @@ -1,4 +1,5 @@ -using System; +/* +using System; using MoonSharp.Interpreter; using Microsoft.Xna.Framework; using FarseerPhysics.Dynamics; @@ -391,3 +392,4 @@ namespace Barotrauma } } } +*/ diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs index 11de83634..344e091d2 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs @@ -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]}'"); diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsNetworking.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsNetworking.cs index 845f90559..38042d1c1 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsNetworking.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsNetworking.cs @@ -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); } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsPerformanceCounter.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsPerformanceCounter.cs index b59ae4dac..591ed005b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsPerformanceCounter.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsPerformanceCounter.cs @@ -77,5 +77,6 @@ namespace Barotrauma } public void Dispose() { } + public bool IsDisposed { get; } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs index e2dcffd5d..c409ac4d3 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs @@ -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(ServiceLifetime.Transient); + _servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); _servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); _servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); _servicesProvider.RegisterServiceType(ServiceLifetime.Transient); @@ -55,13 +57,27 @@ namespace Barotrauma // TODO: INetworkingService // TODO: [Resource Converter/Parser Services] + // IResourceInfo wrappers and mutators. + _servicesProvider.RegisterServiceType, IAssembliesResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient); + _servicesProvider.RegisterServiceType, IConfigsResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient); + _servicesProvider.RegisterServiceType, IConfigProfilesResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient); + _servicesProvider.RegisterServiceType, ILocalizationsResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient); + _servicesProvider.RegisterServiceType, ILuaScriptsResourcesInfo>, ResourceInfoArrayPacker>(ServiceLifetime.Transient); + + _servicesProvider.RegisterServiceType, ModConfigService>(ServiceLifetime.Transient); + _servicesProvider.RegisterServiceType, 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(this); // game state hook in EventService.Subscribe(this); EventService.Subscribe(this); + EventService.Subscribe(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). /// public IConfigEntry ReloadPackagesOnLobbyStart { get; private set; } + + /// + /// TODO: @evilfactory@users.noreply.github.com + /// + public IConfigEntry 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>(ContentPackageManager.VanillaCorePackage, "CsForBarotraumaSteamId") ?? throw new NullReferenceException($"{nameof(CsForBarotraumaSteamId)} cannot be loaded."); + RestrictMessageSize = ConfigService.GetConfig>(ContentPackageManager.VanillaCorePackage, "RestrictMessageSize") + ?? throw new NullReferenceException($"{nameof(RestrictMessageSize)} cannot be loaded."); + ReloadPackagesOnLobbyStart = ConfigService.GetConfig>(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 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; } } + + } /// diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/ModUtils.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/ModUtils.cs index 981c74bad..ad34c723d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/ModUtils.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/ModUtils.cs @@ -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 /// [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 /// /// [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 /// /// [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 + { + /// + /// Executes a series of asynchronous tasks with limited parallelism to maintain execution efficiency. + /// + /// + /// + /// + /// + /// + public static Task ParallelForEachAsync(this IEnumerable source, Func funcBody, int maxDegreeOfParallelism = 4) + { + async Task AwaitParallelLimit(IEnumerator 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 diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Networking/INetCallback.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Networking/INetCallback.cs index f498da372..9d3c86853 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Networking/INetCallback.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Networking/INetCallback.cs @@ -1,6 +1,6 @@ using System; -namespace Barotrauma.LuaCs.Networking; +namespace Barotrauma.LuaCs.Services; public partial interface INetCallback { diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Networking/INetVar.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Networking/INetVar.cs index 38c962046..a907b18fa 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Networking/INetVar.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Networking/INetVar.cs @@ -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 { diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Networking/NetInterfaceCompat.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Networking/NetInterfaceCompat.cs index 8799208df..161bf3e1c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Networking/NetInterfaceCompat.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Networking/NetInterfaceCompat.cs @@ -1,7 +1,7 @@ using Barotrauma.Networking; using Microsoft.Xna.Framework; -namespace Barotrauma.LuaCs.Networking; +namespace Barotrauma.LuaCs.Services; #region Wrapper-IWriteMessage diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/ConfigService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/ConfigService.cs new file mode 100644 index 000000000..f6962300d --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/ConfigService.cs @@ -0,0 +1,6 @@ +namespace Barotrauma.LuaCs.Services; + +public class ConfigService : IConfigService +{ + +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/ContentPackageInfoLookup.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/ContentPackageInfoLookup.cs new file mode 100644 index 000000000..4a2f8e594 --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/ContentPackageInfoLookup.cs @@ -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; + +/// +/// Provides resolution for dynamically locating the best matching package at the time of consumption. +/// +public sealed class ContentPackageInfoLookup : IPackageInfoLookupService, IEventEnabledPackageListChanged, IEventAllPackageListChanged +{ + #region INTERNAL + + // packageinfo query data + private readonly ConcurrentDictionary, IPackageInfo> _packageInfoMap = new(); + // package query data + private readonly ConcurrentDictionary> _packageIdGroups = new(); + private readonly ConcurrentDictionary> _reversePackageIdGroups = new(); + private readonly HashSet _enabledPackages; + private readonly HashSet _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 enabledPackages, + IReadOnlyList 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 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 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> LookupInternal(OneOf.OneOf 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( + 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.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(); + this._allPackages = new HashSet(); + } + + public void Dispose() + { + IsDisposed = true; + // locks + using var l1 = _packageIdGroupsLock.AcquireWriterLock().GetAwaiter().GetResult(); + using var l2 = _packageSetsLock.AcquireWriterLock().GetAwaiter().GetResult(); + + _eventService.Unsubscribe(this); + _eventService.Unsubscribe(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 regularPackages) + { + ((IService)this).CheckDisposed(); + SyncPackagesLists( + regularPackages.Select(p => (ContentPackage)p).ToImmutableArray().Add(package), + _allPackages.ToImmutableArray()) + .GetAwaiter().GetResult(); + } + + public void OnAllPackageListChanged(IEnumerable corePackages, IEnumerable 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> 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> 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> Lookup(ulong steamWorkshopId) + { + ((IService)this).CheckDisposed(); + if (steamWorkshopId is 0) + return FluentResults.Result.Fail($"SteamId is 0."); + return await LookupInternal(steamWorkshopId); + } + + public async Task> 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(); + } +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/EventService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/EventService.cs index c0cde07dc..c237015a7 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/EventService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/EventService.cs @@ -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; diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/LocalizationService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/LocalizationService.cs new file mode 100644 index 000000000..e3a51d638 --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/LocalizationService.cs @@ -0,0 +1,6 @@ +namespace Barotrauma.LuaCs.Services; + +public interface LocalizationService +{ + +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/LuaGame.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/LuaGame.cs index 3a767a3b0..a97beb235 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/LuaGame.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/LuaGame.cs @@ -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) diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/NetworkingService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/NetworkingService.cs index 971661eec..97d0baed2 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/NetworkingService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/NetworkingService.cs @@ -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 { diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/PackageListRetrievalService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/PackageListRetrievalService.cs new file mode 100644 index 000000000..37e1c2bfc --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/PackageListRetrievalService.cs @@ -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 GetEnabledContentPackages() + { + return ContentPackageManager.EnabledPackages.All; + } + + public IEnumerable GetAllContentPackages() + { + return ContentPackageManager.AllPackages; + } +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/PackageManagementService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/PackageManagementService.cs index c9d31605e..23f646bb6 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/PackageManagementService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/PackageManagementService.cs @@ -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 _modInfos = new(); + // lookup caches + private readonly IPackageInfoLookupService _packageInfoLookupService; + // processors + private readonly IConverterServiceAsync _modConfigParserService; + private readonly IProcessorService, IAssembliesResourcesInfo> _assemblyInfoConverter; + private readonly IProcessorService, IConfigsResourcesInfo> _configsInfoConverter; + private readonly IProcessorService, IConfigProfilesResourcesInfo> _configProfilesConverter; + private readonly IProcessorService, ILocalizationsResourcesInfo> _localizationsConverter; + private readonly IProcessorService, 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 Localizations => _modInfos.IsEmpty ? ImmutableArray.Empty + : _modInfos.SelectMany(kvp => kvp.Value.Localizations).ToImmutableArray(); + public ImmutableArray Configs => _modInfos.IsEmpty ? ImmutableArray.Empty + : _modInfos.SelectMany(kvp => kvp.Value.Configs).ToImmutableArray(); + public ImmutableArray ConfigProfiles => _modInfos.IsEmpty ? ImmutableArray.Empty + : _modInfos.SelectMany(kvp => kvp.Value.ConfigProfiles).ToImmutableArray(); + public ImmutableArray LuaScripts => _modInfos.IsEmpty ? ImmutableArray.Empty + : _modInfos.SelectMany(kvp => kvp.Value.LuaScripts).ToImmutableArray(); + public ImmutableArray Assemblies => _modInfos.IsEmpty ? ImmutableArray.Empty + : _modInfos.SelectMany(kvp => kvp.Value.Assemblies).ToImmutableArray(); + + + public async Task 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> LoadPackagesInfosAsync(IReadOnlyList 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 GetAllLoadedPackages() + { + ((IService)this).CheckDisposed(); + return _modInfos.IsEmpty ? ImmutableArray.Empty + : _modInfos.Select(kvp => kvp.Key).ToImmutableArray(); + } + + public void DisposePackageInfos(ContentPackage package) + { + _modInfos.TryRemove(package, out _); + } + + public void DisposePackagesInfos(IReadOnlyList packages) + { + if (packages is null || packages.Count == 0) + return; + + foreach (var package in packages) + { + DisposePackageInfos(package); + } + } + + public Result 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(new PackageDependency(ownerPackage, depInfo, ownerPackage.Name)); + } + + public Result 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(_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 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(_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 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(_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 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(_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 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(_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 GetAssembliesInfos(IReadOnlyList 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(); + 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 GetConfigsInfos(IReadOnlyList 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(); + 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 GetConfigProfilesInfos(IReadOnlyList 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(); + 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 GetLocalizationsInfos(IReadOnlyList 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(); + 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 GetLuaScriptsInfos(IReadOnlyList 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(); + 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> GetAssembliesInfosAsync(IReadOnlyList packages, bool onlySupportedResources = true) + { + return await Task.Run(() => GetAssembliesInfos(packages, onlySupportedResources)); + } + + public async Task> GetConfigsInfosAsync(IReadOnlyList packages, bool onlySupportedResources = true) + { + return await Task.Run(() => GetConfigsInfos(packages, onlySupportedResources)); + } + + public async Task> GetConfigProfilesInfosAsync(IReadOnlyList packages, bool onlySupportedResources = true) + { + return await Task.Run(() => GetConfigProfilesInfos(packages, onlySupportedResources)); + } + + public async Task> GetLocalizationsInfosAsync(IReadOnlyList packages, bool onlySupportedResources = true) + { + return await Task.Run(() => GetLocalizationsInfos(packages, onlySupportedResources)); + } + + public async Task> GetLuaScriptsInfosAsync(IReadOnlyList packages, bool onlySupportedResources = true) + { + return await Task.Run(() => GetLuaScriptsInfos(packages, onlySupportedResources)); + } + } diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/IConverterServiceDefinitions.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/IConverterServiceDefinitions.cs index a71445714..c64ffdb02 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/IConverterServiceDefinitions.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/IConverterServiceDefinitions.cs @@ -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 : IReusableService +public interface IConverterService : IService { Result TryParseResource(TSrc src); - Result TryParseResources(IEnumerable sources); + ImmutableArray> TryParseResources(IEnumerable sources); } -public interface IConverterServiceAsync : IReusableService +public interface IConverterServiceAsync : IService { Task> TryParseResourceAsync(TSrc src); - Task> TryParseResourcesAsync(IEnumerable sources); + Task>> TryParseResourcesAsync(IEnumerable sources); } -#endregion +public interface IProcessorService : IService +{ + TOut Process(TSrc src); +} + +public interface IProcessorServiceAsync : IService +{ + Task ProcessAsync(TSrc src); +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/IModConfigCreatorService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/IModConfigCreatorService.cs deleted file mode 100644 index f91bdba34..000000000 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/IModConfigCreatorService.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Barotrauma.LuaCs.Data; - -namespace Barotrauma.LuaCs.Services.Processing; - -public interface IModConfigCreatorService : IService -{ - FluentResults.Result BuildConfigForPackage(ContentPackage package); - FluentResults.Result BuildConfigFromManifest(string manifestPath); -} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/ModConfigService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/ModConfigService.cs new file mode 100644 index 000000000..b4f39989a --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/ModConfigService.cs @@ -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, IConverterService +{ + private readonly IStorageService _storageService; + private readonly Lazy _packageManagementService; + private int _isDisposed; + + private const string ModConfigFileName = "ModConfig.xml"; + private const string ModConfigRootName = "ModConfig"; + + public ModConfigService(IStorageService storageService, Lazy 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> TryParseResourceAsync(ContentPackage src) + { + ((IService)this).CheckDisposed(); + + // validate package + if (src is null) + return FluentResults.Result.Fail("ContentPackage is null"); + if (_storageService.DirectoryExists(src.Path) is { } res && (res.IsFailed || !res.Value)) + return FluentResults.Result.Fail($"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.Empty, + ConfigProfiles = ImmutableArray.Empty, + Localizations = ImmutableArray.Empty, + Package = src, + PackageName = src.Name +#if CLIENT + ,Styles = ImmutableArray.Empty +#endif + }; + } + catch (Exception e) + { + return FluentResults.Result.Fail($"Unable to parse legacy content package: {src.Name}: {src.Path}"); + } + } + + private partial Task> GetModConfigInfoAsync(ContentPackage package, XElement root); + + private ImmutableArray GetLocalizations(ContentPackage src, IEnumerable elements) + { + var builder = ImmutableArray.CreateBuilder(); + + if (GetXmlFilesList(src, elements, "Localizations") + is not { IsSuccess: true, Value: { } xmlFiles }) + return ImmutableArray.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.Empty; + } + + private ImmutableArray GetAssemblies(ContentPackage src, IEnumerable elements) + { + var builder = ImmutableArray.CreateBuilder(); + var elementsList = elements.ToImmutableArray(); + + if (GetFilesList(src, elementsList, "Assembly", "*.dll") + is not { IsSuccess: true, Value: { } xmlFiles }) + return ImmutableArray.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.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.Empty; + } + + private ImmutableArray GetConfigs(ContentPackage src, IEnumerable elements) + { + var builder = ImmutableArray.CreateBuilder(); + if (GetXmlFilesList(src, elements, "Config") + is not { IsSuccess: true, Value: { } xmlFiles }) + return ImmutableArray.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.Empty; + } + + private ImmutableArray GetConfigProfiles(ContentPackage src, IEnumerable elements) + { + var builder = ImmutableArray.CreateBuilder(); + if (GetXmlFilesList(src, elements, "Config") + is not { IsSuccess: true, Value: { } xmlFiles }) + return ImmutableArray.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.Empty; + } + + private ImmutableArray GetLuaScripts(ContentPackage src, IEnumerable elements) + { + var builder = ImmutableArray.CreateBuilder(); + if (GetXmlFilesList(src, elements, "Config") + is not { IsSuccess: true, Value: { } xmlFiles }) + return ImmutableArray.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.Empty; + } + + private Result)>> GetXmlFilesList(ContentPackage src, + IEnumerable elements, string elementNameCheck) => + GetFilesList(src, elements, elementNameCheck, "*.xml"); + + private Result)>> GetFilesList(ContentPackage src, + IEnumerable elements, string elementNameCheck, string filter) + { + var builder = ImmutableArray.CreateBuilder<(XElement, ImmutableArray)>(); + + 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 GetSupportedCultures(XElement element) + { + var culture = element.GetAttributeString("Culture", string.Empty); + if (string.IsNullOrWhiteSpace(culture)) + return new[] { CultureInfo.InvariantCulture }.ToImmutableArray(); + var builder = ImmutableArray.CreateBuilder(); + 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 GetElementsDependenciesData(XElement element, ContentPackage src) + { + if (element.GetChildElement("Dependencies") is not {} dependencies + || dependencies.GetChildElements("Dependency").ToImmutableArray() is not { Length: >0 } depsList) + return ImmutableArray.Empty; + var builder = ImmutableArray.CreateBuilder(); + 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 GetAssembliesLegacy(ContentPackage src) + { + var builder = ImmutableArray.CreateBuilder(); + // server, linux + if (_storageService.FindFilesInPackage(src, "bin/Server/Linux", "*.dll", true) + is { IsSuccess: true, Value: { IsDefaultOrEmpty: false} filesSrvLin}) + { + builder.Add(new AssemblyResourceInfo() + { + Dependencies = ImmutableArray.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.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.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.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.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.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.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.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 GetLuaScriptsLegacy(ContentPackage src) + { + var builder = ImmutableArray.CreateBuilder(); + + if (_storageService.FindFilesInPackage(src, "Lua", "*.lua", true) + is { IsSuccess: true, Value: { IsDefaultOrEmpty: false } fileAll }) + { + builder.Add(new LuaScriptScriptResourceInfo() + { + Dependencies = ImmutableArray.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.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>> TryParseResourcesAsync(IEnumerable sources) + { + ((IService)this).CheckDisposed(); + + var srcs = sources.ToImmutableArray(); + var results = new AsyncLocal>>(); + 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($"Failed to parse package {pkg?.Name}: {e.Message}")); + } + }); + return results.Value.ToImmutableArray(); + } + + public Result TryParseResource(ContentPackage src) => + TryParseResourceAsync(src).GetAwaiter().GetResult(); + public ImmutableArray> TryParseResources(IEnumerable sources) => + TryParseResourcesAsync(sources.ToImmutableArray()).GetAwaiter().GetResult(); + + private record ResourceAdditionalInfo( + string Name, + Platform SupportedPlatforms, + Target SupportedTargets, + ImmutableArray SupportedCultures, + bool IsOptional, + int LoadPriority); +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/ResourceInfoProcessors.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/ResourceInfoProcessors.cs new file mode 100644 index 000000000..c1151a649 --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/Processing/ResourceInfoProcessors.cs @@ -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, IAssembliesResourcesInfo>, + IProcessorService, IConfigsResourcesInfo>, + IProcessorService, IConfigProfilesResourcesInfo>, + IProcessorService, ILocalizationsResourcesInfo>, + IProcessorService, ILuaScriptsResourcesInfo> +{ + private bool _isDisposed; + public IAssembliesResourcesInfo Process(IReadOnlyList src) + { + return new AssemblyResourcesInfo(src.ToImmutableArray()); + } + + public IConfigsResourcesInfo Process(IReadOnlyList src) + { + return new ConfigResourcesInfo(src.ToImmutableArray()); + } + + public IConfigProfilesResourcesInfo Process(IReadOnlyList src) + { + return new ConfigProfilesResourcesInfo(src.ToImmutableArray()); + } + + public ILocalizationsResourcesInfo Process(IReadOnlyList src) + { + return new LocalizationResourcesInfo(src.ToImmutableArray()); + } + + public ILuaScriptsResourcesInfo Process(IReadOnlyList src) + { + return new LuaScriptsResourcesInfo(src.ToImmutableArray()); + } + + public void Dispose() + { + // Stateless class + GC.SuppressFinalize(this); + IsDisposed = true; + } + + public bool IsDisposed + { + get => _isDisposed; + set => _isDisposed = value; + } +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/StorageService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/StorageService.cs index d812e767d..bc7bd0bad 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/StorageService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/StorageService.cs @@ -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 DirectoryExists(string directoryPath) + { + ((IService)this).CheckDisposed(); + try + { + var di = new DirectoryInfo(directoryPath); + return di.Exists; + } + catch (Exception ex) + { + return new FluentResults.Result().WithError(ex.Message); + } + } + public async Task> 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(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(GetGeneralError(nameof(TryLoadXmlAsync), filePath)); + } } public async Task> TryLoadTextAsync(string filePath, Encoding encoding = null) @@ -601,7 +621,7 @@ public class StorageService : IStorageService localFilePath))); } - private FluentResults.Result GetAbsFromPackage(ContentPackage package, string localFilePath) + public FluentResults.Result GetAbsFromPackage(ContentPackage package, string localFilePath) { if (package is null) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IConfigService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IConfigService.cs index c540f3b78..5e5a19118 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IConfigService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IConfigService.cs @@ -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; diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/INetworkingService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/INetworkingService.cs index e5c94a1d3..835176d95 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/INetworkingService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/INetworkingService.cs @@ -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; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IPackageInfoLookupService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IPackageInfoLookupService.cs new file mode 100644 index 000000000..36dabf384 --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IPackageInfoLookupService.cs @@ -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> Lookup(string packageName); + Task> Lookup(string packageName, ulong steamWorkshopId); + Task> Lookup(ulong steamWorkshopId); + Task> Lookup(ContentPackage package); + void RefreshPackageLists(); +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IPackageListRetrievalService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IPackageListRetrievalService.cs new file mode 100644 index 000000000..c534047a5 --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IPackageListRetrievalService.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Barotrauma.LuaCs.Services; + +public interface IPackageListRetrievalService : IService +{ + IEnumerable GetEnabledContentPackages(); + IEnumerable GetAllContentPackages(); +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IPackageManagementService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IPackageManagementService.cs index e3013afe9..72bf528d0 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IPackageManagementService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IPackageManagementService.cs @@ -17,20 +17,22 @@ public interface IPackageManagementService : IReusableService, ILocalizationsRes { /// /// Loads and parses the provided for supported by the current runtime environment. + /// Will overwrite any existing package data. /// - /// + /// Package to load. /// - Task LoadPackageInfosAsync(ContentPackage packages); + Task LoadPackageInfosAsync(ContentPackage package); /// /// Loads and parses the provided collection for supported by the current runtime environment. + /// Will overwrite any existing package data. /// - /// + /// List of packages to load. /// Task> LoadPackagesInfosAsync(IReadOnlyList packages); IReadOnlyList GetAllLoadedPackages(); void DisposePackageInfos(ContentPackage package); void DisposePackagesInfos(IReadOnlyList packages); - void DisposeAllPackagesInfos(); + FluentResults.Result GetPackageDependencyInfo(ContentPackage ownerPackage, string packageName, ulong steamWorkshopId); // single FluentResults.Result GetAssembliesInfos(ContentPackage package, bool onlySupportedResources = true); diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IService.cs index 680f51fc2..3e8457e19 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IService.cs @@ -18,6 +18,7 @@ public interface IReusableService : IService /// /// Base interface inherited by all services. /// +/// Throws exception if `IsDisposed` return true. public interface IService : IDisposable { bool IsDisposed { get; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IStorageService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IStorageService.cs index bbcac52a4..281c39a9b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IStorageService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IStorageService.cs @@ -32,6 +32,7 @@ public interface IStorageService : IService ImmutableArray<(string, FluentResults.Result)> LoadPackageBinaryFiles(ContentPackage package, ImmutableArray localFilePaths); ImmutableArray<(string, FluentResults.Result)> LoadPackageTextFiles(ContentPackage package, ImmutableArray localFilePaths); FluentResults.Result> FindFilesInPackage(ContentPackage package, string localSubfolder, string regexFilter, bool searchRecursively); + FluentResults.Result GetAbsFromPackage(ContentPackage package, string localFilePath); // async // singles Task> 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 FileExists(string filePath); + FluentResults.Result DirectoryExists(string directoryPath); + //async Task> TryLoadXmlAsync(string filePath, Encoding encoding = null); Task> TryLoadTextAsync(string filePath, Encoding encoding = null); diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Plugins/CsPackageManager.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Plugins/CsPackageManager.cs index d323ac849..9e7060756 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Plugins/CsPackageManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Plugins/CsPackageManager.cs @@ -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 set, @@ -1098,3 +1096,4 @@ public sealed class CsPackageManager : IDisposable #endregion } +*/ diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Plugins/MemoryFileAssemblyContextLoader.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Plugins/MemoryFileAssemblyContextLoader.cs index d5ccc6a86..dc570158b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Plugins/MemoryFileAssemblyContextLoader.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Plugins/MemoryFileAssemblyContextLoader.cs @@ -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; } } +*/ diff --git a/Barotrauma/BarotraumaTest/LuaCs/HookPatchHelpers.cs b/Barotrauma/BarotraumaTest/LuaCs/HookPatchHelpers.cs index cdc4ed6f1..49037a3c3 100644 --- a/Barotrauma/BarotraumaTest/LuaCs/HookPatchHelpers.cs +++ b/Barotrauma/BarotraumaTest/LuaCs/HookPatchHelpers.cs @@ -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(this LuaCsSetup luaCs, string body, string methodName, string[]? parameters = null, string? patchId = null) diff --git a/Barotrauma/BarotraumaTest/LuaCs/HookPatchTests.cs b/Barotrauma/BarotraumaTest/LuaCs/HookPatchTests.cs index b9366b239..9608b804a 100644 --- a/Barotrauma/BarotraumaTest/LuaCs/HookPatchTests.cs +++ b/Barotrauma/BarotraumaTest/LuaCs/HookPatchTests.cs @@ -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(); UserData.RegisterType(); UserData.RegisterType(); - - luaCs.Initialize(); - luaCs.Lua.Globals["TestValueType"] = UserData.CreateStatic(); - luaCs.Lua.Globals["InterfaceImplementingType"] = UserData.CreateStatic(); + + luaCs.ForceRunState(RunState.Running); + throw new NotImplementedException(); + //luaCs.Initialize(); + //luaCs.Lua.Globals["TestValueType"] = UserData.CreateStatic(); + //luaCs.Lua.Globals["InterfaceImplementingType"] = UserData.CreateStatic(); } private class PatchTargetSimple @@ -664,3 +670,4 @@ namespace TestProject.LuaCs } } } +*/ diff --git a/Barotrauma/BarotraumaTest/LuaCs/LuaCsFixture.cs b/Barotrauma/BarotraumaTest/LuaCs/LuaCsFixture.cs index 70c2d9c1e..61fdc58a6 100644 --- a/Barotrauma/BarotraumaTest/LuaCs/LuaCsFixture.cs +++ b/Barotrauma/BarotraumaTest/LuaCs/LuaCsFixture.cs @@ -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 { /// @@ -31,3 +34,4 @@ namespace TestProject.LuaCs void IDisposable.Dispose() => LuaCs.Stop(); } } +*/