- Finished most of LuaCsSetup top-level functionality.
- Removed some unneeded interface definitions. - Clean-slated some Services that need to be re-written.
This commit is contained in:
+25
-3
@@ -9,6 +9,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.LuaCs.Events;
|
||||
using Barotrauma.Steam;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
@@ -48,7 +49,12 @@ namespace Barotrauma
|
||||
public static ImmutableArray<RegularPackage>? Regular;
|
||||
}
|
||||
|
||||
public static void SetCore(CorePackage newCore) => SetCoreEnumerable(newCore).Consume();
|
||||
public static void SetCore(CorePackage newCore)
|
||||
{
|
||||
SetCoreEnumerable(newCore).Consume();
|
||||
GameMain.LuaCs.EventService.PublishEvent<IEventEnabledPackageListChanged>(
|
||||
sub => sub.OnEnabledPackageListChanged(Core, Regular));
|
||||
}
|
||||
|
||||
public static IEnumerable<LoadProgress> SetCoreEnumerable(CorePackage newCore)
|
||||
{
|
||||
@@ -85,7 +91,11 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public static void SetRegular(IReadOnlyList<RegularPackage> newRegular)
|
||||
=> SetRegularEnumerable(newRegular).Consume();
|
||||
{
|
||||
SetRegularEnumerable(newRegular).Consume();
|
||||
GameMain.LuaCs.EventService.PublishEvent<IEventEnabledPackageListChanged>(
|
||||
sub => sub.OnEnabledPackageListChanged(Core, Regular));
|
||||
}
|
||||
|
||||
public static IEnumerable<LoadProgress> SetRegularEnumerable(IReadOnlyList<RegularPackage> inNewRegular)
|
||||
{
|
||||
@@ -327,6 +337,12 @@ namespace Barotrauma
|
||||
|
||||
Debug.WriteLine($"Loaded \"{newPackage.Name}\"");
|
||||
}
|
||||
|
||||
GameMain.LuaCs.EventService.PublishEvent<IEventAllPackageListChanged>(sub =>
|
||||
sub.OnAllPackageListChanged(corePackages
|
||||
.Select((ContentPackage p) => p)
|
||||
.Union(regularPackages.Select((ContentPackage p) => p))
|
||||
.ToImmutableArray()));
|
||||
}
|
||||
|
||||
private readonly string directory;
|
||||
@@ -566,6 +582,12 @@ namespace Barotrauma
|
||||
yield return p.Transform(loadingRange);
|
||||
}
|
||||
|
||||
GameMain.LuaCs.EventService.PublishEvent<IEventAllPackageListChanged>(
|
||||
sub => sub.OnAllPackageListChanged(CorePackages, RegularPackages));
|
||||
|
||||
GameMain.LuaCs.EventService.PublishEvent<IEventEnabledPackageListChanged>(
|
||||
sub => sub.OnEnabledPackageListChanged(EnabledPackages.Core, EnabledPackages.Regular));
|
||||
|
||||
yield return LoadProgress.Progress(1.0f);
|
||||
}
|
||||
|
||||
@@ -597,4 +619,4 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,4 @@ public interface IConfigEntry<T> : IConfigBase, INetVar where T : IConvertible,
|
||||
bool TrySetValue(T value);
|
||||
bool IsAssignable(T value);
|
||||
void Initialize(IVarId id, T defaultValue);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public partial record ModConfigInfo : IModConfigInfo
|
||||
public ImmutableArray<CultureInfo> SupportedCultures { get; init; }
|
||||
public ImmutableArray<IAssemblyResourceInfo> Assemblies { get; init; }
|
||||
public ImmutableArray<ILocalizationResourceInfo> Localizations { get; init; }
|
||||
public ImmutableArray<ILuaResourceInfo> LuaScripts { get; init; }
|
||||
public ImmutableArray<ILuaScriptResourceInfo> LuaScripts { get; init; }
|
||||
public ImmutableArray<IConfigResourceInfo> Configs { get; init; }
|
||||
public ImmutableArray<IConfigProfileResourceInfo> ConfigProfiles { get; init; }
|
||||
}
|
||||
@@ -160,7 +160,7 @@ public record LocalizationResourceInfo : ILocalizationResourceInfo
|
||||
public bool Optional { get; init; }
|
||||
}
|
||||
|
||||
public readonly struct LuaScriptResourceInfo : ILuaResourceInfo
|
||||
public readonly struct LuaScriptScriptResourceInfo : ILuaScriptResourceInfo
|
||||
{
|
||||
public ContentPackage OwnerPackage { get; init; }
|
||||
public string FallbackPackageName { get; init; }
|
||||
|
||||
@@ -10,7 +10,7 @@ public interface ILocalizationResourceInfo : IResourceInfo, IResourceCultureInfo
|
||||
/// <summary>
|
||||
/// Represents loadable Lua files.
|
||||
/// </summary>
|
||||
public interface ILuaResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IPackageInfo { }
|
||||
public interface ILuaScriptResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IPackageInfo { }
|
||||
public interface IAssemblyResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IPackageInfo
|
||||
{
|
||||
/// <summary>
|
||||
@@ -39,7 +39,7 @@ public interface ILocalizationsResourcesInfo
|
||||
|
||||
public interface ILuaScriptsResourcesInfo
|
||||
{
|
||||
ImmutableArray<ILuaResourceInfo> LuaScripts { get; }
|
||||
ImmutableArray<ILuaScriptResourceInfo> LuaScripts { get; }
|
||||
}
|
||||
|
||||
public interface IConfigsResourcesInfo
|
||||
|
||||
@@ -29,6 +29,35 @@ public interface IEvent<out T> : IEvent where T : IEvent<T>
|
||||
}
|
||||
}
|
||||
|
||||
#region RuntimeEvents
|
||||
|
||||
/// <summary>
|
||||
/// Called when the current <see cref="Screen"/> (game state) changes. Upstream Type 'Screen' is internal.
|
||||
/// </summary>
|
||||
internal interface IEventScreenSelected : IEvent<IEventScreenSelected>
|
||||
{
|
||||
void OnScreenSelected(Screen screen);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever the list of all <see cref="ContentPackage"/> (enabled and disabled) on disk has changed.
|
||||
/// </summary>
|
||||
internal interface IEventAllPackageListChanged : IEvent<IEventAllPackageListChanged>
|
||||
{
|
||||
void OnAllPackageListChanged(IEnumerable<CorePackage> corePackages, IEnumerable<RegularPackage> regularPackages);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever the list of enabled <see cref="ContentPackage"/> has changed.
|
||||
/// </summary>
|
||||
internal interface IEventEnabledPackageListChanged : IEvent<IEventEnabledPackageListChanged>
|
||||
{
|
||||
void OnEnabledPackageListChanged(CorePackage package, IEnumerable<RegularPackage> regularPackages);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region GameEvents
|
||||
|
||||
/// <summary>
|
||||
@@ -62,11 +91,11 @@ public interface IEventRoundStarted : IEvent<IEventRoundStarted>
|
||||
/// </summary>
|
||||
public interface IEventUpdate : IEvent<IEventUpdate>
|
||||
{
|
||||
void OnUpdate(float fixedDeltaTime);
|
||||
void OnUpdate(double fixedDeltaTime);
|
||||
static IEventUpdate IEvent<IEventUpdate>.GetLuaRunner(IDictionary<string, LuaCsFunc> luaFunc) => new
|
||||
{
|
||||
IsLuaRunner = Return<bool>.Arguments(() => true),
|
||||
OnUpdate = ReturnVoid.Arguments<float>((fixedDeltaTime) => luaFunc[nameof(OnUpdate)](fixedDeltaTime))
|
||||
OnUpdate = ReturnVoid.Arguments<double>((fixedDeltaTime) => luaFunc[nameof(OnUpdate)](fixedDeltaTime))
|
||||
}.ActLike<IEventUpdate>();
|
||||
}
|
||||
|
||||
@@ -75,11 +104,11 @@ public interface IEventUpdate : IEvent<IEventUpdate>
|
||||
/// </summary>
|
||||
public interface IEventDrawUpdate : IEvent<IEventDrawUpdate>
|
||||
{
|
||||
void OnDrawUpdate(float deltaTime);
|
||||
void OnDrawUpdate(double deltaTime);
|
||||
static IEventDrawUpdate IEvent<IEventDrawUpdate>.GetLuaRunner(IDictionary<string, LuaCsFunc> luaFunc) => new
|
||||
{
|
||||
IsLuaRunner = Return<bool>.Arguments(() => true),
|
||||
OnDrawUpdate = ReturnVoid.Arguments<float>((deltaTime) => luaFunc[nameof(OnDrawUpdate)](deltaTime))
|
||||
OnDrawUpdate = ReturnVoid.Arguments<double>((deltaTime) => luaFunc[nameof(OnDrawUpdate)](deltaTime))
|
||||
}.ActLike<IEventDrawUpdate>();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,130 +1,77 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.LuaCs.Configuration;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.LuaCs.Events;
|
||||
using Barotrauma.LuaCs.Services;
|
||||
using Barotrauma.LuaCs.Services.Compatibility;
|
||||
using Barotrauma.LuaCs.Services.Processing;
|
||||
using Barotrauma.Networking;
|
||||
using FluentResults;
|
||||
using ImpromptuInterface;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class LuaCsSetupConfig
|
||||
{
|
||||
public bool EnableCsScripting = false;
|
||||
public bool TreatForcedModsAsNormal = true;
|
||||
public bool PreferToUseWorkshopLuaSetup = false;
|
||||
public bool DisableErrorGUIOverlay = false;
|
||||
public bool HideUserNames
|
||||
{
|
||||
get { return LuaCsLogger.HideUserNames; }
|
||||
set { LuaCsLogger.HideUserNames = value; }
|
||||
}
|
||||
|
||||
public LuaCsSetupConfig() { }
|
||||
public LuaCsSetupConfig(LuaCsSetupConfig config)
|
||||
{
|
||||
EnableCsScripting = config.EnableCsScripting;
|
||||
TreatForcedModsAsNormal = config.TreatForcedModsAsNormal;
|
||||
PreferToUseWorkshopLuaSetup = config.PreferToUseWorkshopLuaSetup;
|
||||
DisableErrorGUIOverlay = config.DisableErrorGUIOverlay;
|
||||
}
|
||||
}
|
||||
|
||||
internal delegate void LuaCsMessageLogger(string message);
|
||||
internal delegate void LuaCsErrorHandler(Exception ex, LuaCsMessageOrigin origin);
|
||||
internal delegate void LuaCsExceptionHandler(Exception ex, LuaCsMessageOrigin origin);
|
||||
|
||||
partial class LuaCsSetup : IDisposable
|
||||
partial class LuaCsSetup : IDisposable, IEventScreenSelected, IEventAllPackageListChanged, IEventEnabledPackageListChanged
|
||||
{
|
||||
public LuaCsSetup()
|
||||
{
|
||||
// load services
|
||||
// == startup
|
||||
_servicesProvider = new ServicesProvider();
|
||||
RegisterServices();
|
||||
|
||||
// load manifest
|
||||
if (!_servicesProvider.TryGetService(out IModConfigCreatorService modConfigSvc))
|
||||
throw new NullReferenceException("LuaCsSetup: Failed to get mod config parser service!"); // we should crash here
|
||||
var luaConfig = modConfigSvc.BuildConfigFromManifest(Directory.GetCurrentDirectory() + LuaCsConfigFile);
|
||||
if (!luaConfig.IsSuccess)
|
||||
{
|
||||
Logger.LogResults(luaConfig.ToResult());
|
||||
throw new FileLoadException("LuaCsSetup: Failed to load config file!");
|
||||
}
|
||||
|
||||
// load resources
|
||||
RegisterLocalizations();
|
||||
RegisterConfigs();
|
||||
|
||||
LuaForBarotraumaId = new SteamWorkshopId(LuaForBarotraumaSteamId.Value);
|
||||
ValidateLuaCsContent();
|
||||
SubscribeToLuaCsEvents();
|
||||
|
||||
return;
|
||||
//---
|
||||
// == end
|
||||
|
||||
// == helpers
|
||||
void RegisterServices()
|
||||
{
|
||||
_servicesProvider.RegisterServiceType<ILoggerService, LoggerService>(ServiceLifetime.Singleton);
|
||||
_servicesProvider.RegisterServiceType<PerformanceCounterService, PerformanceCounterService>(ServiceLifetime.Singleton);
|
||||
_servicesProvider.RegisterServiceType<IPackageService, PackageService>(ServiceLifetime.Singleton);
|
||||
_servicesProvider.RegisterServiceType<IStorageService, StorageService>(ServiceLifetime.Transient);
|
||||
_servicesProvider.RegisterServiceType<IEventService, EventService>(ServiceLifetime.Singleton);
|
||||
#if CLIENT
|
||||
_servicesProvider.RegisterServiceType<IStylesService, StylesService>(ServiceLifetime.Transient);
|
||||
_servicesProvider.RegisterServiceType<IStylesManagementService, StylesManagementService>(ServiceLifetime.Singleton);
|
||||
#endif
|
||||
_servicesProvider.RegisterServiceType<IPackageManagementService, PackageManagementService>(ServiceLifetime.Singleton);
|
||||
_servicesProvider.RegisterServiceType<ILuaScriptService, LuaScriptService>(ServiceLifetime.Singleton);
|
||||
_servicesProvider.RegisterServiceType<ILuaScriptManagementService, LuaScriptService>(ServiceLifetime.Singleton);
|
||||
_servicesProvider.RegisterServiceType<IPluginManagementService, PluginManagementService>(ServiceLifetime.Singleton);
|
||||
_servicesProvider.RegisterServiceType<ILuaScriptManagementService, LuaScriptManagementService>(ServiceLifetime.Singleton);
|
||||
// TODO: ILocalizationService
|
||||
// TODO: IConfigService
|
||||
// TODO: INetworkingService
|
||||
// TODO: [Resource Converter/Parser Services]
|
||||
// TODO: ILocalizationService
|
||||
// TODO: IEventService
|
||||
|
||||
_servicesProvider.Compile();
|
||||
}
|
||||
|
||||
void RegisterLocalizations()
|
||||
// Validates LuaCs assets in /Content are valid and ready to use.
|
||||
void ValidateLuaCsContent()
|
||||
{
|
||||
LocalizationService.LoadLocalizations(luaConfig.Value.Localizations);
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
void RegisterConfigs()
|
||||
{
|
||||
if (ConfigService.AddConfigs(luaConfig.Value.Configs) is { IsSuccess: false } res1)
|
||||
{
|
||||
Logger.LogResults(res1);
|
||||
throw new Exception("LuaCsSetup: Failed to load config!");
|
||||
}
|
||||
|
||||
if (ConfigService.AddConfigsProfiles(luaConfig.Value.ConfigProfiles) is { IsSuccess: false } res2)
|
||||
{
|
||||
Logger.LogResults(res2);
|
||||
throw new Exception("LuaCsSetup: Failed to load config profiles!");
|
||||
}
|
||||
|
||||
IsCsEnabled = GetOrThrowForConfig<bool>(luaConfig.Value.PackageName, "IsCsEnabled");
|
||||
TreatForcedModsAsNormal = GetOrThrowForConfig<bool>(luaConfig.Value.PackageName, "TreatForcedModsAsNormal");
|
||||
PreferToUseWorkshopLuaSetup = GetOrThrowForConfig<bool>(luaConfig.Value.PackageName, "PreferToUseWorkshopLuaSetup");
|
||||
DisableErrorGUIOverlay = GetOrThrowForConfig<bool>(luaConfig.Value.PackageName, "DisableErrorGUIOverlay");
|
||||
EnableThreadedLoading = GetOrThrowForConfig<bool>(luaConfig.Value.PackageName, "EnableThreadedLoading");
|
||||
HideUserNamesInLogs = GetOrThrowForConfig<bool>(luaConfig.Value.PackageName, "HideUserNamesInLogs");
|
||||
LuaForBarotraumaSteamId = GetOrThrowForConfig<ulong>(luaConfig.Value.PackageName, "LuaForBarotraumaSteamId");
|
||||
|
||||
return;
|
||||
//---
|
||||
|
||||
IConfigEntry<T> GetOrThrowForConfig<T>(string packName, string internalName) where T : IConvertible, IEquatable<T>
|
||||
{
|
||||
var cfgRes = ConfigService.GetConfig<IConfigEntry<T>>(packName, internalName);
|
||||
if (cfgRes.IsSuccess)
|
||||
{
|
||||
return cfgRes.Value;
|
||||
}
|
||||
Logger.LogResults(cfgRes.ToResult());
|
||||
throw new Exception($"LuaCsSetup: Failed to load config for {internalName}!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void SubscribeToLuaCsEvents()
|
||||
{
|
||||
EventService.Subscribe<IEventScreenSelected>(this); // game state hook in
|
||||
EventService.Subscribe<IEventAllPackageListChanged>(this);
|
||||
EventService.Subscribe<IEventEnabledPackageListChanged>(this);
|
||||
}
|
||||
|
||||
#region CONST_DEF
|
||||
|
||||
public const string LuaCsConfigFile = "LuaCsConfig.xml";
|
||||
|
||||
#if SERVER
|
||||
public const bool IsServer = true;
|
||||
@@ -154,7 +101,7 @@ namespace Barotrauma
|
||||
? svc : throw new NullReferenceException("Package Manager service not found!");
|
||||
public IPluginManagementService PluginManagementService => _servicesProvider.TryGetService<IPluginManagementService>(out var svc)
|
||||
? svc : throw new NullReferenceException("Plugin Manager service not found!");
|
||||
public ILuaScriptManagementService LuaScriptService => _servicesProvider.TryGetService<ILuaScriptManagementService>(out var svc)
|
||||
public ILuaScriptManagementService LuaScriptManagementService => _servicesProvider.TryGetService<ILuaScriptManagementService>(out var svc)
|
||||
? svc : throw new NullReferenceException("Lua Script Manager service not found!");
|
||||
public ILocalizationService LocalizationService => _servicesProvider.TryGetService<ILocalizationService>(out var svc)
|
||||
? svc : throw new NullReferenceException("Localization Manager service not found!");
|
||||
@@ -187,17 +134,42 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public IConfigEntry<bool> DisableErrorGUIOverlay { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// [Experimental] Whether multithreading should be used for loading.
|
||||
/// </summary>
|
||||
public IConfigEntry<bool> EnableThreadedLoading { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether usernames are anonymized or show in logs.
|
||||
/// </summary>
|
||||
public IConfigEntry<bool> HideUserNamesInLogs { get; private set; }
|
||||
|
||||
private IConfigEntry<ulong> LuaForBarotraumaSteamId { get; set; }
|
||||
/// <summary>
|
||||
/// The SteamId of the Workshop LuaCs CPackage in use, if available.
|
||||
/// </summary>
|
||||
public IConfigEntry<ulong> LuaForBarotraumaSteamId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The SteamId of the Workshop LuaCs CsForBarotrauma add-on, if available.
|
||||
/// </summary>
|
||||
public IConfigEntry<ulong> CsForBarotraumaSteamId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to (re)load all package assets when a lobby starts/code session begins.
|
||||
/// Intended for development use, or when packages are expected to change outside of External Updates (ie. Steam Workshop).
|
||||
/// </summary>
|
||||
public IConfigEntry<bool> ReloadPackagesOnLobbyStart { get; private set; }
|
||||
|
||||
/**
|
||||
* == Ops Vars
|
||||
*/
|
||||
private RunState _runState;
|
||||
/// <summary>
|
||||
/// The current run state of all services managed by LuaCs.
|
||||
/// </summary>
|
||||
public RunState CurrentRunState => _runState;
|
||||
|
||||
private bool CPacksParsed => CurrentRunState >= RunState.Parsed;
|
||||
private bool IsStaticAssetsLoaded => CurrentRunState >= RunState.Configuration;
|
||||
private bool IsCodeRunning => CurrentRunState >= RunState.Running;
|
||||
|
||||
private readonly ConcurrentQueue<ContentPackage> _toLoad = new();
|
||||
private readonly ConcurrentQueue<ContentPackage> _toUnload = new();
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -208,13 +180,6 @@ namespace Barotrauma
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Whether mod content is loaded and being executed.
|
||||
/// </summary>
|
||||
public bool IsModContentRunning { get; private set; }
|
||||
|
||||
public readonly ContentPackageId LuaForBarotraumaId;
|
||||
|
||||
public static bool IsRunningInsideWorkshop
|
||||
{
|
||||
get
|
||||
@@ -227,84 +192,16 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
/*public Script Lua { get; private set; }
|
||||
public LuaScriptLoader LuaScriptLoader { get; private set; }
|
||||
|
||||
public LuaGame Game { get; private set; }
|
||||
public LuaCsHook Hook { get; private set; }
|
||||
public LuaCsTimer Timer { get; private set; }
|
||||
public LuaCsNetworking Networking { get; private set; }
|
||||
public LuaCsSteam Steam { get; private set; }
|
||||
|
||||
// must be available at anytime
|
||||
private static AssemblyManager _assemblyManager;
|
||||
public static AssemblyManager AssemblyManager => _assemblyManager ??= new AssemblyManager();
|
||||
|
||||
private CsPackageManager _pluginPackageManager;
|
||||
public CsPackageManager PluginPackageManager => _pluginPackageManager ??= new CsPackageManager(AssemblyManager, this);
|
||||
private LuaRequire Require { get; set; }
|
||||
public LuaCsSetupConfig Config { get; private set; }
|
||||
public MoonSharpVsCodeDebugServer DebugServer { get; private set; }
|
||||
public bool IsInitialized { get; private set; }*/
|
||||
|
||||
private bool ShouldRunCs
|
||||
{
|
||||
get
|
||||
{
|
||||
#if SERVER
|
||||
if (GetPackage(CsForBarotraumaId, false, false) != null && GameMain.Server.ServerPeer is LidgrenServerPeer) { return true; }
|
||||
#endif
|
||||
return IsCsEnabled.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private partial bool ShouldRunCs();
|
||||
|
||||
|
||||
[Obsolete("Use AssemblyManager::GetTypesByName()")]
|
||||
// TODO: Rework
|
||||
[Obsolete("Use IPluginManagementService::GetTypesByName()")]
|
||||
public static Type GetType(string typeName, bool throwOnError = false, bool ignoreCase = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
//return AssemblyManager.GetTypesByName(typeName).FirstOrDefault((Type)null);
|
||||
}
|
||||
|
||||
// Old config ref
|
||||
/*public void ReadSettings()
|
||||
{
|
||||
Config = new LuaCsSetupConfig();
|
||||
|
||||
if (File.Exists(configFileName))
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var file = File.Open(configFileName, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
XDocument document = XDocument.Load(file);
|
||||
Config.EnableCsScripting = document.Root.GetAttributeBool("EnableCsScripting", Config.EnableCsScripting);
|
||||
Config.TreatForcedModsAsNormal = document.Root.GetAttributeBool("TreatForcedModsAsNormal", Config.TreatForcedModsAsNormal);
|
||||
Config.PreferToUseWorkshopLuaSetup = document.Root.GetAttributeBool("PreferToUseWorkshopLuaSetup", Config.PreferToUseWorkshopLuaSetup);
|
||||
Config.DisableErrorGUIOverlay = document.Root.GetAttributeBool("DisableErrorGUIOverlay", Config.DisableErrorGUIOverlay);
|
||||
Config.HideUserNames = document.Root.GetAttributeBool("HideUserNames", Config.HideUserNames);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LuaCsLogger.HandleException(e, LuaCsMessageOrigin.LuaCs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteSettings()
|
||||
{
|
||||
XDocument document = new XDocument();
|
||||
document.Add(new XElement("LuaCsSetupConfig"));
|
||||
document.Root.SetAttributeValue("EnableCsScripting", Config.EnableCsScripting);
|
||||
document.Root.SetAttributeValue("EnableCsScripting", Config.EnableCsScripting);
|
||||
document.Root.SetAttributeValue("TreatForcedModsAsNormal", Config.TreatForcedModsAsNormal);
|
||||
document.Root.SetAttributeValue("PreferToUseWorkshopLuaSetup", Config.PreferToUseWorkshopLuaSetup);
|
||||
document.Root.SetAttributeValue("DisableErrorGUIOverlay", Config.DisableErrorGUIOverlay);
|
||||
document.Root.SetAttributeValue("HideUserNames", Config.HideUserNames);
|
||||
document.Save(configFileName);
|
||||
}*/
|
||||
|
||||
public static ContentPackage GetPackage(ContentPackageId id, bool fallbackToAll = true, bool useBackup = false)
|
||||
{
|
||||
@@ -349,111 +246,495 @@ namespace Barotrauma
|
||||
return null;
|
||||
}
|
||||
|
||||
// Old code ref
|
||||
/*private DynValue DoFile(string file, Table globalContext = null, string codeStringFriendly = null)
|
||||
{
|
||||
if (!LuaCsFile.CanReadFromPath(file))
|
||||
{
|
||||
throw new ScriptRuntimeException($"dofile: File access to {file} not allowed.");
|
||||
}
|
||||
|
||||
if (!LuaCsFile.Exists(file))
|
||||
{
|
||||
throw new ScriptRuntimeException($"dofile: File {file} not found.");
|
||||
}
|
||||
|
||||
return Lua.DoFile(file, globalContext, codeStringFriendly);
|
||||
}
|
||||
|
||||
private DynValue LoadFile(string file, Table globalContext = null, string codeStringFriendly = null)
|
||||
{
|
||||
if (!LuaCsFile.CanReadFromPath(file))
|
||||
{
|
||||
throw new ScriptRuntimeException($"loadfile: File access to {file} not allowed.");
|
||||
}
|
||||
|
||||
if (!LuaCsFile.Exists(file))
|
||||
{
|
||||
throw new ScriptRuntimeException($"loadfile: File {file} not found.");
|
||||
}
|
||||
|
||||
return Lua.LoadFile(file, globalContext, codeStringFriendly);
|
||||
}
|
||||
|
||||
public DynValue CallLuaFunction(object function, params object[] args)
|
||||
{
|
||||
// XXX: `lua` might be null if `LuaCsSetup.Stop()` is called while
|
||||
// a patched function is still running.
|
||||
if (Lua == null) { return null; }
|
||||
|
||||
lock (Lua)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Lua.Call(function, args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LuaCsLogger.HandleException(e, LuaCsMessageOrigin.LuaMod);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetModulePaths(string[] str)
|
||||
{
|
||||
LuaScriptLoader.ModulePaths = str;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
Timer?.Update();
|
||||
Steam?.Update();
|
||||
|
||||
#if CLIENT
|
||||
Stopwatch luaSw = new Stopwatch();
|
||||
luaSw.Start();
|
||||
#endif
|
||||
Hook?.Call("think");
|
||||
#if CLIENT
|
||||
luaSw.Stop();
|
||||
GameMain.PerformanceCounter.AddElapsedTicks("Think Hook", luaSw.ElapsedTicks);
|
||||
#endif
|
||||
}
|
||||
|
||||
*/
|
||||
public void Stop()
|
||||
{
|
||||
|
||||
|
||||
IsInitialized = false;
|
||||
}
|
||||
|
||||
public void Initialize(bool forceEnableCs = false)
|
||||
{
|
||||
if (IsInitialized)
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
IsInitialized = true;
|
||||
|
||||
Logger.Log($"Initializing LuaCs, git revision = {AssemblyInfo.GitRevision}");
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// TODO release managed resources here
|
||||
try
|
||||
{
|
||||
SetRunState(RunState.Unloaded);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(e.Message);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
DisposeLuaCsConfig();
|
||||
|
||||
PluginManagementService.Dispose();
|
||||
LuaScriptManagementService.Dispose();
|
||||
#if CLIENT
|
||||
StylesManagementService.Dispose();
|
||||
#endif
|
||||
ConfigService.Dispose();
|
||||
LocalizationService.Dispose();
|
||||
PackageManagementService.Dispose();
|
||||
// TODO: Add all missing services.
|
||||
//NetworkingService.Dispose();
|
||||
EventService.Dispose();
|
||||
|
||||
_servicesProvider.DisposeAndReset();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles changes in game states tracked by screen changes.
|
||||
/// </summary>
|
||||
/// <param name="screen">The new game screen.</param>
|
||||
public partial void OnScreenSelected(Screen screen);
|
||||
|
||||
public void OnAllPackageListChanged(IEnumerable<CorePackage> corePackages, IEnumerable<RegularPackage> regularPackages)
|
||||
{
|
||||
UpdateLoadedPackagesList();
|
||||
}
|
||||
|
||||
public void OnEnabledPackageListChanged(CorePackage corePackage, IEnumerable<RegularPackage> regularPackages)
|
||||
{
|
||||
UpdateLoadedPackagesList();
|
||||
}
|
||||
|
||||
private void UpdateLoadedPackagesList()
|
||||
{
|
||||
var newPackSet = ContentPackageManager.AllPackages
|
||||
.Union(ContentPackageManager.EnabledPackages.All)
|
||||
.ToHashSet();
|
||||
var currPackSet = PackageManagementService.GetAllLoadedPackages().ToHashSet();
|
||||
var toAdd = newPackSet.Except(currPackSet);
|
||||
var toRemove = currPackSet.Except(newPackSet);
|
||||
foreach (var package in toAdd)
|
||||
_toLoad.Enqueue(package);
|
||||
foreach (var package in toRemove)
|
||||
_toUnload.Enqueue(package);
|
||||
|
||||
|
||||
ProcessPackagesListDifferences();
|
||||
}
|
||||
|
||||
void ProcessPackagesListDifferences()
|
||||
{
|
||||
if (IsCodeRunning)
|
||||
return;
|
||||
|
||||
// no reason to do anything if we're fully unloaded.
|
||||
if (!CPacksParsed)
|
||||
{
|
||||
_toLoad.Clear();
|
||||
_toUnload.Clear();
|
||||
}
|
||||
|
||||
while (_toUnload.TryDequeue(out var cp))
|
||||
{
|
||||
LuaScriptManagementService.DisposePackageResources(cp);
|
||||
ConfigService.DisposeConfigsProfiles(cp);
|
||||
ConfigService.DisposeConfigs(cp);
|
||||
#if CLIENT
|
||||
StylesManagementService.DisposeStylesForPackage(cp);
|
||||
#endif
|
||||
LocalizationService.DisposePackage(cp);
|
||||
PackageManagementService.DisposePackageInfos(cp);
|
||||
}
|
||||
|
||||
var ls = new List<ContentPackage>();
|
||||
|
||||
while (_toLoad.TryDequeue(out var cp))
|
||||
{
|
||||
if (PackageManagementService.LoadPackageInfosAsync(cp).GetAwaiter().GetResult() is
|
||||
{ IsFailed: true } failure)
|
||||
{
|
||||
Logger.LogError($"Failed to load package infos for {cp.Name}");
|
||||
Logger.LogResults(failure);
|
||||
continue;
|
||||
}
|
||||
|
||||
ls.Add(cp);
|
||||
}
|
||||
|
||||
if (ls.Any())
|
||||
{
|
||||
LoadStaticAssetsAsync(ls).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
|
||||
void SetRunState(RunState runState)
|
||||
{
|
||||
if (CurrentRunState == runState)
|
||||
return;
|
||||
if (runState > CurrentRunState)
|
||||
{
|
||||
if (CurrentRunState < RunState.Parsed)
|
||||
LoadCurrentContentPackageInfos();
|
||||
|
||||
if (runState <= CurrentRunState)
|
||||
return;
|
||||
|
||||
if (CurrentRunState < RunState.Configuration)
|
||||
LoadStaticAssets();
|
||||
|
||||
if (runState <= CurrentRunState)
|
||||
return;
|
||||
|
||||
if (CurrentRunState < RunState.Running)
|
||||
RunScripts();
|
||||
}
|
||||
else if (runState < CurrentRunState)
|
||||
{
|
||||
if (CurrentRunState >= RunState.Running)
|
||||
{
|
||||
StopScripts();
|
||||
ProcessPackagesListDifferences();
|
||||
_runState = RunState.Configuration;
|
||||
}
|
||||
|
||||
if (runState >= CurrentRunState)
|
||||
return;
|
||||
|
||||
if (CurrentRunState == RunState.Configuration)
|
||||
{
|
||||
UnloadStaticAssets();
|
||||
_runState = RunState.Parsed;
|
||||
}
|
||||
|
||||
if (runState >= CurrentRunState)
|
||||
return;
|
||||
|
||||
if (CurrentRunState == RunState.Parsed)
|
||||
{
|
||||
UnloadContentPackageInfos();
|
||||
_runState = RunState.Unloaded;
|
||||
}
|
||||
|
||||
// we should be unloaded completely now | RunState.Unloaded
|
||||
}
|
||||
}
|
||||
|
||||
void LoadCurrentContentPackageInfos()
|
||||
{
|
||||
if (CurrentRunState >= RunState.Parsed)
|
||||
return;
|
||||
|
||||
// load core
|
||||
var result1 = PackageManagementService.LoadPackageInfosAsync(ContentPackageManager.VanillaCorePackage)
|
||||
.GetAwaiter().GetResult();
|
||||
if (result1.IsFailed)
|
||||
{
|
||||
Logger.LogError($"Unable to load LuaCs CorePackage resources! Running in degraded mode.");
|
||||
Logger.LogResults(result1);
|
||||
}
|
||||
|
||||
// load regular
|
||||
var list = ContentPackageManager.RegularPackages
|
||||
.Union(ContentPackageManager.EnabledPackages.All)
|
||||
.ToImmutableList();
|
||||
|
||||
LoadContentPackagesInfos(list);
|
||||
|
||||
if (CurrentRunState < RunState.Parsed)
|
||||
_runState = RunState.Parsed;
|
||||
}
|
||||
|
||||
void LoadContentPackagesInfos(IReadOnlyList<ContentPackage> packages)
|
||||
{
|
||||
var result2 = PackageManagementService.LoadPackagesInfosAsync(packages)
|
||||
.GetAwaiter().GetResult();
|
||||
|
||||
foreach (var entry in result2)
|
||||
{
|
||||
if (entry.Item2.IsSuccess)
|
||||
Logger.LogMessage($"Successfully parsed package: {entry.Item1.Name}");
|
||||
else if (entry.Item2.IsFailed)
|
||||
Logger.LogResults(entry.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadStaticAssets()
|
||||
{
|
||||
if (CurrentRunState < RunState.Parsed)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(LoadStaticAssets)} cannot load assets in the '{CurrentRunState}' state.");
|
||||
}
|
||||
|
||||
if (CurrentRunState >= RunState.Configuration)
|
||||
return;
|
||||
|
||||
while (_toUnload.TryDequeue(out var cp))
|
||||
PackageManagementService.DisposePackageInfos(cp);
|
||||
|
||||
LoadStaticAssetsAsync(PackageManagementService.GetAllLoadedPackages()).GetAwaiter().GetResult();
|
||||
LoadLuaCsConfig();
|
||||
|
||||
if (CurrentRunState < RunState.Configuration)
|
||||
_runState = RunState.Configuration;
|
||||
}
|
||||
|
||||
void LoadLuaCsConfig()
|
||||
{
|
||||
IsCsEnabled = ConfigService.GetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "IsCsEnabled")
|
||||
?? throw new NullReferenceException($"{nameof(IsCsEnabled)} cannot be loaded.");
|
||||
TreatForcedModsAsNormal = ConfigService.GetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "TreatForcedModsAsNormal")
|
||||
?? throw new NullReferenceException($"{nameof(TreatForcedModsAsNormal)} cannot be loaded.");
|
||||
DisableErrorGUIOverlay = ConfigService.GetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "DisableErrorGUIOverlay")
|
||||
?? throw new NullReferenceException($"{nameof(DisableErrorGUIOverlay)} cannot be loaded.");
|
||||
HideUserNamesInLogs = ConfigService.GetConfig<IConfigEntry<bool>>(ContentPackageManager.VanillaCorePackage, "HideUserNamesInLogs")
|
||||
?? throw new NullReferenceException($"{nameof(HideUserNamesInLogs)} cannot be loaded.");
|
||||
LuaForBarotraumaSteamId = ConfigService.GetConfig<IConfigEntry<ulong>>(ContentPackageManager.VanillaCorePackage, "LuaForBarotraumaSteamId")
|
||||
?? throw new NullReferenceException($"{nameof(LuaForBarotraumaSteamId)} cannot be loaded.");
|
||||
CsForBarotraumaSteamId = ConfigService.GetConfig<IConfigEntry<ulong>>(ContentPackageManager.VanillaCorePackage, "CsForBarotraumaSteamId")
|
||||
?? throw new NullReferenceException($"{nameof(CsForBarotraumaSteamId)} cannot be loaded.");
|
||||
}
|
||||
|
||||
void DisposeLuaCsConfig()
|
||||
{
|
||||
IsCsEnabled = null;
|
||||
TreatForcedModsAsNormal = null;
|
||||
DisableErrorGUIOverlay = null;
|
||||
HideUserNamesInLogs = null;
|
||||
LuaForBarotraumaSteamId = null;
|
||||
CsForBarotraumaSteamId = null;
|
||||
}
|
||||
|
||||
async Task LoadStaticAssetsAsync(IReadOnlyList<ContentPackage> packages)
|
||||
{
|
||||
var locRes = ImmutableArray<ILocalizationResourceInfo>.Empty;
|
||||
var cfgRes = ImmutableArray<IConfigResourceInfo>.Empty;
|
||||
var cfpRes = ImmutableArray<IConfigProfileResourceInfo>.Empty;
|
||||
var luaRes = ImmutableArray<ILuaScriptResourceInfo>.Empty;
|
||||
|
||||
#if CLIENT
|
||||
var styleRes = ImmutableArray<IStylesResourceInfo>.Empty;
|
||||
#endif
|
||||
|
||||
var tasksBuilder = ImmutableArray.CreateBuilder<Task>();
|
||||
|
||||
//---- get resource infos
|
||||
tasksBuilder.AddRange(new Func<Task>(async () =>
|
||||
{
|
||||
var res = await PackageManagementService.GetLocalizationsInfosAsync(packages);
|
||||
if (res.IsSuccess)
|
||||
locRes = res.Value.Localizations;
|
||||
if (res.Errors.Any())
|
||||
ThreadPool.QueueUserWorkItem(state => Logger.LogResults((FluentResults.Result)state),
|
||||
res.ToResult());
|
||||
})(),
|
||||
new Func<Task>(async () =>
|
||||
{
|
||||
var res = await PackageManagementService.GetConfigsInfosAsync(packages);
|
||||
if (res.IsSuccess)
|
||||
cfgRes = res.Value.Configs;
|
||||
if (res.Errors.Any())
|
||||
ThreadPool.QueueUserWorkItem(state => Logger.LogResults((FluentResults.Result)state),
|
||||
res.ToResult());
|
||||
})(),
|
||||
new Func<Task>(async () =>
|
||||
{
|
||||
var res = await PackageManagementService.GetConfigProfilesInfosAsync(packages);
|
||||
if (res.IsSuccess)
|
||||
cfpRes = res.Value.ConfigProfiles;
|
||||
if (res.Errors.Any())
|
||||
ThreadPool.QueueUserWorkItem(state => Logger.LogResults((FluentResults.Result)state),
|
||||
res.ToResult());
|
||||
})(),
|
||||
new Func<Task>(async () =>
|
||||
{
|
||||
var res = await PackageManagementService.GetLuaScriptsInfosAsync(packages);
|
||||
if (res.IsSuccess)
|
||||
luaRes = res.Value.LuaScripts;
|
||||
if (res.Errors.Any())
|
||||
ThreadPool.QueueUserWorkItem(state => Logger.LogResults((FluentResults.Result)state),
|
||||
res.ToResult());
|
||||
})());
|
||||
|
||||
#if CLIENT
|
||||
tasksBuilder.Add(new Func<Task>(async () =>
|
||||
{
|
||||
var res = await PackageManagementService.GetStylesInfosAsync(packages);
|
||||
if (res.IsSuccess)
|
||||
styleRes = res.Value.StylesResourceInfos;
|
||||
if (res.Errors.Any())
|
||||
ThreadPool.QueueUserWorkItem(state => Logger.LogResults((FluentResults.Result)state),
|
||||
res.ToResult());
|
||||
})());
|
||||
#endif
|
||||
await Task.WhenAll(tasksBuilder.MoveToImmutable());
|
||||
tasksBuilder.Clear();
|
||||
|
||||
//---- load resources
|
||||
tasksBuilder.AddRange(new Func<Task>(async () =>
|
||||
{
|
||||
var res = await ConfigService.LoadConfigsAsync(cfgRes);
|
||||
if (res.Errors.Any())
|
||||
Logger.LogResults(res);
|
||||
res = await ConfigService.LoadConfigsProfilesAsync(cfpRes);
|
||||
if (res.Errors.Any())
|
||||
Logger.LogResults(res);
|
||||
})(),
|
||||
new Func<Task>(async () =>
|
||||
{
|
||||
var res = await LuaScriptManagementService.LoadScriptResourcesAsync(luaRes);
|
||||
if (res.Errors.Any())
|
||||
Logger.LogResults(res);
|
||||
})());
|
||||
|
||||
#if CLIENT
|
||||
tasksBuilder.Add(new Func<Task>(async () =>
|
||||
{
|
||||
var res = await StylesManagementService.LoadStylesAsync(styleRes);
|
||||
if (res.Errors.Any())
|
||||
Logger.LogResults(res);
|
||||
})());
|
||||
#endif
|
||||
|
||||
// load localizations first
|
||||
if (!locRes.IsDefaultOrEmpty)
|
||||
{
|
||||
var res = await LocalizationService.LoadLocalizations(locRes);
|
||||
if (res.Errors.Any())
|
||||
Logger.LogResults(res);
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasksBuilder.MoveToImmutable());
|
||||
}
|
||||
|
||||
void RunScripts()
|
||||
{
|
||||
if (!IsStaticAssetsLoaded)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(RunScripts)} cannot load assets in the '{CurrentRunState}' state.");
|
||||
}
|
||||
|
||||
if (CurrentRunState >= RunState.Running)
|
||||
return;
|
||||
|
||||
if (ShouldRunCs())
|
||||
{
|
||||
var asmRes =
|
||||
PackageManagementService.GetAssembliesInfos(PackageManagementService
|
||||
.GetAllLoadedPackages()
|
||||
.Where(ContentPackageManager.EnabledPackages.All.Contains)
|
||||
.ToList());
|
||||
if (asmRes.IsFailed)
|
||||
{
|
||||
Logger.LogError($"{nameof(RunScripts)}: Errors will retrieving assembly resources, cannot load scripts!");
|
||||
Logger.LogResults(asmRes.ToResult());
|
||||
return;
|
||||
}
|
||||
var res = PluginManagementService.LoadAssemblyResources(asmRes.Value.Assemblies);
|
||||
if (res.IsFailed)
|
||||
{
|
||||
Logger.LogError($"{nameof(RunScripts)}: Failed to initialize scripts!");
|
||||
Logger.LogResults(res.ToResult());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (res.Errors.Any())
|
||||
Logger.LogResults(res.ToResult());
|
||||
if (PluginManagementService.GetImplementingTypes<IAssemblyPlugin>() is {IsSuccess: true} types)
|
||||
{
|
||||
var typeInst = PluginManagementService.ActivateTypeInstances<IAssemblyPlugin>(types.Value, true, true);
|
||||
foreach (var loadRes in typeInst)
|
||||
{
|
||||
if (loadRes is { IsSuccess: true, Value: { Item2: { } pluginInstance } })
|
||||
{
|
||||
EventService.Subscribe<IEventPluginPreInitialize>(pluginInstance);
|
||||
EventService.Subscribe<IEventPluginInitialize>(pluginInstance);
|
||||
EventService.Subscribe<IEventPluginLoadCompleted>(pluginInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogResults(loadRes.ToResult());
|
||||
}
|
||||
}
|
||||
|
||||
EventService.PublishEvent<IEventPluginPreInitialize>(sub => sub.PreInitPatching());
|
||||
EventService.PublishEvent<IEventPluginInitialize>(sub => sub.Initialize());
|
||||
EventService.PublishEvent<IEventPluginLoadCompleted>(sub => sub.OnLoadCompleted());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//lua
|
||||
var luaRes = PackageManagementService.GetLuaScriptsInfos(PackageManagementService
|
||||
.GetAllLoadedPackages()
|
||||
.Where(ContentPackageManager.EnabledPackages.All.Contains)
|
||||
.ToList());
|
||||
if (luaRes.IsFailed)
|
||||
{
|
||||
Logger.LogError($"{nameof(RunScripts)}: Failed to get enabled lua script resources!");
|
||||
Logger.LogResults(luaRes.ToResult());
|
||||
return;
|
||||
}
|
||||
|
||||
if (luaRes.Errors.Any())
|
||||
Logger.LogResults(luaRes.ToResult());
|
||||
|
||||
|
||||
LuaScriptManagementService.ExecuteLoadedScripts(luaRes.Value.LuaScripts);
|
||||
|
||||
if (CurrentRunState < RunState.Running)
|
||||
_runState = RunState.Running;
|
||||
}
|
||||
|
||||
void UnloadContentPackageInfos()
|
||||
{
|
||||
if (IsStaticAssetsLoaded)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(UnloadStaticAssets)}: Cannot unload static assets when the current run state is {CurrentRunState}.");
|
||||
}
|
||||
|
||||
PackageManagementService.Reset();
|
||||
_toUnload.Clear();
|
||||
}
|
||||
|
||||
void UnloadStaticAssets()
|
||||
{
|
||||
if (IsCodeRunning)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(UnloadStaticAssets)}: Cannot unload static assets when the current run state is {CurrentRunState}.");
|
||||
}
|
||||
|
||||
PluginManagementService.Reset();
|
||||
LuaScriptManagementService.Reset();
|
||||
ConfigService.Reset();
|
||||
#if CLIENT
|
||||
StylesManagementService.Reset();
|
||||
#endif
|
||||
LocalizationService.Reset();
|
||||
|
||||
if (CurrentRunState >= RunState.Configuration)
|
||||
{
|
||||
_runState = RunState.Parsed;
|
||||
}
|
||||
}
|
||||
|
||||
void StopScripts()
|
||||
{
|
||||
EventService.ClearAllSubscribers();
|
||||
LuaScriptManagementService.UnloadActiveScripts();
|
||||
PluginManagementService.UnloadAllAssemblyResources();
|
||||
SubscribeToLuaCsEvents();
|
||||
|
||||
if (IsCodeRunning)
|
||||
{
|
||||
_runState = RunState.Configuration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the current run state of the LuaCs Modding System.
|
||||
/// <b>[Important]Enum State values ordering must be in the form of (lower state) === (higher state)</b>
|
||||
/// </summary>
|
||||
public enum RunState : byte
|
||||
{
|
||||
Unloaded = 0, // No asset data loaded.
|
||||
Parsed, // CPacks' ResourceInfos are parsed.
|
||||
Configuration, // localization and configuration assets loaded.
|
||||
Running // all assets loaded, code running.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,8 @@ namespace Barotrauma
|
||||
|
||||
foreach (var package in ContentPackageManager.AllPackages)
|
||||
{
|
||||
if (package.UgcId.ValueEquals(LuaCsSetup.LuaForBarotraumaId) && pathStartsWith(getFullPath(package.Path)))
|
||||
if (package.UgcId.ValueEquals(new SteamWorkshopId(GameMain.LuaCs.LuaForBarotraumaSteamId?.Value ?? 0ul))
|
||||
&& pathStartsWith(getFullPath(package.Path)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ public class EventService : IEventService, IEventAssemblyContextUnloading
|
||||
public EventService(Lazy<IPluginManagementService> pluginManagementService)
|
||||
{
|
||||
_pluginManagementService = pluginManagementService ?? throw new ArgumentNullException(nameof(pluginManagementService));
|
||||
this.Subscribe<IEventAssemblyContextUnloading>(this);
|
||||
}
|
||||
|
||||
public bool IsDisposed { get; private set; } = false;
|
||||
@@ -301,8 +302,19 @@ public class EventService : IEventService, IEventAssemblyContextUnloading
|
||||
dict.Remove(OneOf<string, IEvent>.FromT1(subscriber));
|
||||
}
|
||||
|
||||
public void ClearAllEventSubscribers<T>() where T : IEvent => _subscriptions.Remove(typeof(T));
|
||||
public void ClearAllSubscribers() => _subscriptions.Clear();
|
||||
public void ClearAllEventSubscribers<T>() where T : IEvent
|
||||
{
|
||||
_subscriptions.Remove(typeof(T));
|
||||
if (typeof(IEventAssemblyContextUnloading) == typeof(T))
|
||||
{
|
||||
this.Subscribe<IEventAssemblyContextUnloading>(this);
|
||||
}
|
||||
}
|
||||
public void ClearAllSubscribers()
|
||||
{
|
||||
_subscriptions.Clear();
|
||||
this.Subscribe<IEventAssemblyContextUnloading>(this);
|
||||
}
|
||||
|
||||
public FluentResults.Result PublishEvent<T>(Action<T> action) where T : IEvent<T>
|
||||
{
|
||||
|
||||
@@ -151,4 +151,6 @@ public partial class LoggerService : ILoggerService
|
||||
|
||||
public void Dispose() { }
|
||||
public FluentResults.Result Reset() => FluentResults.Result.Ok();
|
||||
|
||||
public bool IsDisposed { get; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using MoonSharp.Interpreter;
|
||||
using MoonSharp.Interpreter.Interop;
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public class LuaScriptManagementService : ILuaScriptManagementService
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using MoonSharp.Interpreter;
|
||||
using MoonSharp.Interpreter.Interop;
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public class LuaScriptService : ILuaScriptService, ILuaScriptManagementService
|
||||
{
|
||||
public void AddField(IUserDataDescriptor descriptor, string fieldName, DynValue value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void AddMethod(IUserDataDescriptor descriptor, string methodName, object function)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public FluentResults.Result AddScriptFiles(ImmutableArray<ILuaResourceInfo> luaResource)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public object CreateEnumTable(string typeName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public object CreateStatic(string typeName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public DynValue CreateUserDataFromDescriptor(DynValue scriptObject, IUserDataDescriptor descriptor)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public DynValue CreateUserDataFromType(DynValue scriptObject, Type desiredType)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public FluentResults.Result ExecuteLoadedScripts(ContentPackage package, bool pauseExecutionOnError = false, bool verboseLogging = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public FluentResults.Result ExecuteLoadedScripts(ImmutableArray<ILuaResourceInfo> scripts, bool pauseExecutionOnError = false, bool verboseLogging = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public FluentResults.Result ExecuteLoadedScripts(bool pauseExecutionOnError = false, bool verboseLogging = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public FluentResults.Result ExecuteScripts(bool pauseExecutionOnScriptError = false, bool verboseLogging = false)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public FieldInfo FindFieldRecursively(Type type, string fieldName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public MethodInfo FindMethodRecursively(Type type, string methodName, Type[] types = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public PropertyInfo FindPropertyRecursively(Type type, string propertyName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ImmutableArray<ILuaResourceInfo> GetScriptResources()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public bool HasMember(object obj, string memberName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsRegistered(Type type)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsTargetType(object obj, string typeName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void MakeFieldAccessible(IUserDataDescriptor descriptor, string fieldName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void MakeMethodAccessible(IUserDataDescriptor descriptor, string methodName, string[] parameters = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void MakePropertyAccessible(IUserDataDescriptor descriptor, string propertyName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IUserDataDescriptor RegisterGenericType(Type type)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IUserDataDescriptor RegisterGenericType(string typeName, params string[] typeNameArgs)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IUserDataDescriptor RegisterType(Type type)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IUserDataDescriptor RegisterType(string typeName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void RemoveMember(IUserDataDescriptor descriptor, string memberName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void RemoveScriptFiles(ImmutableArray<ILuaResourceInfo> luaResource)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public FluentResults.Result Reset()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public string TypeOf(object obj)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void UnregisterAllTypes()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void UnregisterType(Type type)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void UnregisterType(string typeName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
+1
-449
@@ -1,461 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.Steam;
|
||||
using FluentResults;
|
||||
using FluentResults.LuaCs;
|
||||
using QuikGraph;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public class PackageManagementService : IPackageManagementService
|
||||
{
|
||||
private readonly Func<IPackageService> _contentPackageServiceFactory;
|
||||
private readonly Lazy<IAssemblyManagementService> _assemblyManagementService;
|
||||
private readonly ConcurrentDictionary<ContentPackage, IPackageService> _contentPackages = new();
|
||||
private readonly ConcurrentQueue<LoadablePackage> _queuedPackages = new();
|
||||
private readonly ConcurrentDictionary<DependencyEntryKey, IPackageDependencyInfo> _packageDependencyInfos = new();
|
||||
|
||||
/// <summary>
|
||||
/// ConcurrentDictionary handles access/read synchronization. This is to ensure that we are not trying to
|
||||
/// access the collection during a load/unload/modify operation.
|
||||
/// </summary>
|
||||
private readonly ReaderWriterLockSlim _contentPackagesModificationsLock = new();
|
||||
/// <summary>
|
||||
/// This lock ensures that we are not adding new entries to the queue between when we read the contents and
|
||||
/// empty the buffer.
|
||||
/// </summary>
|
||||
private readonly ReaderWriterLockSlim _packageQueueProcessingLock = new();
|
||||
|
||||
public PackageManagementService(
|
||||
Func<IPackageService> getPackageService,
|
||||
Lazy<IAssemblyManagementService> assemblyManagementService)
|
||||
{
|
||||
this._contentPackageServiceFactory = getPackageService;
|
||||
this._assemblyManagementService = assemblyManagementService;
|
||||
}
|
||||
|
||||
#region STATE_RESET
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// TODO release managed resources here
|
||||
}
|
||||
|
||||
public FluentResults.Result Reset()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void QueuePackages(ImmutableArray<LoadablePackage> packages)
|
||||
{
|
||||
_packageQueueProcessingLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
foreach (LoadablePackage package in packages)
|
||||
_queuedPackages.Enqueue(package);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_packageQueueProcessingLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public FluentResults.Result ParseQueuedPackages(bool loadParallel = true, bool reportFailOnDuplicates = false)
|
||||
{
|
||||
if (!ModUtils.Environment.IsMainThread)
|
||||
throw new InvalidOperationException($"{nameof(ParseQueuedPackages)}: This method can only be called on the main thread.");
|
||||
|
||||
ImmutableArray<LoadablePackage> packagesToProcess = ImmutableArray<LoadablePackage>.Empty;
|
||||
|
||||
_packageQueueProcessingLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
Interlocked.MemoryBarrier();
|
||||
if (_queuedPackages.IsEmpty)
|
||||
return FluentResults.Result.Ok().WithSuccess($"{nameof(ParseQueuedPackages)}: The Queue is empty.");
|
||||
packagesToProcess = _queuedPackages.Where(p => p.Package is not null)
|
||||
.Distinct().ToImmutableArray();
|
||||
_queuedPackages.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_packageQueueProcessingLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
FluentResults.Result[] loadResults = new FluentResults.Result[packagesToProcess.Length];
|
||||
FluentResults.Result res = new FluentResults.Result();
|
||||
|
||||
// Load ModConfigInfo
|
||||
_contentPackagesModificationsLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
Stopwatch stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
Interlocked.MemoryBarrier();
|
||||
if (loadParallel)
|
||||
{
|
||||
Parallel.For(0, loadResults.Length, new ParallelOptions()
|
||||
{
|
||||
/*
|
||||
* This is an IO-bound operation. The purpose of parallelism here is to allow loaded package
|
||||
* data to be processed while another package is waiting on the storage device for its info.
|
||||
*/
|
||||
MaxDegreeOfParallelism = 2
|
||||
},i =>
|
||||
{
|
||||
loadResults[i] = LoadPackageInfo(packagesToProcess[i]);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < loadResults.Length; i++)
|
||||
{
|
||||
loadResults[i] = LoadPackageInfo(packagesToProcess[i]);
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
|
||||
res.WithSuccess(new Success(
|
||||
$"Completed parsing of {loadResults.Length} packages in {stopwatch.ElapsedMilliseconds} milliseconds."));
|
||||
|
||||
for (int i = 0; i < loadResults.Length; i++)
|
||||
{
|
||||
res = loadResults[i].IsSuccess
|
||||
? res.WithSuccesses(loadResults[i].Successes)
|
||||
: res.WithErrors(loadResults[i].Errors);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
return FluentResults.Result.Fail(new Error($"{nameof(ParseQueuedPackages)}: Failed to load packages! AE.")
|
||||
.WithMetadata(MetadataType.ExceptionDetails, ae.InnerException?.Message ?? ae.Message)
|
||||
.WithMetadata(MetadataType.StackTrace, ae.StackTrace)
|
||||
.WithMetadata(MetadataType.ExceptionObject, this));
|
||||
}
|
||||
catch (ArgumentNullException ane)
|
||||
{
|
||||
return FluentResults.Result.Fail(
|
||||
new Error($"{nameof(ParseQueuedPackages)}: Failed to load packages! ANE.")
|
||||
.WithMetadata(MetadataType.ExceptionDetails, ane.InnerException?.Message ?? ane.Message)
|
||||
.WithMetadata(MetadataType.StackTrace, ane.StackTrace)
|
||||
.WithMetadata(MetadataType.ExceptionObject, this));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_contentPackagesModificationsLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Helper functions
|
||||
*/
|
||||
|
||||
// register in the list so we can check against it.
|
||||
FluentResults.Result LoadPackageInfo(LoadablePackage package)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (package.Package == null)
|
||||
{
|
||||
return FluentResults.Result.Fail(
|
||||
new Error($"{nameof(LoadPackageInfo)}: Package is null!")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, package));
|
||||
}
|
||||
|
||||
if (_contentPackages.TryGetValue(package.Package, out var packageService))
|
||||
{
|
||||
if (reportFailOnDuplicates)
|
||||
{
|
||||
return FluentResults.Result.Fail(new Error($"The package {package.Package?.Name} is already loaded.")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, package.Package));
|
||||
}
|
||||
|
||||
return FluentResults.Result.Ok();
|
||||
}
|
||||
|
||||
packageService = _contentPackageServiceFactory.Invoke();
|
||||
_contentPackages[package.Package] = packageService;
|
||||
return packageService.LoadResourcesInfo(package);
|
||||
}
|
||||
catch (NullReferenceException nre)
|
||||
{
|
||||
return FluentResults.Result.Fail(new Error($"{nameof(LoadPackageInfo)}: NRE while loading package {package.Package?.Name}!")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.StackTrace, nre.StackTrace ?? "StackTrace not available")
|
||||
.WithMetadata(MetadataType.ExceptionDetails, nre.InnerException?.Message ?? nre.Message)
|
||||
.WithMetadata(MetadataType.RootObject, package));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FluentResults.Result LoadPackageConfigsResourcesGroup(bool loadParallel = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public FluentResults.Result LoadAllPackageResources(bool loadParallel = true, bool safeResourcesOnly = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public FluentResults.Result UnloadPackages()
|
||||
{
|
||||
if (!ModUtils.Environment.IsMainThread)
|
||||
{
|
||||
return FluentResults.Result.Fail(
|
||||
new ExceptionalError(new InvalidOperationException($"{nameof(UnloadPackages)}: This method can only be called on the main thread."))
|
||||
.WithMetadata(MetadataType.ExceptionObject, this));
|
||||
}
|
||||
|
||||
var res = new FluentResults.Result();
|
||||
_contentPackagesModificationsLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
// TODO: Finish him
|
||||
}
|
||||
finally
|
||||
{
|
||||
_contentPackagesModificationsLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsPackageLoaded(ContentPackage package) => package is not null && _contentPackages.ContainsKey(package);
|
||||
|
||||
public bool CheckDependencyLoaded(IPackageDependencyInfo info) =>
|
||||
info is not null && IsPackageLoaded(info.DependencyPackage);
|
||||
|
||||
public bool CheckDependenciesLoaded([NotNull]IEnumerable<IPackageDependencyInfo> infos, out ImmutableArray<IPackageDependencyInfo> missingPackages)
|
||||
{
|
||||
var missing = ImmutableArray.CreateBuilder<IPackageDependencyInfo>();
|
||||
missing.AddRange(infos
|
||||
.Where(i => i.DependencyPackage is not null)
|
||||
.DistinctBy(i => i.DependencyPackage)
|
||||
.Where(i => !CheckDependencyLoaded(i)));
|
||||
missingPackages = missing.MoveToImmutable();
|
||||
return missingPackages.Length == 0;
|
||||
}
|
||||
|
||||
public bool CheckEnvironmentSupported(IPlatformInfo platform)
|
||||
{
|
||||
return (platform.SupportedPlatforms & ModUtils.Environment.CurrentPlatform) > 0
|
||||
&& (platform.SupportedTargets & ModUtils.Environment.CurrentTarget) > 0;
|
||||
}
|
||||
|
||||
public Result<IPackageDependencyInfo> GetPackageDependencyInfoRecord(ContentPackage package, bool addIfMissing = false)
|
||||
{
|
||||
if (package is null)
|
||||
{
|
||||
return new FluentResults.Result<IPackageDependencyInfo>()
|
||||
.WithError(new Error($"{nameof(GetPackageDependencyInfoRecord)}: Package is null!")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this));
|
||||
}
|
||||
|
||||
if (_packageDependencyInfos.TryGetValue(package, out var result))
|
||||
{
|
||||
return new FluentResults.Result<IPackageDependencyInfo>()
|
||||
.WithValue(result);
|
||||
}
|
||||
|
||||
if (addIfMissing)
|
||||
{
|
||||
return AddDependencyRecord(package, package.Name, package.Path,
|
||||
package.TryExtractSteamWorkshopId(out var id) ? id.Value : 0,
|
||||
false);
|
||||
}
|
||||
|
||||
return FluentResults.Result.Fail<IPackageDependencyInfo>(new Error($"Could not find package {package.Name}!")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, package));
|
||||
}
|
||||
|
||||
public Result<IPackageDependencyInfo> GetPackageDependencyInfoRecord(ulong steamWorkshopId, string packageName, string folderPath = null,
|
||||
bool addIfMissing = false)
|
||||
{
|
||||
if (packageName.IsNullOrWhiteSpace() || folderPath.IsNullOrWhiteSpace())
|
||||
{
|
||||
return new FluentResults.Result<IPackageDependencyInfo>()
|
||||
.WithError(new Error($"{nameof(GetPackageDependencyInfoRecord)}: folder path and/or package name are null!")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this));
|
||||
}
|
||||
|
||||
if (_packageDependencyInfos.TryGetValue((packageName,steamWorkshopId,folderPath), out var result))
|
||||
{
|
||||
return new FluentResults.Result<IPackageDependencyInfo>()
|
||||
.WithValue(result);
|
||||
}
|
||||
|
||||
// TODO: Finish this
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result<IPackageDependencyInfo> GetPackageDependencyInfoRecord(string folderPath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
public IPackageDependencyInfo CreateOrphanPackageDependencyInfoRecord(
|
||||
string packageName,
|
||||
string packagePath,
|
||||
ulong steamWorkshopId)
|
||||
{
|
||||
return new DependencyInfo()
|
||||
{
|
||||
DependencyPackage = null,
|
||||
FallbackPackageName = packageName,
|
||||
FolderPath = packagePath.IsNullOrWhiteSpace() ? null : System.IO.Path.GetFullPath(packagePath),
|
||||
SteamWorkshopId = steamWorkshopId,
|
||||
IsMissing = true,
|
||||
IsWorkshopInstallation = false
|
||||
};
|
||||
}
|
||||
|
||||
private Result<IPackageDependencyInfo> AddDependencyRecord(
|
||||
ContentPackage package,
|
||||
string packageName,
|
||||
string folderPath,
|
||||
ulong steamWorkshopId,
|
||||
bool isMissing)
|
||||
{
|
||||
// TODO: Redo
|
||||
try
|
||||
{
|
||||
var dependencyInfo = new DependencyInfo()
|
||||
{
|
||||
DependencyPackage = package,
|
||||
FallbackPackageName = packageName,
|
||||
FolderPath = System.IO.Path.GetFullPath(folderPath),
|
||||
SteamWorkshopId = steamWorkshopId,
|
||||
IsMissing = isMissing,
|
||||
IsWorkshopInstallation = steamWorkshopId != 0
|
||||
};
|
||||
if (package is not null)
|
||||
{
|
||||
_packageDependencyInfos.AddOrUpdate(package, pack => dependencyInfo,
|
||||
(pack, dep) => dependencyInfo);
|
||||
}
|
||||
return new FluentResults.Result<IPackageDependencyInfo>()
|
||||
.WithValue(dependencyInfo)
|
||||
.WithSuccess($"New value created.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new FluentResults.Result<IPackageDependencyInfo>()
|
||||
.WithError(new ExceptionalError(ex)
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.ExceptionDetails, ex.Message)
|
||||
.WithMetadata(MetadataType.RootObject, package)
|
||||
.WithMetadata(MetadataType.StackTrace, ex.StackTrace ?? "StackTrace not available"));
|
||||
}
|
||||
}
|
||||
|
||||
private readonly record struct DependencyEntryKey : IEqualityComparer<DependencyEntryKey>, IEquatable<DependencyEntryKey>
|
||||
{
|
||||
public ContentPackage Package { get; init; }
|
||||
public string FolderPath { get; init; }
|
||||
public string PackageName { get; init; }
|
||||
public ulong SteamWorkshopId { get; init; }
|
||||
|
||||
public DependencyEntryKey(ContentPackage package)
|
||||
{
|
||||
Package = package ?? throw new ArgumentNullException(nameof(package), $"{nameof(DependencyEntryKey)}.ctor: Package cannot be null!");
|
||||
PackageName = package.Name;
|
||||
SteamWorkshopId = package.TryExtractSteamWorkshopId(out var id) ? id.Value : (ulong)0;
|
||||
FolderPath = package.Path;
|
||||
}
|
||||
|
||||
public DependencyEntryKey(string packageName, string folderPath, ulong steamWorkshopId)
|
||||
{
|
||||
PackageName = packageName;
|
||||
SteamWorkshopId = steamWorkshopId;
|
||||
FolderPath = folderPath;
|
||||
Package = null;
|
||||
}
|
||||
|
||||
public DependencyEntryKey(string packageName, ulong steamWorkshopId)
|
||||
{
|
||||
PackageName = packageName;
|
||||
SteamWorkshopId = steamWorkshopId;
|
||||
FolderPath = null;
|
||||
Package = null;
|
||||
}
|
||||
|
||||
public bool Equals(DependencyEntryKey other)
|
||||
{
|
||||
return Equals(this, other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return GetHashCode(this);
|
||||
}
|
||||
|
||||
public bool Equals(DependencyEntryKey x, DependencyEntryKey y)
|
||||
{
|
||||
if (x == y)
|
||||
return true;
|
||||
|
||||
if (x.Package is not null && y.Package is not null && x.Package == Package)
|
||||
return true;
|
||||
|
||||
// folder should be a unique key if not unset.
|
||||
if (!x.FolderPath.IsNullOrWhiteSpace() && !y.FolderPath.IsNullOrWhiteSpace() &&
|
||||
x.FolderPath == FolderPath)
|
||||
return true;
|
||||
|
||||
if (!x.PackageName.IsNullOrWhiteSpace() && !y.PackageName.IsNullOrWhiteSpace()
|
||||
&& x.SteamWorkshopId != 0 && y.SteamWorkshopId != 0)
|
||||
return x.PackageName == y.PackageName && x.SteamWorkshopId == y.SteamWorkshopId;
|
||||
|
||||
if (!x.PackageName.IsNullOrWhiteSpace() && !y.PackageName.IsNullOrWhiteSpace() && x.PackageName == PackageName)
|
||||
return true;
|
||||
|
||||
if (x.SteamWorkshopId != 0 && y.SteamWorkshopId != 0 &&
|
||||
x.SteamWorkshopId == y.SteamWorkshopId)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int GetHashCode(DependencyEntryKey obj)
|
||||
{
|
||||
if (!obj.PackageName.IsNullOrWhiteSpace())
|
||||
return obj.PackageName.GetHashCode();
|
||||
if (obj.SteamWorkshopId != 0)
|
||||
return obj.SteamWorkshopId.GetHashCode();
|
||||
if (obj.Package is not null)
|
||||
return obj.Package.GetHashCode();
|
||||
// We don't want to check the FolderPath because we want to resolve dependencies using packages
|
||||
// that might be local instead in the workshop folder.
|
||||
return 2342568; // random const value: collisions are fine as we want to call Equals()
|
||||
}
|
||||
|
||||
public static implicit operator DependencyEntryKey(ContentPackage package) => new(package);
|
||||
public static implicit operator DependencyEntryKey((string packageName, ulong steamWorkshopId) tuple1) =>
|
||||
new (tuple1.packageName, tuple1.steamWorkshopId);
|
||||
public static implicit operator DependencyEntryKey((string packageName, ulong steamWorkshopId, string folderPath) tuple1) =>
|
||||
new (tuple1.packageName, tuple1.folderPath, tuple1.steamWorkshopId);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,686 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.LuaCs.Services.Processing;
|
||||
using FluentResults;
|
||||
using FluentResults.LuaCs;
|
||||
using OneOf;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public partial class PackageService : IPackageService
|
||||
{
|
||||
private readonly ReaderWriterLockSlim _operationsUsageLock = new();
|
||||
// only stops race conditions for pointer access
|
||||
|
||||
|
||||
// mod config / package scanners/parsers
|
||||
private readonly Lazy<IModConfigCreatorService> _configParserService;
|
||||
private readonly Lazy<ILuaScriptService> _luaScriptService;
|
||||
private readonly Lazy<ILocalizationService> _localizationService;
|
||||
private readonly Lazy<IPluginService> _pluginService;
|
||||
private readonly Lazy<IConfigService> _configService;
|
||||
private readonly IPackageManagementService _packageManagementService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly ILoggerService _loggerService;
|
||||
|
||||
// .ctor in server source and client source
|
||||
|
||||
// state monitors
|
||||
private int _configsLoaded, _localizationsLoaded, _luaScriptsLoaded, _pluginsLoaded, _isDisposed;
|
||||
private int _loadingOperationsRunning;
|
||||
private int _isEnabledInModList;
|
||||
|
||||
public bool ConfigsLoaded
|
||||
{
|
||||
get => ModUtils.Threading.GetBool(ref _configsLoaded);
|
||||
private set => ModUtils.Threading.SetBool(ref _configsLoaded, value);
|
||||
}
|
||||
public bool LocalizationsLoaded
|
||||
{
|
||||
get => ModUtils.Threading.GetBool(ref _localizationsLoaded);
|
||||
private set => ModUtils.Threading.SetBool(ref _localizationsLoaded, value);
|
||||
}
|
||||
public bool LuaScriptsLoaded
|
||||
{
|
||||
get => ModUtils.Threading.GetBool(ref _luaScriptsLoaded);
|
||||
private set => ModUtils.Threading.SetBool(ref _luaScriptsLoaded, value);
|
||||
}
|
||||
public bool PluginsLoaded
|
||||
{
|
||||
get => ModUtils.Threading.GetBool(ref _pluginsLoaded);
|
||||
private set => ModUtils.Threading.SetBool(ref _pluginsLoaded, value);
|
||||
}
|
||||
public bool IsDisposed
|
||||
{
|
||||
get => ModUtils.Threading.GetBool(ref _isDisposed);
|
||||
private set => ModUtils.Threading.SetBool(ref _isDisposed, value);
|
||||
}
|
||||
|
||||
private bool LoadingOperationsRunning
|
||||
{
|
||||
get => Interlocked.CompareExchange(ref _loadingOperationsRunning, 0, 0) > 0;
|
||||
set // we use the set as our inc/decr
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
Interlocked.Add(ref _loadingOperationsRunning, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Interlocked.Add(ref _loadingOperationsRunning, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Member: ContentPackage
|
||||
|
||||
private readonly ReaderWriterLockSlim _packageAccessLock = new();
|
||||
private ContentPackage _package;
|
||||
public ContentPackage Package
|
||||
{
|
||||
get
|
||||
{
|
||||
_packageAccessLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
return _package;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_packageAccessLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
private set
|
||||
{
|
||||
_packageAccessLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
_package = value;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_packageAccessLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DataContracts
|
||||
|
||||
#region Member: ModConfigInfo
|
||||
|
||||
private readonly ReaderWriterLockSlim _modConfigUsageLock = new();
|
||||
private IModConfigInfo _modConfigInfo;
|
||||
public IModConfigInfo ModConfigInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
_modConfigUsageLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
return _modConfigInfo;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_modConfigUsageLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
private set
|
||||
{
|
||||
_modConfigUsageLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
_modConfigInfo = value;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_modConfigUsageLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEnabledInModList
|
||||
{
|
||||
get => ModUtils.Threading.GetBool(ref _isEnabledInModList);
|
||||
private set => ModUtils.Threading.SetBool(ref _isEnabledInModList, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public ImmutableArray<CultureInfo> SupportedCultures => ModConfigInfo?.SupportedCultures ?? ImmutableArray<CultureInfo>.Empty;
|
||||
public ImmutableArray<IAssemblyResourceInfo> Assemblies => ModConfigInfo?.Assemblies ?? ImmutableArray<IAssemblyResourceInfo>.Empty;
|
||||
public ImmutableArray<ILocalizationResourceInfo> Localizations => ModConfigInfo?.Localizations ?? ImmutableArray<ILocalizationResourceInfo>.Empty;
|
||||
public ImmutableArray<ILuaResourceInfo> LuaScripts => ModConfigInfo?.LuaScripts ?? ImmutableArray<ILuaResourceInfo>.Empty;
|
||||
public ImmutableArray<IConfigResourceInfo> Configs => ModConfigInfo?.Configs ?? ImmutableArray<IConfigResourceInfo>.Empty;
|
||||
public ImmutableArray<IConfigProfileResourceInfo> ConfigProfiles => ModConfigInfo?.ConfigProfiles ?? ImmutableArray<IConfigProfileResourceInfo>.Empty;
|
||||
|
||||
#endregion
|
||||
|
||||
#region PublicAPI
|
||||
|
||||
public FluentResults.Result LoadResourcesInfo(LoadablePackage cpackage)
|
||||
{
|
||||
if (cpackage.Package == null)
|
||||
{
|
||||
return FluentResults.Result.Fail(new Error($"{nameof(LoadResourcesInfo)}: Package is null!")
|
||||
.WithMetadata(MetadataType.ExceptionObject,this)
|
||||
.WithMetadata(MetadataType.RootObject, cpackage));
|
||||
}
|
||||
ContentPackage package = cpackage.Package;
|
||||
|
||||
_operationsUsageLock.EnterWriteLock();
|
||||
LoadingOperationsRunning = true;
|
||||
try
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return FluentResults.Result.Fail(
|
||||
new Error("Service is disposed.")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, package));
|
||||
}
|
||||
|
||||
var res = _configParserService.Value.BuildConfigForPackage(package);
|
||||
|
||||
if (res.IsFailed)
|
||||
{
|
||||
return FluentResults.Result.Fail(res.Errors)
|
||||
.WithError(new Error("PackageService failed to load ModConfigInfo")
|
||||
.WithMetadata(MetadataType.ExceptionObject, _configParserService)
|
||||
.WithMetadata(MetadataType.RootObject, package));
|
||||
}
|
||||
|
||||
this.ModConfigInfo = res.Value;
|
||||
this.IsEnabledInModList = cpackage.IsEnabled;
|
||||
return FluentResults.Result.Ok();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return FluentResults.Result.Fail(new Error(e.Message)
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, package)
|
||||
.WithMetadata(MetadataType.StackTrace, e.StackTrace));
|
||||
}
|
||||
finally
|
||||
{
|
||||
LoadingOperationsRunning = false;
|
||||
_operationsUsageLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public FluentResults.Result LoadPlugins([NotNull]IAssembliesResourcesInfo assembliesInfo, bool ignoreDependencySorting = false)
|
||||
{
|
||||
_operationsUsageLock.EnterReadLock();
|
||||
LoadingOperationsRunning = true;
|
||||
try
|
||||
{
|
||||
if (CheckResourceSanitation(OneOf<IAssembliesResourcesInfo, ILocalizationsResourcesInfo,
|
||||
IConfigsResourcesInfo, IConfigProfilesResourcesInfo, ILuaScriptsResourcesInfo>
|
||||
.FromT0(assembliesInfo)) is { IsFailed: true } failed)
|
||||
{
|
||||
return failed;
|
||||
}
|
||||
|
||||
// Order these assemblies by internal dependencies
|
||||
ImmutableArray<IAssemblyResourceInfo> resources;
|
||||
if (ignoreDependencySorting)
|
||||
{
|
||||
resources = assembliesInfo.Assemblies;
|
||||
}
|
||||
else // sort by load order
|
||||
{
|
||||
resources = assembliesInfo.Assemblies
|
||||
.OrderByDescending(a => a.LoadPriority)
|
||||
.ToImmutableArray();
|
||||
}
|
||||
|
||||
// Try loading them, throw on failure.
|
||||
if (_pluginService.Value.LoadAndInstanceTypes<IAssemblyPlugin>(resources, true, out var instancedTypes) is { IsFailed: true} failed2)
|
||||
{
|
||||
return failed2.WithError(new Error($"{nameof(LoadPlugins)}: Failed to load plugins for {this.Package.Name}")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, assembliesInfo));
|
||||
}
|
||||
|
||||
PluginsLoaded = true;
|
||||
return FluentResults.Result.Ok();
|
||||
}
|
||||
finally
|
||||
{
|
||||
LoadingOperationsRunning = false;
|
||||
_operationsUsageLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public FluentResults.Result LoadLocalizations([NotNull]ILocalizationsResourcesInfo localizationsInfo)
|
||||
{
|
||||
_operationsUsageLock.EnterReadLock();
|
||||
LoadingOperationsRunning = true;
|
||||
try
|
||||
{
|
||||
if (CheckResourceSanitation(OneOf<IAssembliesResourcesInfo, ILocalizationsResourcesInfo,
|
||||
IConfigsResourcesInfo, IConfigProfilesResourcesInfo, ILuaScriptsResourcesInfo>
|
||||
.FromT1(localizationsInfo)) is { IsFailed: true } failed)
|
||||
{
|
||||
return failed;
|
||||
}
|
||||
|
||||
if (_localizationService.Value.LoadLocalizations(localizationsInfo.Localizations) is { IsFailed: true} failed2)
|
||||
{
|
||||
return failed2.WithError(new Error($"{nameof(LoadLocalizations)}: Failed to load localizations")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, localizationsInfo));
|
||||
}
|
||||
|
||||
LocalizationsLoaded = true;
|
||||
return FluentResults.Result.Ok();
|
||||
}
|
||||
finally
|
||||
{
|
||||
LoadingOperationsRunning = false;
|
||||
_operationsUsageLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public FluentResults.Result AddLuaScripts([NotNull]ILuaScriptsResourcesInfo luaScriptsInfo)
|
||||
{
|
||||
_operationsUsageLock.EnterReadLock();
|
||||
LoadingOperationsRunning = true;
|
||||
try
|
||||
{
|
||||
if (CheckResourceSanitation(OneOf<IAssembliesResourcesInfo, ILocalizationsResourcesInfo,
|
||||
IConfigsResourcesInfo, IConfigProfilesResourcesInfo, ILuaScriptsResourcesInfo>
|
||||
.FromT4(luaScriptsInfo)) is { IsFailed: true } failed)
|
||||
{
|
||||
return failed;
|
||||
}
|
||||
|
||||
if (_luaScriptService.Value.AddScriptFiles(luaScriptsInfo.LuaScripts) is { IsFailed: true} failed2)
|
||||
{
|
||||
return failed2.WithError(new Error($"{nameof(LoadLocalizations)}: Failed to load lua scripts.")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, luaScriptsInfo));
|
||||
}
|
||||
|
||||
LuaScriptsLoaded = true;
|
||||
return FluentResults.Result.Ok();
|
||||
}
|
||||
finally
|
||||
{
|
||||
LoadingOperationsRunning = false;
|
||||
_operationsUsageLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public FluentResults.Result LoadConfig(
|
||||
[NotNull]IConfigsResourcesInfo configsResourcesInfo,
|
||||
[NotNull]IConfigProfilesResourcesInfo configProfilesResourcesInfo)
|
||||
{
|
||||
_operationsUsageLock.EnterReadLock();
|
||||
LoadingOperationsRunning = true;
|
||||
try
|
||||
{
|
||||
// register configs
|
||||
if (CheckResourceSanitation(OneOf<IAssembliesResourcesInfo, ILocalizationsResourcesInfo,
|
||||
IConfigsResourcesInfo, IConfigProfilesResourcesInfo, ILuaScriptsResourcesInfo>
|
||||
.FromT2(configsResourcesInfo)) is { IsFailed: true } failed)
|
||||
{
|
||||
return failed;
|
||||
}
|
||||
|
||||
if (_configService.Value.AddConfigs(configsResourcesInfo.Configs) is { IsFailed: true} failed2)
|
||||
{
|
||||
return failed2.WithError(new Error($"{nameof(LoadLocalizations)}: Failed to load configs.")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, configsResourcesInfo));
|
||||
}
|
||||
|
||||
// register config profiles
|
||||
if (CheckResourceSanitation(OneOf<IAssembliesResourcesInfo, ILocalizationsResourcesInfo,
|
||||
IConfigsResourcesInfo, IConfigProfilesResourcesInfo, ILuaScriptsResourcesInfo>
|
||||
.FromT3(configProfilesResourcesInfo)) is { IsFailed: true } failed3)
|
||||
{
|
||||
return failed3;
|
||||
}
|
||||
|
||||
if (_configService.Value.AddConfigsProfiles(configProfilesResourcesInfo.ConfigProfiles) is { IsFailed: true} failed4)
|
||||
{
|
||||
return failed4.WithError(new Error($"{nameof(LoadLocalizations)}: Failed to load config profiles.")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, configProfilesResourcesInfo));
|
||||
}
|
||||
|
||||
ConfigsLoaded = true;
|
||||
return FluentResults.Result.Ok();
|
||||
}
|
||||
finally
|
||||
{
|
||||
LoadingOperationsRunning = false;
|
||||
_operationsUsageLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
/*
|
||||
* Notes: we need to unload this package from services in the order that the services are dependent on each other.
|
||||
* Unloading Order: Lua Scripts > Assemblies > Config Profiles > Configs > Styles > Localizations
|
||||
*/
|
||||
_operationsUsageLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
if (this.Package is null)
|
||||
{
|
||||
_loggerService.LogError(
|
||||
$"Package Service: cannot Dispose of service as ContentPackage and info is not set!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.ModConfigInfo is null)
|
||||
{
|
||||
_loggerService.LogError($"Package Service: cannot Dispose of service as ModConfigInfo is not loaded!");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* To be graceful, we want to ensure that any async calls and other threads are allowed to be processed before we begin
|
||||
* disposal to reduce friction with other thread operations, so we release the lock and periodically check it
|
||||
* to see of other threads have finished operations before cleaning everything up.
|
||||
*/
|
||||
|
||||
IsDisposed = true; // set stop flag, callers should handle exception cases
|
||||
Interlocked.MemoryBarrier(); //ensure cache states
|
||||
|
||||
DateTime timeoutLimit = DateTime.Now.AddSeconds(10);
|
||||
while (LoadingOperationsRunning)
|
||||
{
|
||||
_operationsUsageLock.ExitWriteLock();
|
||||
Thread.Sleep(1);
|
||||
_operationsUsageLock.EnterWriteLock();
|
||||
if (timeoutLimit < DateTime.Now)
|
||||
{
|
||||
_loggerService.LogError($"Package Service: Dispose() time out reached while waiting for other operations. Continuing.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
_luaScriptService.Value.RemoveScriptFiles(this.LuaScripts);
|
||||
_pluginService.Value.DisposePlugins();
|
||||
_configService.Value.RemoveConfigsProfiles(this.ConfigProfiles);
|
||||
_configService.Value.RemoveConfigs(this.Configs);
|
||||
#if CLIENT
|
||||
_stylesService.Value.UnloadAllStyles();
|
||||
#endif
|
||||
_localizationService.Value.Remove(this.Localizations);
|
||||
|
||||
ModConfigInfo = null;
|
||||
Package = null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
_loggerService.LogError($"Package Service: exception while running Dispose().");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_operationsUsageLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public FluentResults.Result Reset()
|
||||
{
|
||||
_operationsUsageLock.EnterWriteLock();
|
||||
|
||||
try
|
||||
{
|
||||
if (this.Package is null)
|
||||
{
|
||||
return FluentResults.Result.Fail(new Error($"Package Service: cannot Dispose of service as ContentPackage and info is not set!")
|
||||
.WithMetadata(MetadataType.ExceptionDetails, nameof(Reset))
|
||||
.WithMetadata(MetadataType.ExceptionObject, this));
|
||||
}
|
||||
|
||||
if (this.ModConfigInfo is null)
|
||||
{
|
||||
return FluentResults.Result.Fail(new Error($"Package Service: cannot Dispose of service as ModConfigInfo is not set!")
|
||||
.WithMetadata(MetadataType.ExceptionDetails, nameof(Reset))
|
||||
.WithMetadata(MetadataType.ExceptionObject, this));
|
||||
}
|
||||
|
||||
Interlocked.MemoryBarrier(); //ensure cache states
|
||||
|
||||
DateTime timeoutLimit = DateTime.Now.AddSeconds(10);
|
||||
while (LoadingOperationsRunning)
|
||||
{
|
||||
_operationsUsageLock.ExitWriteLock();
|
||||
Thread.Sleep(1);
|
||||
_operationsUsageLock.EnterWriteLock();
|
||||
if (timeoutLimit < DateTime.Now)
|
||||
{
|
||||
_loggerService.LogError($"Package Service: Dispose() grace time-out reached while waiting for other operations. Continuing.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (LuaScriptsLoaded)
|
||||
{
|
||||
_luaScriptService.Value.RemoveScriptFiles(this.LuaScripts);
|
||||
LuaScriptsLoaded = false;
|
||||
}
|
||||
|
||||
if (PluginsLoaded)
|
||||
{
|
||||
_pluginService.Value.DisposePlugins();
|
||||
PluginsLoaded = false;
|
||||
}
|
||||
|
||||
if (ConfigsLoaded)
|
||||
{
|
||||
_configService.Value.RemoveConfigsProfiles(this.ConfigProfiles);
|
||||
_configService.Value.RemoveConfigs(this.Configs);
|
||||
ConfigsLoaded = false;
|
||||
}
|
||||
|
||||
if (LocalizationsLoaded)
|
||||
{
|
||||
_localizationService.Value.Remove(this.Localizations);
|
||||
LocalizationsLoaded = false;
|
||||
}
|
||||
return FluentResults.Result.Ok();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_operationsUsageLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INTERNAL
|
||||
|
||||
/// <summary>
|
||||
/// [Thread Unsafe] Performs sanitation and null checks on resources and returns the results.
|
||||
/// NOTE: Requires that resource locks be set by the caller.
|
||||
/// </summary>
|
||||
/// <param name="resourcesInfos"></param>
|
||||
/// <returns></returns>
|
||||
private FluentResults.Result CheckResourceSanitation(
|
||||
OneOf.OneOf<IAssembliesResourcesInfo, ILocalizationsResourcesInfo,
|
||||
IConfigsResourcesInfo, IConfigProfilesResourcesInfo, ILuaScriptsResourcesInfo> resourcesInfos)
|
||||
{
|
||||
// execute checks based on known types
|
||||
return resourcesInfos.Match<FluentResults.Result>(
|
||||
ass => ChecksDispatcher(ass, nameof(ass.Assemblies), nameof(LoadPlugins),
|
||||
ass.Assemblies, this.Assemblies),
|
||||
loc => ChecksDispatcher(loc, nameof(loc.Localizations), nameof(LoadLocalizations),
|
||||
loc.Localizations, this.Localizations),
|
||||
cfg => ChecksDispatcher(cfg, nameof(cfg.Configs), nameof(LoadConfig),
|
||||
cfg.Configs, this.Configs),
|
||||
cfp => ChecksDispatcher(cfp, nameof(cfp.ConfigProfiles), nameof(LoadConfig),
|
||||
cfp.ConfigProfiles, this.ConfigProfiles),
|
||||
lua => ChecksDispatcher(lua, nameof(lua.LuaScripts), nameof(AddLuaScripts),
|
||||
lua.LuaScripts, this.LuaScripts));
|
||||
|
||||
|
||||
/*
|
||||
* Helper functions
|
||||
*/
|
||||
FluentResults.Result ChecksDispatcher<T>(object obj, string resName, string callerName,
|
||||
ImmutableArray<T> resList, ImmutableArray<T> compareList)
|
||||
where T : class, IPackageInfo, IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo
|
||||
{
|
||||
string errMsg = $"{callerName}: Failed to load {resName}.";
|
||||
if (DisposeCheck(obj) is { IsFailed: true } failed)
|
||||
return failed;
|
||||
if (SanitationChecksCore(obj, resName, callerName) is { IsFailed: true } failed1)
|
||||
return failed1.WithError(new Error(errMsg));
|
||||
if (SanitationChecksEnumerable(resList, resName, callerName) is { IsFailed: true } failed2)
|
||||
return failed2.WithError(new Error(errMsg));
|
||||
if (DebugCheck(resList, compareList, resName) is {IsFailed: true} failed3)
|
||||
return failed3.WithError(new Error(errMsg));
|
||||
return FluentResults.Result.Ok();
|
||||
}
|
||||
|
||||
FluentResults.Result DisposeCheck(object obj)
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return FluentResults.Result.Fail(new Error($"{nameof(PackageService)}: Tried to load resources when disposed.")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, obj));
|
||||
}
|
||||
return FluentResults.Result.Ok();
|
||||
}
|
||||
|
||||
FluentResults.Result DebugCheck<T>(ImmutableArray<T> resList, ImmutableArray<T> compareList, string resName)
|
||||
where T : class, IPackageInfo
|
||||
{
|
||||
#if DEBUG
|
||||
Stack<Error> errors = new();
|
||||
resList.ForEach(res =>
|
||||
{
|
||||
if (!compareList.Contains(res))
|
||||
{
|
||||
errors.Push(new Error($"Failed to load {resName} for: {this.Package.Name}")
|
||||
.WithMetadata(MetadataType.ExceptionDetails, $"Tries to load {resName} resource {res.InternalName} but it is not from this package!")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, res));
|
||||
}
|
||||
});
|
||||
if (errors.Count > 0)
|
||||
{
|
||||
return FluentResults.Result.Fail(errors).WithError(
|
||||
new Error($"{nameof(LoadPlugins)}: errors in {resName} resources.")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, this.Package));
|
||||
}
|
||||
#endif
|
||||
return FluentResults.Result.Ok();
|
||||
}
|
||||
}
|
||||
|
||||
private FluentResults.Result SanitationChecksCore(object obj, string resTypeInfoName, string callerName)
|
||||
{
|
||||
Error e = null;
|
||||
|
||||
if (obj is null)
|
||||
{
|
||||
e = new Error($"{nameof(SanitationChecksCore)}: null checks failed!")
|
||||
.WithMetadata(MetadataType.ExceptionDetails, "Object is null!")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.Sources, new List<string>() { resTypeInfoName, callerName });
|
||||
}
|
||||
|
||||
if (this.Package is null)
|
||||
{
|
||||
e = (e ?? new Error($"{nameof(SanitationChecksCore)}: null checks failed!"))
|
||||
.WithMetadata(MetadataType.ExceptionDetails, "The Package is null!")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.Sources, new List<string>() { resTypeInfoName, callerName });
|
||||
}
|
||||
|
||||
return e is null ? FluentResults.Result.Ok() : FluentResults.Result.Fail(e);
|
||||
}
|
||||
|
||||
private FluentResults.Result SanitationChecksEnumerable<T>(ImmutableArray<T> resourceInfos, string resTypeInfoName, string callerName) where T : IResourceInfo, IResourceCultureInfo, IPackageInfo, IPackageDependenciesInfo
|
||||
{
|
||||
// Check if list is empty. Nothing more to do.
|
||||
if (resourceInfos.IsDefaultOrEmpty)
|
||||
return FluentResults.Result.Ok();
|
||||
|
||||
Stack<Error> errors = new();
|
||||
|
||||
// Check if all resources in the list are registered to this package, throw if not.
|
||||
foreach (var resourceInfo in resourceInfos)
|
||||
{
|
||||
// ownership checks
|
||||
if (resourceInfo.OwnerPackage is null)
|
||||
{
|
||||
errors.Push(new Error($"Error for resource: {resTypeInfoName}. OwnerPackage is null!")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, resourceInfo));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resourceInfo.OwnerPackage != this.Package)
|
||||
{
|
||||
errors.Push(new Error($"Error for resource: {resTypeInfoName}. $\"OwnerPackage {{resourceInfo.OwnerPackage?.Name}} is not the same as this package: {{this.Package}}")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, resourceInfo));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resourceInfo.Dependencies.IsDefaultOrEmpty)
|
||||
continue;
|
||||
|
||||
// ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
|
||||
foreach (var pdi in resourceInfo.Dependencies)
|
||||
{
|
||||
// for clarification: all resources passed to the function should always be loaded.
|
||||
// unneeded optional resources should be filtered out before the list is sent.
|
||||
// left this as a reminder :)
|
||||
/*if (pdi.Optional)
|
||||
return;*/
|
||||
if (!_packageManagementService.CheckDependencyLoaded(pdi))
|
||||
{
|
||||
errors.Push(new Error($"Dependency missing for resource: {resourceInfo.OwnerPackage.Name}")
|
||||
.WithMetadata(MetadataType.ExceptionDetails, $"Missing dependency: {pdi.DependencyPackage?.Name ?? (pdi.FallbackPackageName.IsNullOrWhiteSpace() ? pdi.SteamWorkshopId.ToString() : pdi.FallbackPackageName)}")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, resourceInfo));
|
||||
}
|
||||
}
|
||||
|
||||
// check runtime platform
|
||||
if (!_packageManagementService.CheckEnvironmentSupported(resourceInfo))
|
||||
{
|
||||
errors.Push(new Error($"The resource {resourceInfo.OwnerPackage?.Name} does not support the current platform!")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, resourceInfo));
|
||||
}
|
||||
|
||||
// check local culture
|
||||
if (!_localizationService.Value.IsCurrentCultureSupported(resourceInfo))
|
||||
{
|
||||
errors.Push(new Error($"The resource {resourceInfo.OwnerPackage?.Name} does not support the current culture/region!")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, resourceInfo));
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Count > 0 ? FluentResults.Result.Fail(errors) : FluentResults.Result.Ok();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -107,6 +107,22 @@ public class PluginManagementService : IPluginManagementService, IAssemblyManage
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IReadOnlyList<Result<(Type, T)>> ActivateTypeInstances<T>(ImmutableArray<Type> types, bool serviceInjection = true,
|
||||
bool hostInstanceReference = false) where T : IDisposable
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public FluentResults.Result UnloadHostedReferences()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public FluentResults.Result UnloadAllAssemblyResources()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result<Assembly> GetLoadedAssembly(string assemblyName, in Guid[] excludedContexts)
|
||||
{
|
||||
((IService)this).CheckDisposed();
|
||||
|
||||
+7
-36
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using FluentResults;
|
||||
@@ -7,46 +8,16 @@ namespace Barotrauma.LuaCs.Services.Processing;
|
||||
|
||||
#region TypeDef
|
||||
|
||||
// ReSharper disable once TypeParameterCanBeVariant
|
||||
public interface IConverterService<TSrc, TOut> : IReusableService
|
||||
public interface IConverterService<in TSrc, TOut> : IReusableService
|
||||
{
|
||||
Result<TOut> TryParseResource(TSrc src);
|
||||
Result<TOut> TryParseResources(IEnumerable<TSrc> sources);
|
||||
}
|
||||
|
||||
public interface IXmlResourceConverterService<TOut> : IConverterService<XElement, TOut> { }
|
||||
public interface IResourceToXmlConverterService<TSrc> : IConverterService<TSrc, XElement> { }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Parses Xml to produce loading metadata info for linked loadable files.
|
||||
/// </summary>
|
||||
#region XmlToResourceInfoParsers
|
||||
|
||||
public interface IXmlAssemblyResConverter : IXmlResourceConverterService<IAssemblyResourceInfo> { }
|
||||
public interface IXmlConfigResConverterService : IXmlResourceConverterService<IConfigResourceInfo> { }
|
||||
public interface IXmlLocalizationResConverterService : IXmlResourceConverterService<ILocalizationResourceInfo> { }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Parses Xml to produce ready-to-use info/data without any additional file/data loading.
|
||||
/// </summary>
|
||||
#region XmlToInfoParsers
|
||||
public interface IXmlDependencyConverterService : IXmlResourceConverterService<IPackageDependencyInfo> { }
|
||||
public interface IXmlModConfigConverterService : IXmlResourceConverterService<IModConfigInfo> { }
|
||||
/// <summary>
|
||||
/// Parses legacy packages that make use of the RunConfig.xml structure to produce a ModConfig.
|
||||
/// </summary>
|
||||
public interface IXmlLegacyModConfigConverterService : IXmlResourceConverterService<IModConfigInfo> { }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region ResToInfoParsers
|
||||
public interface ILocalizationResToInfoParser : IConverterService<ILocalizationResourceInfo, ILocalizationInfo> { }
|
||||
public interface IConfigResConverterService : IConverterService<IConfigResourceInfo, IConfigInfo> { }
|
||||
public interface IConfigProfileResConverterService : IConverterService<IConfigProfileResourceInfo, IConfigProfileInfo> { }
|
||||
public interface IConverterServiceAsync<in TSrc, TOut> : IReusableService
|
||||
{
|
||||
Task<Result<TOut>> TryParseResourceAsync(TSrc src);
|
||||
Task<Result<TOut>> TryParseResourcesAsync(IEnumerable<TSrc> sources);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -33,26 +33,25 @@ public class StorageService : IStorageService
|
||||
private IConfigEntry<string> _kLocalFilePathRules = null;
|
||||
private const string _packagePathKeyword = "<PACKNAME>";
|
||||
private readonly string _runLocation = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location.CleanUpPath());
|
||||
|
||||
// TODO: Rewrite the config to get info from .ctor.
|
||||
private IConfigEntry<string> LocalStoragePath => _kLocalStoragePath ??= GetOrCreateConfig(nameof(LocalStoragePath), "/Data/Mods");
|
||||
private IConfigEntry<string> LocalFilePathRule => _kLocalFilePathRules ??= GetOrCreateConfig(nameof(LocalFilePathRule), _packagePathKeyword);
|
||||
private IConfigEntry<string> GetOrCreateConfig(string name, string defaultValue)
|
||||
{
|
||||
var c = _configService.Value
|
||||
.GetConfig<IConfigEntry<string>>(ModUtils.Definitions.LuaCsForBarotrauma, name);
|
||||
if (c.IsSuccess)
|
||||
{
|
||||
return c.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = _configService.Value.AddConfigEntry(
|
||||
ModUtils.Definitions.LuaCsForBarotrauma,
|
||||
name, defaultValue, NetSync.None, valueChangePredicate: (value) => false);
|
||||
if (c.IsSuccess)
|
||||
return c.Value;
|
||||
else
|
||||
throw new KeyNotFoundException("Cannot find storage value for key: " + name);
|
||||
}
|
||||
if (c is not null)
|
||||
return c;
|
||||
|
||||
var c1 = _configService.Value.AddConfigEntry(
|
||||
ModUtils.Definitions.LuaCsForBarotrauma,
|
||||
name, defaultValue, NetSync.None, valueChangePredicate: (value) => false);
|
||||
if (c1.IsSuccess)
|
||||
return c1.Value;
|
||||
|
||||
throw new KeyNotFoundException("Cannot find storage value for key: " + name);
|
||||
|
||||
}
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
|
||||
+9
-16
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.LuaCs.Configuration;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.LuaCs.Networking;
|
||||
@@ -15,20 +16,14 @@ public partial interface IConfigService : IReusableService, ILuaConfigService
|
||||
/*
|
||||
* Resource Files.
|
||||
*/
|
||||
FluentResults.Result AddConfigs(ImmutableArray<IConfigResourceInfo> configResources);
|
||||
FluentResults.Result AddConfigsProfiles(ImmutableArray<IConfigProfileResourceInfo> configProfileResources);
|
||||
FluentResults.Result RemoveConfigs(ImmutableArray<IConfigResourceInfo> configResources);
|
||||
FluentResults.Result RemoveConfigsProfiles(ImmutableArray<IConfigProfileResourceInfo> configProfilesResources);
|
||||
Task<FluentResults.Result> LoadConfigsAsync(ImmutableArray<IConfigResourceInfo> configResources);
|
||||
Task<FluentResults.Result> LoadConfigsProfilesAsync(ImmutableArray<IConfigProfileResourceInfo> configProfileResources);
|
||||
FluentResults.Result DisposeConfigs(ImmutableArray<IConfigResourceInfo> configResources);
|
||||
FluentResults.Result DisposeConfigsProfiles(ImmutableArray<IConfigProfileResourceInfo> configProfilesResources);
|
||||
FluentResults.Result DisposeConfigs(ContentPackage package);
|
||||
FluentResults.Result DisposeConfigsProfiles(ContentPackage package);
|
||||
|
||||
|
||||
/*
|
||||
* From resources
|
||||
*/
|
||||
FluentResults.Result AddConfigs(ImmutableArray<IConfigInfo> configs);
|
||||
FluentResults.Result AddConfigsProfiles(ImmutableArray<IConfigProfileInfo> configProfiles);
|
||||
FluentResults.Result RemoveConfigs(ImmutableArray<IConfigInfo> configs);
|
||||
FluentResults.Result RemoveConfigsProfiles(ImmutableArray<IConfigProfileInfo> configProfiles);
|
||||
|
||||
/*
|
||||
* Immediate mode
|
||||
*/
|
||||
@@ -79,8 +74,6 @@ public partial interface IConfigService : IReusableService, ILuaConfigService
|
||||
FluentResults.Result<IReadOnlyDictionary<string, IConfigBase>> GetConfigsForPackage(ContentPackage package);
|
||||
FluentResults.Result<IReadOnlyDictionary<string, IConfigBase>> GetConfigsForPackage(string packageName);
|
||||
IReadOnlyDictionary<(ContentPackage, string), IConfigBase> GetAllConfigs();
|
||||
FluentResults.Result<IConfigBase> GetConfig(ContentPackage package, string name);
|
||||
FluentResults.Result<IConfigBase> GetConfig(string packageName, string name);
|
||||
FluentResults.Result<T> GetConfig<T>(ContentPackage package, string name) where T : IConfigBase;
|
||||
FluentResults.Result<T> GetConfig<T>(string packageName, string name) where T : IConfigBase;
|
||||
T GetConfig<T>(ContentPackage package, string name) where T : IConfigBase;
|
||||
T GetConfig<T>(string packageName, string name) where T : IConfigBase;
|
||||
}
|
||||
|
||||
+3
-1
@@ -2,6 +2,7 @@
|
||||
using System.Globalization;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
@@ -10,9 +11,10 @@ public interface ILocalizationService : IReusableService
|
||||
{
|
||||
IReadOnlyCollection<CultureInfo> GetLoadedLocales();
|
||||
void Remove(ImmutableArray<ILocalizationResourceInfo> localizations);
|
||||
void DisposePackage(ContentPackage package);
|
||||
FluentResults.Result SetCurrentCulture(CultureInfo culture);
|
||||
FluentResults.Result SetCurrentCulture(string cultureName);
|
||||
FluentResults.Result LoadLocalizations(ImmutableArray<ILocalizationResourceInfo> localizationResources);
|
||||
Task<FluentResults.Result> LoadLocalizations(ImmutableArray<ILocalizationResourceInfo> localizationResources);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a localized string without a fallback. Returns success/failure and associated data.
|
||||
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using MoonSharp.Interpreter;
|
||||
using MoonSharp.Interpreter.Interop;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface ILuaScriptManagementService : IReusableService
|
||||
{
|
||||
#region Script_Ops
|
||||
|
||||
Task<FluentResults.Result> LoadScriptResourcesAsync(ImmutableArray<ILuaScriptResourceInfo> resourcesInfo);
|
||||
|
||||
FluentResults.Result ExecuteLoadedScripts(ContentPackage package, bool pauseExecutionOnError = false, bool verboseLogging = false);
|
||||
FluentResults.Result ExecuteLoadedScripts(ImmutableArray<ILuaScriptResourceInfo> scripts, bool pauseExecutionOnError = false, bool verboseLogging = false);
|
||||
FluentResults.Result ExecuteLoadedScripts(bool pauseExecutionOnError = false, bool verboseLogging = false);
|
||||
FluentResults.Result DisposePackageResources(ContentPackage package);
|
||||
FluentResults.Result UnloadActiveScripts();
|
||||
FluentResults.Result DisposeAllPackageResources();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Type_Registration
|
||||
|
||||
IUserDataDescriptor RegisterType(Type type);
|
||||
/// <summary>
|
||||
/// <b>[Deprecated]</b><br/>
|
||||
/// Use <see cref="GetTypeInfo"/>() instead.
|
||||
/// Gets the type information for an already registered type.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The fully qualified name of the type and namespace.</param>
|
||||
/// <returns>The <see cref="IUserDataDescriptor"/> for the type, if registered. Null if none is found.</returns>
|
||||
[Obsolete($"Use {nameof(GetTypeInfo)} instead.")]
|
||||
IUserDataDescriptor RegisterType(string typeName) => GetTypeInfo(typeName);
|
||||
IUserDataDescriptor RegisterGenericType(Type type);
|
||||
/// <summary>
|
||||
/// <b>[Deprecated]</b><br/>
|
||||
/// Use <see cref="GetTypeInfo"/>() instead.
|
||||
/// Gets the generic type information for an already registered type.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The fully qualified name of the generic type and namespace.</param>
|
||||
/// <param name="typeNameArgs">The fully qualified name of the template types.</param>
|
||||
/// <returns>The <see cref="IUserDataDescriptor"/> for the type, if registered. Null if none is found.</returns>
|
||||
[Obsolete($"Use {nameof(GetGenericTypeInfo)} instead.")]
|
||||
IUserDataDescriptor RegisterGenericType(string typeName, params string[] typeNameArgs) => GetGenericTypeInfo(typeName, typeNameArgs);
|
||||
/// <summary>
|
||||
/// Gets the type information for an already registered type.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The fully qualified name of the type and namespace.</param>
|
||||
/// <returns>The <see cref="IUserDataDescriptor"/> for the type, if registered. Null if none is found.</returns>
|
||||
IUserDataDescriptor GetTypeInfo(string typeName);
|
||||
/// <summary>
|
||||
/// Gets the generic type information for an already registered type.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The fully qualified name of the generic type and namespace.</param>
|
||||
/// <param name="typeNameArgs">The fully qualified name of the template types.</param>
|
||||
/// <returns>The <see cref="IUserDataDescriptor"/> for the type, if registered. Null if none is found.</returns>
|
||||
IUserDataDescriptor GetGenericTypeInfo(string typeName, params string[] typeNameArgs);
|
||||
void UnregisterType(Type type);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Type_Checks_&Utilities
|
||||
|
||||
bool IsRegistered(Type type);
|
||||
bool IsTargetType(object obj, string typeName);
|
||||
string TypeOf(object obj);
|
||||
object CreateStatic(string typeName);
|
||||
object CreateEnumTable(string typeName);
|
||||
FieldInfo FindFieldRecursively(Type type, string fieldName);
|
||||
void MakeFieldAccessible(IUserDataDescriptor descriptor, string fieldName);
|
||||
MethodInfo FindMethodRecursively(Type type, string methodName, Type[] types = null);
|
||||
void MakeMethodAccessible(IUserDataDescriptor descriptor, string methodName, string[] parameters = null);
|
||||
PropertyInfo FindPropertyRecursively(Type type, string propertyName);
|
||||
void MakePropertyAccessible(IUserDataDescriptor descriptor, string propertyName);
|
||||
void AddMethod(IUserDataDescriptor descriptor, string methodName, object function);
|
||||
void AddField(IUserDataDescriptor descriptor, string fieldName, DynValue value);
|
||||
void RemoveMember(IUserDataDescriptor descriptor, string memberName);
|
||||
bool HasMember(object obj, string memberName);
|
||||
DynValue CreateUserDataFromDescriptor(DynValue scriptObject, IUserDataDescriptor descriptor);
|
||||
DynValue CreateUserDataFromType(DynValue scriptObject, Type desiredType);
|
||||
|
||||
#endregion
|
||||
}
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Reflection;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using MoonSharp.Interpreter;
|
||||
using MoonSharp.Interpreter.Interop;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface ILuaScriptService : IReusableService
|
||||
{
|
||||
#region Script_File_Collector
|
||||
|
||||
/// <summary>
|
||||
/// Adds the script files to the runner but does not execute them.
|
||||
/// </summary>
|
||||
/// <param name="luaResource"></param>
|
||||
/// <returns></returns>
|
||||
FluentResults.Result AddScriptFiles(ImmutableArray<ILuaResourceInfo> luaResource);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specific resources from the script runner. Important: Does not stop the
|
||||
/// execution of any code related to the files nor guarantee cleanup of resources!
|
||||
/// </summary>
|
||||
/// <param name="luaResource"></param>
|
||||
void RemoveScriptFiles(ImmutableArray<ILuaResourceInfo> luaResource);
|
||||
|
||||
/// <summary>
|
||||
/// Executes loaded script files on the management service.
|
||||
/// </summary>
|
||||
/// <param name="pauseExecutionOnScriptError"></param>
|
||||
/// <param name="verboseLogging"></param>
|
||||
/// <returns></returns>
|
||||
FluentResults.Result ExecuteScripts(bool pauseExecutionOnScriptError = false, bool verboseLogging = false);
|
||||
|
||||
ImmutableArray<ILuaResourceInfo> GetScriptResources();
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public interface ILuaScriptManagementService : IReusableService
|
||||
{
|
||||
#region Script_File_Execution
|
||||
|
||||
FluentResults.Result ExecuteLoadedScripts(ContentPackage package, bool pauseExecutionOnError = false, bool verboseLogging = false);
|
||||
FluentResults.Result ExecuteLoadedScripts(ImmutableArray<ILuaResourceInfo> scripts, bool pauseExecutionOnError = false, bool verboseLogging = false);
|
||||
FluentResults.Result ExecuteLoadedScripts(bool pauseExecutionOnError = false, bool verboseLogging = false);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Type_Registration
|
||||
|
||||
IUserDataDescriptor RegisterType(Type type);
|
||||
IUserDataDescriptor RegisterType(string typeName);
|
||||
IUserDataDescriptor RegisterGenericType(Type type);
|
||||
IUserDataDescriptor RegisterGenericType(string typeName, params string[] typeNameArgs);
|
||||
void UnregisterType(Type type);
|
||||
void UnregisterType(string typeName);
|
||||
void UnregisterAllTypes();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Type_Checks_&Utilities
|
||||
|
||||
bool IsRegistered(Type type);
|
||||
bool IsTargetType(object obj, string typeName);
|
||||
string TypeOf(object obj);
|
||||
object CreateStatic(string typeName);
|
||||
object CreateEnumTable(string typeName);
|
||||
FieldInfo FindFieldRecursively(Type type, string fieldName);
|
||||
void MakeFieldAccessible(IUserDataDescriptor descriptor, string fieldName);
|
||||
MethodInfo FindMethodRecursively(Type type, string methodName, Type[] types = null);
|
||||
void MakeMethodAccessible(IUserDataDescriptor descriptor, string methodName, string[] parameters = null);
|
||||
PropertyInfo FindPropertyRecursively(Type type, string propertyName);
|
||||
void MakePropertyAccessible(IUserDataDescriptor descriptor, string propertyName);
|
||||
void AddMethod(IUserDataDescriptor descriptor, string methodName, object function);
|
||||
void AddField(IUserDataDescriptor descriptor, string fieldName, DynValue value);
|
||||
void RemoveMember(IUserDataDescriptor descriptor, string memberName);
|
||||
bool HasMember(object obj, string memberName);
|
||||
DynValue CreateUserDataFromDescriptor(DynValue scriptObject, IUserDataDescriptor descriptor);
|
||||
DynValue CreateUserDataFromType(DynValue scriptObject, Type desiredType);
|
||||
|
||||
#endregion
|
||||
}
|
||||
+46
-74
@@ -3,89 +3,61 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface IPackageManagementService : IReusableService
|
||||
public interface IPackageManagementService : IReusableService, ILocalizationsResourcesInfo, IConfigsResourcesInfo, IConfigProfilesResourcesInfo, ILuaScriptsResourcesInfo, IAssembliesResourcesInfo
|
||||
#if CLIENT
|
||||
,IStylesResourcesInfo
|
||||
#endif
|
||||
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds packages to the queue of loadable packages without initializing them.
|
||||
/// Loads and parses the provided <see cref="ContentPackage"/> for <see cref="IResourceInfo"/> supported by the current runtime environment.
|
||||
/// </summary>
|
||||
/// <param name="packages"></param>
|
||||
void QueuePackages(ImmutableArray<LoadablePackage> packages);
|
||||
/// <returns></returns>
|
||||
Task<FluentResults.Result> LoadPackageInfosAsync(ContentPackage packages);
|
||||
/// <summary>
|
||||
/// Loads and parses the provided <see cref="ContentPackage"/> collection for <see cref="IResourceInfo"/> supported by the current runtime environment.
|
||||
/// </summary>
|
||||
/// <param name="packages"></param>
|
||||
/// <returns></returns>
|
||||
Task<IReadOnlyList<(ContentPackage, FluentResults.Result)>> LoadPackagesInfosAsync(IReadOnlyList<ContentPackage> packages);
|
||||
IReadOnlyList<ContentPackage> GetAllLoadedPackages();
|
||||
void DisposePackageInfos(ContentPackage package);
|
||||
void DisposePackagesInfos(IReadOnlyList<ContentPackage> packages);
|
||||
void DisposeAllPackagesInfos();
|
||||
|
||||
/// <summary>
|
||||
/// Generates the ModConfigInfo for all queued packages and adds them to the store.
|
||||
/// </summary>
|
||||
/// <param name="loadParallel">Use multithreaded loading.</param>
|
||||
/// <param name="reportFailOnDuplicates">Whether duplicate packages should be reported as errors.</param>
|
||||
/// <returns>Failure/Success records for each package.</returns>
|
||||
FluentResults.Result ParseQueuedPackages(bool loadParallel = true, bool reportFailOnDuplicates = false);
|
||||
/// <summary>
|
||||
/// Loads only the localizations, configs, and config profiles for stored packages.
|
||||
/// </summary>
|
||||
/// <param name="loadParallel"></param>
|
||||
/// <returns></returns>
|
||||
FluentResults.Result LoadPackageConfigsResourcesGroup(bool loadParallel = true);
|
||||
/// <summary>
|
||||
/// Loads all resources for stored packages.
|
||||
/// </summary>
|
||||
/// <param name="loadParallel">Use multithreaded loading.</param>
|
||||
/// <param name="safeResourcesOnly">Only load safe scripting resources, such as Lua. C# plugins disabled.</param>
|
||||
/// <returns></returns>
|
||||
FluentResults.Result LoadAllPackageResources(bool loadParallel = true, bool safeResourcesOnly = true);
|
||||
FluentResults.Result UnloadPackages();
|
||||
bool IsPackageLoaded(ContentPackage package);
|
||||
bool CheckDependencyLoaded(IPackageDependencyInfo info);
|
||||
bool CheckDependenciesLoaded([NotNull]IEnumerable<IPackageDependencyInfo> infos, out ImmutableArray<IPackageDependencyInfo> missingPackages);
|
||||
bool CheckEnvironmentSupported(IPlatformInfo platform);
|
||||
/// <summary>
|
||||
/// Tries to get the package dependency record to refer to that specific package if it exists, optionally create it.
|
||||
/// </summary>
|
||||
/// <param name="package">ContentPackage reference</param>
|
||||
/// <param name="addIfMissing">Register a new IPackageDependencyInfo reference.</param>
|
||||
/// <returns></returns>
|
||||
FluentResults.Result<IPackageDependencyInfo> GetPackageDependencyInfoRecord(ContentPackage package,
|
||||
bool addIfMissing = false);
|
||||
/// <summary>
|
||||
/// Tries to get the package dependency record to refer to that specific package if it exists, optionally create it.
|
||||
/// </summary>
|
||||
/// <param name="steamWorkshopId">The Steam Workshop ID, if available, if not enter zero ('0').</param>
|
||||
/// <param name="packageName">The name of the package.</param>
|
||||
/// <param name="folderPath">The folder path, as formatted in [ContentPackage.Path].</param>
|
||||
/// <param name="addIfMissing">Register a new IPackageDependencyInfo reference.</param>
|
||||
/// <returns></returns>
|
||||
FluentResults.Result<IPackageDependencyInfo> GetPackageDependencyInfoRecord(ulong steamWorkshopId,
|
||||
string packageName, string folderPath = null, bool addIfMissing = false);
|
||||
/// <summary>
|
||||
/// Tries to get the package dependency record to refer to that specific package if it exists.
|
||||
/// Note: This overload does not allow the registration of a new dependency.
|
||||
/// </summary>
|
||||
/// <param name="folderPath">The folder path, as formatted in [ContentPackage.Path].</param>
|
||||
/// <returns></returns>
|
||||
FluentResults.Result<IPackageDependencyInfo> GetPackageDependencyInfoRecord(string folderPath);
|
||||
|
||||
IPackageDependencyInfo CreateOrphanPackageDependencyInfoRecord(string packageName,
|
||||
string packagePath, ulong steamWorkshopId);
|
||||
}
|
||||
|
||||
public readonly record struct LoadablePackage
|
||||
{
|
||||
public ContentPackage Package { get; }
|
||||
public bool IsEnabled { get; }
|
||||
|
||||
public LoadablePackage(ContentPackage package, bool isEnabled)
|
||||
{
|
||||
Package = package;
|
||||
IsEnabled = isEnabled;
|
||||
}
|
||||
// single
|
||||
FluentResults.Result<IAssembliesResourcesInfo> GetAssembliesInfos(ContentPackage package, bool onlySupportedResources = true);
|
||||
FluentResults.Result<IConfigsResourcesInfo> GetConfigsInfos(ContentPackage package, bool onlySupportedResources = true);
|
||||
FluentResults.Result<IConfigProfilesResourcesInfo> GetConfigProfilesInfos(ContentPackage package, bool onlySupportedResources = true);
|
||||
FluentResults.Result<ILocalizationsResourcesInfo> GetLocalizationsInfos(ContentPackage package, bool onlySupportedResources = true);
|
||||
FluentResults.Result<ILuaScriptsResourcesInfo> GetLuaScriptsInfos(ContentPackage package, bool onlySupportedResources = true);
|
||||
#if CLIENT
|
||||
FluentResults.Result<IStylesResourcesInfo> GetStylesInfos(ContentPackage package, bool onlySupportedResources = true);
|
||||
#endif
|
||||
// collection
|
||||
FluentResults.Result<IAssembliesResourcesInfo> GetAssembliesInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||
FluentResults.Result<IConfigsResourcesInfo> GetConfigsInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||
FluentResults.Result<IConfigProfilesResourcesInfo> GetConfigProfilesInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||
FluentResults.Result<ILocalizationsResourcesInfo> GetLocalizationsInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||
FluentResults.Result<ILuaScriptsResourcesInfo> GetLuaScriptsInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||
#if CLIENT
|
||||
FluentResults.Result<IStylesResourcesInfo> GetStylesInfos(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||
#endif
|
||||
|
||||
Task<FluentResults.Result<IAssembliesResourcesInfo>> GetAssembliesInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||
Task<FluentResults.Result<IConfigsResourcesInfo>> GetConfigsInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||
Task<FluentResults.Result<IConfigProfilesResourcesInfo>> GetConfigProfilesInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||
Task<FluentResults.Result<ILocalizationsResourcesInfo>> GetLocalizationsInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||
Task<FluentResults.Result<ILuaScriptsResourcesInfo>> GetLuaScriptsInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||
#if CLIENT
|
||||
Task<FluentResults.Result<IStylesResourcesInfo>> GetStylesInfosAsync(IReadOnlyList<ContentPackage> packages, bool onlySupportedResources = true);
|
||||
#endif
|
||||
|
||||
public static ImmutableArray<LoadablePackage> FromEnumerable(IEnumerable<ContentPackage> packages, bool isEnabled)
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<LoadablePackage>();
|
||||
packages.ForEach(p => builder.Add(new LoadablePackage(p, isEnabled)));
|
||||
return builder.ToImmutable();
|
||||
}
|
||||
}
|
||||
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using FluentResults;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface IPackageService : IReusableService,
|
||||
// These allow us the pass the IContentPackageService to anything that needs the data without having to directly reference the member
|
||||
IResourceCultureInfo, IAssembliesResourcesInfo, ILocalizationsResourcesInfo, ILuaScriptsResourcesInfo
|
||||
{
|
||||
ContentPackage Package { get; }
|
||||
IModConfigInfo ModConfigInfo { get; }
|
||||
bool IsEnabledInModList { get; }
|
||||
/// <summary>
|
||||
/// Try to load the XML config and resources information from the given package.
|
||||
/// </summary>
|
||||
/// <param name="package"></param>
|
||||
/// <returns>Whether the package was parsed without errors.</returns>
|
||||
FluentResults.Result LoadResourcesInfo([NotNull]LoadablePackage package);
|
||||
/// <summary>
|
||||
/// Tries to load all assemblies and instance plugins for the given resources list, regardless whether they're marked as optional and/or lazy load.
|
||||
/// Will sort by load priority unless overriden/bypassed.
|
||||
/// </summary>
|
||||
/// <param name="assembliesInfo"></param>
|
||||
/// <param name="ignoreDependencySorting"></param>
|
||||
/// <returns>Whether loading is successful. Returns true on an empty list.</returns>
|
||||
FluentResults.Result LoadPlugins([NotNull]IAssembliesResourcesInfo assembliesInfo, bool ignoreDependencySorting = false);
|
||||
FluentResults.Result LoadLocalizations([NotNull]ILocalizationsResourcesInfo localizationsInfo);
|
||||
FluentResults.Result AddLuaScripts([NotNull]ILuaScriptsResourcesInfo luaScriptsInfo);
|
||||
#if CLIENT
|
||||
FluentResults.Result LoadStyles([NotNull]IStylesResourcesInfo stylesInfo);
|
||||
#endif
|
||||
FluentResults.Result LoadConfig([NotNull]IConfigsResourcesInfo configsResourcesInfo, [NotNull]IConfigProfilesResourcesInfo configProfilesResourcesInfo);
|
||||
}
|
||||
|
||||
+22
-2
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Reflection;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
@@ -31,16 +32,35 @@ public interface IPluginManagementService : IReusableService
|
||||
bool includeDefaultContext = true);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the
|
||||
/// Tries to get the Type given the fully qualified name.
|
||||
/// </summary>
|
||||
/// <param name="typeName"></param>
|
||||
/// <returns></returns>
|
||||
Type GetType(string typeName);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Loads the provided assembly resources in the order of their dependencies and intra-mod priority load order.
|
||||
/// </summary>
|
||||
/// <param name="resource"></param>
|
||||
/// <returns>Success/Failure and list of failed resources, if any.</returns>
|
||||
FluentResults.Result<ImmutableArray<IAssemblyResourceInfo>> LoadAssemblyResources(ImmutableArray<IAssemblyResourceInfo> resource);
|
||||
|
||||
/// <summary>
|
||||
/// Creates instances of the given type and provides Property Injection and instance reference caching. Disposes of
|
||||
/// all references that throw errors on
|
||||
/// </summary>
|
||||
/// <param name="types">List of Types</param>
|
||||
/// <param name="serviceInjection"></param>
|
||||
/// <param name="hostInstanceReference"></param>
|
||||
/// <returns></returns>
|
||||
IReadOnlyList<FluentResults.Result<(Type, T)>> ActivateTypeInstances<T>(ImmutableArray<Type> types, bool serviceInjection = true,
|
||||
bool hostInstanceReference = false) where T : IDisposable;
|
||||
|
||||
FluentResults.Result UnloadHostedReferences();
|
||||
|
||||
/// <summary>
|
||||
/// Tries to gracefully unload all hosted plugin references
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
FluentResults.Result UnloadAllAssemblyResources();
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
GUI.SettingsMenuOpen = false;
|
||||
#endif
|
||||
Selected = this;
|
||||
GameMain.LuaCs.EventService.PublishEvent<LuaCs.Events.IEventScreenSelected>(sub => sub.OnScreenSelected(this));
|
||||
}
|
||||
|
||||
public virtual Camera Cam => null;
|
||||
|
||||
Reference in New Issue
Block a user