- Made Package loading conditional on resources being available.

- Made States registration use named parameters.
- Changed IPluginManagementService interface to better suit expected return results.
This commit is contained in:
MapleWheels
2026-01-23 13:48:01 -05:00
committed by Maplewheels
parent 15e0a2bd10
commit 6e66a3114a
9 changed files with 136 additions and 76 deletions
@@ -60,7 +60,7 @@ namespace Barotrauma
foreach (var package in ContentPackageManager.AllPackages)
{
if (package.UgcId.ValueEquals(new SteamWorkshopId(GameMain.LuaCs.LuaForBarotraumaSteamId?.Value ?? 0ul))
if (package.UgcId.ValueEquals(new SteamWorkshopId(GameMain.LuaCs.LuaForBarotraumaSteamId))
&& pathStartsWith(getFullPath(package.Path)))
{
return false;
@@ -5,6 +5,7 @@ using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Net.Mime;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Barotrauma.LuaCs;
@@ -93,60 +94,102 @@ namespace Barotrauma
public LuaGame Game => _servicesProvider.GetService<LuaGame>();
internal IStorageService StorageService => _servicesProvider.GetService<IStorageService>();
/// <summary>
/// Whether C# plugin code is enabled.
/// </summary>
internal ISettingEntry<bool> IsCsEnabled { get; private set; }
public bool IsCsEnabled
{
get => _isCsEnabled?.Value ?? false;
internal set => _isCsEnabled?.TrySetValue(value);
}
private ISettingEntry<bool> _isCsEnabled;
/// <summary>
/// Whether mods marked as 'forced' or 'always load' should only be loaded if they're in the enabled mods list.
/// </summary>
internal ISettingEntry<bool> TreatForcedModsAsNormal { get; private set; }
public bool TreatForcedModsAsNormal
{
get => _treatForcedModsAsNormal?.Value ?? true;
internal set => _treatForcedModsAsNormal?.TrySetValue(value);
}
private ISettingEntry<bool> _treatForcedModsAsNormal;
/// <summary>
/// Whether the lua script runner from Workshop package should be used over the in-built version.
/// </summary>
internal ISettingEntry<bool> PreferToUseWorkshopLuaSetup { get; private set; }
public bool PreferToUseWorkshopLuaSetup
{
get => _preferToUseWorkshopLuaSetup?.Value ?? false;
internal set => _preferToUseWorkshopLuaSetup?.TrySetValue(value);
}
private ISettingEntry<bool> _preferToUseWorkshopLuaSetup;
/// <summary>
/// Whether the popup error GUI should be hidden/suppressed.
/// </summary>
internal ISettingEntry<bool> DisableErrorGUIOverlay { get; private set; }
public bool DisableErrorGUIOverlay
{
get => _disableErrorGUIOverlay?.Value ?? false;
internal set => _disableErrorGUIOverlay?.TrySetValue(value);
}
private ISettingEntry<bool> _disableErrorGUIOverlay;
/// <summary>
/// Whether usernames are anonymized or show in logs.
/// </summary>
internal ISettingEntry<bool> HideUserNamesInLogs { get; private set; }
public bool HideUserNamesInLogs
{
get => _hideUserNamesInLogs?.Value ?? false;
internal set => _hideUserNamesInLogs?.TrySetValue(value);
}
private ISettingEntry<bool> _hideUserNamesInLogs;
/// <summary>
/// The SteamId of the Workshop LuaCs CPackage in use, if available.
/// </summary>
internal ISettingEntry<ulong> LuaForBarotraumaSteamId { get; private set; }
public ulong LuaForBarotraumaSteamId
{
get => _luaForBarotraumaSteamId?.Value ?? 0;
internal set => _luaForBarotraumaSteamId?.TrySetValue(value);
}
private ISettingEntry<ulong> _luaForBarotraumaSteamId;
/// <summary>
/// TODO: @evilfactory@users.noreply.github.com
/// </summary>
internal ISettingEntry<bool> RestrictMessageSize { get; private set; }
public bool RestrictMessageSize
{
get => _restrictMessageSize?.Value ?? false;
internal set => _restrictMessageSize?.TrySetValue(value);
}
private ISettingEntry<bool> _restrictMessageSize;
/// <summary>
/// The local save path for all local data storage for mods.
/// </summary>
internal ISettingEntry<string> LocalDataSavePath { get; private set; }
public string LocalDataSavePath
{
get => _localDataSavePath?.Value ?? Path.Combine(Directory.GetCurrentDirectory(), "/Data/Mods");
internal set => _localDataSavePath?.TrySetValue(value);
}
private ISettingEntry<string> _localDataSavePath;
void LoadLuaCsConfig()
{
IsCsEnabled = ConfigService.TryGetConfig<ISettingEntry<bool>>(ContentPackageManager.VanillaCorePackage, "IsCsEnabled", out var val1) ? val1
_isCsEnabled = ConfigService.TryGetConfig<ISettingEntry<bool>>(ContentPackageManager.VanillaCorePackage, "IsCsEnabled", out var val1) ? val1
: throw new NullReferenceException($"{nameof(IsCsEnabled)} cannot be loaded.");
TreatForcedModsAsNormal = ConfigService.TryGetConfig<ISettingEntry<bool>>(ContentPackageManager.VanillaCorePackage, "TreatForcedModsAsNormal", out var val2) ? val2
_treatForcedModsAsNormal = ConfigService.TryGetConfig<ISettingEntry<bool>>(ContentPackageManager.VanillaCorePackage, "TreatForcedModsAsNormal", out var val2) ? val2
: throw new NullReferenceException($"{nameof(TreatForcedModsAsNormal)} cannot be loaded.");
DisableErrorGUIOverlay = ConfigService.TryGetConfig<ISettingEntry<bool>>(ContentPackageManager.VanillaCorePackage, "DisableErrorGUIOverlay", out var val3) ? val3
_disableErrorGUIOverlay = ConfigService.TryGetConfig<ISettingEntry<bool>>(ContentPackageManager.VanillaCorePackage, "DisableErrorGUIOverlay", out var val3) ? val3
: throw new NullReferenceException($"{nameof(DisableErrorGUIOverlay)} cannot be loaded.");
HideUserNamesInLogs = ConfigService.TryGetConfig<ISettingEntry<bool>>(ContentPackageManager.VanillaCorePackage, "HideUserNamesInLogs", out var val4) ? val4
_hideUserNamesInLogs = ConfigService.TryGetConfig<ISettingEntry<bool>>(ContentPackageManager.VanillaCorePackage, "HideUserNamesInLogs", out var val4) ? val4
: throw new NullReferenceException($"{nameof(HideUserNamesInLogs)} cannot be loaded.");
LuaForBarotraumaSteamId = ConfigService.TryGetConfig<ISettingEntry<ulong>>(ContentPackageManager.VanillaCorePackage, "LuaForBarotraumaSteamId", out var val5) ? val5
_luaForBarotraumaSteamId = ConfigService.TryGetConfig<ISettingEntry<ulong>>(ContentPackageManager.VanillaCorePackage, "LuaForBarotraumaSteamId", out var val5) ? val5
: throw new NullReferenceException($"{nameof(LuaForBarotraumaSteamId)} cannot be loaded.");
RestrictMessageSize = ConfigService.TryGetConfig<ISettingEntry<bool>>(ContentPackageManager.VanillaCorePackage, "RestrictMessageSize", out var val7) ? val7
_restrictMessageSize = ConfigService.TryGetConfig<ISettingEntry<bool>>(ContentPackageManager.VanillaCorePackage, "RestrictMessageSize", out var val7) ? val7
: throw new NullReferenceException($"{nameof(RestrictMessageSize)} cannot be loaded.");
}
@@ -244,9 +287,9 @@ namespace Barotrauma
private StateMachine<RunState> SetupStateMachine()
{
return new StateMachine<RunState>(false, RunState.Unloaded, RunStateUnloaded_OnEnter, null)
.AddState(RunState.LoadedNoExec, RunStateLoadedNoExec_OnEnter, null)
.AddState(RunState.Running, RunStateRunning_OnEnter, RunStateRunning_OnExit);
return new StateMachine<RunState>(false, RunState.Unloaded, onEnter: RunStateUnloaded_OnEnter, null)
.AddState(RunState.LoadedNoExec, onEnter: RunStateLoadedNoExec_OnEnter, null)
.AddState(RunState.Running, onEnter: RunStateRunning_OnEnter, RunStateRunning_OnExit);
// ReSharper disable InconsistentNaming
void RunStateUnloaded_OnEnter(State<RunState> currentState)
@@ -355,12 +398,12 @@ namespace Barotrauma
void DisposeLuaCsConfig()
{
IsCsEnabled = null;
TreatForcedModsAsNormal = null;
DisableErrorGUIOverlay = null;
HideUserNamesInLogs = null;
LuaForBarotraumaSteamId = null;
RestrictMessageSize = null;
_isCsEnabled = null;
_treatForcedModsAsNormal = null;
_disableErrorGUIOverlay = null;
_hideUserNamesInLogs = null;
_luaForBarotraumaSteamId = null;
_restrictMessageSize = null;
}
}
@@ -151,17 +151,28 @@ public sealed class PackageManagementService : IPackageManagementService
try
{
var res = new FluentResults.Result();
var configsTask = new Task<Task<FluentResults.Result>>(async Task<FluentResults.Result> () =>
new FluentResults.Result()
.WithReasons((await _configService.LoadConfigsAsync(config.Configs)).Reasons)
.WithReasons((await _configService.LoadConfigsProfilesAsync(config.Configs)).Reasons));
var luaScriptsTask = new Task<Task<FluentResults.Result>>(async () =>
await _luaScriptManagementService.LoadScriptResourcesAsync(config.LuaScripts));
var tasks = ImmutableArray.CreateBuilder<Task<Task<FluentResults.Result>>>();
if (!config.Configs.IsDefaultOrEmpty)
{
tasks.Add(Task.Factory.StartNew(async Task<FluentResults.Result> () =>
new FluentResults.Result()
.WithReasons((await _configService.LoadConfigsAsync(config.Configs)).Reasons)
.WithReasons((await _configService.LoadConfigsProfilesAsync(config.Configs)).Reasons)));
}
if (!config.LuaScripts.IsDefaultOrEmpty)
{
tasks.Add(Task.Factory.StartNew(async () =>
await _luaScriptManagementService.LoadScriptResourcesAsync(config.LuaScripts)));
}
if (tasks.Count == 0)
{
return FluentResults.Result.Ok();
}
configsTask.Start();
luaScriptsTask.Start();
var r = Task.WhenAll(configsTask, luaScriptsTask).ConfigureAwait(false).GetAwaiter().GetResult();
var r = Task.WhenAll(tasks.ToArray()).ConfigureAwait(false).GetAwaiter().GetResult();
foreach (var task in r)
res.WithReasons(task.ConfigureAwait(false).GetAwaiter().GetResult().Reasons);
@@ -180,7 +191,9 @@ public sealed class PackageManagementService : IPackageManagementService
IService.CheckDisposed(this);
if (executionOrder.IsDefaultOrEmpty)
{
return FluentResults.Result.Fail($"{nameof(ExecuteLoadedPackages)}: No packages in the execution order list.");
}
if (!_runningPackages.IsEmpty)
{
@@ -190,7 +203,9 @@ public sealed class PackageManagementService : IPackageManagementService
}
if (_loadedPackages.IsEmpty)
{
return FluentResults.Result.Fail($"{nameof(ExecuteLoadedPackages)}: No packages loaded. Nothing to run!)");
}
var result = new FluentResults.Result();
@@ -198,24 +213,16 @@ public sealed class PackageManagementService : IPackageManagementService
var loadingOrderedPackages = _loadedPackages.OrderBy(pkg => executionOrder.IndexOf(pkg.Key))
.ToImmutableArray();
//mod settings
var settings = loadingOrderedPackages
.SelectMany(pkg => pkg.Value.Configs.OrderBy(scr => scr.LoadPriority))
.ToImmutableArray();
if (!settings.IsDefaultOrEmpty)
{
result.WithReasons(_configService.LoadConfigsAsync(settings).ConfigureAwait(false).GetAwaiter()
.GetResult().Reasons);
result.WithReasons(_configService.LoadConfigsProfilesAsync(settings).ConfigureAwait(false)
.GetAwaiter().GetResult().Reasons);
}
// NOTE: Config/Settings are instanced in LoadPackages()
//lua scripts
var luaScripts = loadingOrderedPackages
.SelectMany(pkg => pkg.Value.LuaScripts.OrderBy(scr => scr.LoadPriority))
.ToImmutableArray();
if (!luaScripts.IsDefaultOrEmpty)
{
result.WithReasons(_luaScriptManagementService.ExecuteLoadedScripts(luaScripts).Reasons);
}
if (_runConfig.IsCsEnabled)
{
@@ -223,7 +230,9 @@ public sealed class PackageManagementService : IPackageManagementService
loadingOrderedPackages.SelectMany(pkg => pkg.Value.Assemblies.OrderBy(scr => scr.LoadPriority))
.ToImmutableArray();
if (!plugins.IsDefaultOrEmpty)
{
result.WithReasons(_pluginManagementService.LoadAssemblyResources(plugins).Reasons);
}
}
return result;
@@ -13,6 +13,7 @@ using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs.Events;
using FluentResults;
using FluentResults.LuaCs;
using ImpromptuInterface.Build;
using Microsoft.CodeAnalysis;
using OneOf;
@@ -21,14 +22,15 @@ namespace Barotrauma.LuaCs.Services;
public class PluginManagementService : IPluginManagementService, IAssemblyManagementService
{
private readonly Func<IAssemblyLoaderService.LoaderInitData, IAssemblyLoaderService> _assemblyLoaderServiceFactory;
private readonly ConcurrentDictionary<ContentPackage, (List<IAssemblyResourceInfo> ResourceInfos, IAssemblyLoaderService Loader)> _packageAssemblyResources;
private readonly ConcurrentDictionary<ContentPackage, List<IDisposable>> _pluginInstances;
private readonly ConcurrentDictionary<ContentPackage, (List<IAssemblyResourceInfo> ResourceInfos, IAssemblyLoaderService Loader)> _packageAssemblyResources = new();
private readonly ConcurrentDictionary<ContentPackage, List<IDisposable>> _pluginInstances = new();
private readonly Lazy<IEventService> _eventService;
private readonly ConditionalWeakTable<IAssemblyLoaderService, ContentPackage> _unloadingAssemblyLoaders;
private readonly ConditionalWeakTable<Assembly, ConcurrentDictionary<string, Type>> _assemblyTypesCache;
private readonly ConditionalWeakTable<IAssemblyLoaderService, ContentPackage> _unloadingAssemblyLoaders = new();
private readonly ConditionalWeakTable<Assembly, ConcurrentDictionary<string, Type>> _assemblyTypesCache = new();
public PluginManagementService(
Func<IAssemblyLoaderService.LoaderInitData, IAssemblyLoaderService> assemblyLoaderServiceFactory,
Func<IAssemblyLoaderService.LoaderInitData,
IAssemblyLoaderService> assemblyLoaderServiceFactory,
Lazy<IEventService> eventService)
{
_assemblyLoaderServiceFactory = assemblyLoaderServiceFactory;
@@ -123,14 +125,20 @@ public class PluginManagementService : IPluginManagementService, IAssemblyManage
throw new NotImplementedException();
}
public Result<ImmutableArray<IAssemblyResourceInfo>> LoadAssemblyResources(ImmutableArray<IAssemblyResourceInfo> resource)
public FluentResults.Result LoadAssemblyResources(ImmutableArray<IAssemblyResourceInfo> resource)
{
#if DEBUG
return FluentResults.Result.Fail($"{nameof(LoadAssemblyResources)}: Plugin loading not currently implemented.");
#endif
throw new NotImplementedException();
}
public ImmutableArray<Result<(Type, T)>> ActivateTypeInstances<T>(ImmutableArray<Type> types, bool serviceInjection = true,
bool hostInstanceReference = false) where T : IDisposable
{
#if DEBUG
return ImmutableArray<Result<(Type, T)>>.Empty;
#endif
throw new NotImplementedException();
}
@@ -37,7 +37,7 @@ public interface IPluginManagementService : IReusableService
/// </summary>
/// <param name="resource"></param>
/// <returns>Success/Failure and list of failed resources, if any.</returns>
FluentResults.Result<ImmutableArray<IAssemblyResourceInfo>> LoadAssemblyResources(ImmutableArray<IAssemblyResourceInfo> resource);
FluentResults.Result LoadAssemblyResources(ImmutableArray<IAssemblyResourceInfo> resource);
/// <summary>
/// Creates instances of the given type and provides Property Injection and instance reference caching. Disposes of
@@ -30,7 +30,7 @@ public class StateMachine<T> where T : Enum
ThrowHelper.ThrowArgumentException($"State with id {stateId} already exists.");
}
_states[stateId] = new State<T>(stateId, onEnter, onExit);
_states[stateId] = new State<T>(stateId, onEnterState: onEnter, onExitState: onExit);
return this;
}