- 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:
@@ -18,12 +18,14 @@ using System.Reflection;
|
||||
using System.Threading;
|
||||
using Barotrauma.Extensions;
|
||||
using System.Collections.Immutable;
|
||||
using Barotrauma.LuaCs.Events;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class GameMain : Game
|
||||
{
|
||||
public static LuaCsSetup LuaCs;
|
||||
private static LuaCsSetup _luaCs;
|
||||
public static LuaCsSetup LuaCs => _luaCs ??= new LuaCsSetup();
|
||||
public static bool ShowFPS;
|
||||
public static bool ShowPerf;
|
||||
public static bool DebugDraw;
|
||||
@@ -244,8 +246,6 @@ namespace Barotrauma
|
||||
throw new Exception("Content folder not found. If you are trying to compile the game from the source code and own a legal copy of the game, you can copy the Content folder from the game's files to BarotraumaShared/Content.");
|
||||
}
|
||||
|
||||
LuaCs = new LuaCsSetup();
|
||||
|
||||
GameSettings.Init();
|
||||
CreatureMetrics.Init();
|
||||
|
||||
@@ -1054,7 +1054,7 @@ namespace Barotrauma
|
||||
|
||||
SoundManager?.Update();
|
||||
|
||||
GameMain.LuaCs.Update();
|
||||
LuaCs.EventService.PublishEvent<IEventUpdate>(sub => sub.OnUpdate(Timing.Step));
|
||||
|
||||
Timing.Accumulator -= Timing.Step;
|
||||
|
||||
@@ -1237,8 +1237,6 @@ namespace Barotrauma
|
||||
GUIMessageBox.CloseAll();
|
||||
MainMenuScreen.Select();
|
||||
GameSession = null;
|
||||
|
||||
GameMain.LuaCs.Stop();
|
||||
}
|
||||
|
||||
public void ShowBugReporter()
|
||||
|
||||
@@ -179,8 +179,6 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
public void Start()
|
||||
{
|
||||
GameMain.LuaCs.CheckInitialize();
|
||||
|
||||
GameMain.Instance.ShowLoading(Loading());
|
||||
ObjectiveManager.ResetObjectives();
|
||||
|
||||
|
||||
@@ -18,5 +18,6 @@ public record StylesResourceInfo : IStylesResourceInfo
|
||||
public ImmutableArray<CultureInfo> SupportedCultures { get; init; }
|
||||
public string InternalName { get; init; }
|
||||
public ContentPackage OwnerPackage { get; init; }
|
||||
public string FallbackPackageName { get; }
|
||||
public ImmutableArray<IPackageDependencyInfo> Dependencies { get; init; }
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (!File.Exists(LuaCsSetup.VersionFile)) { return; }
|
||||
|
||||
ContentPackage luaPackage = LuaCsSetup.GetPackage(LuaCsSetup.LuaForBarotraumaId);
|
||||
ContentPackage luaPackage = LuaCsSetup.GetPackage(new SteamWorkshopId(GameMain.LuaCs.LuaForBarotraumaSteamId?.Value ?? 0));
|
||||
|
||||
if (luaPackage == null) { return; }
|
||||
|
||||
|
||||
@@ -19,65 +19,55 @@ namespace Barotrauma
|
||||
|
||||
new GUITickBox(new RectTransform(new Vector2(0.8f, 0.1f), list.Content.RectTransform), "Enable CSharp Scripting")
|
||||
{
|
||||
Selected = GameMain.LuaCs.Config.EnableCsScripting,
|
||||
Selected = GameMain.LuaCs.IsCsEnabled?.Value ?? false,
|
||||
ToolTip = "This enables CSharp Scripting for mods to use, WARNING: CSharp is NOT sandboxed, be careful with what mods you download.",
|
||||
OnSelected = (GUITickBox tick) =>
|
||||
{
|
||||
GameMain.LuaCs.Config.EnableCsScripting = tick.Selected;
|
||||
GameMain.LuaCs.WriteSettings();
|
||||
|
||||
GameMain.LuaCs.IsCsEnabled?.TrySetValue(tick.Selected);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
new GUITickBox(new RectTransform(new Vector2(0.8f, 0.1f), list.Content.RectTransform), "Treat Forced Mods As Normal")
|
||||
{
|
||||
Selected = GameMain.LuaCs.Config.TreatForcedModsAsNormal,
|
||||
Selected = GameMain.LuaCs.TreatForcedModsAsNormal?.Value ?? false,
|
||||
ToolTip = "This makes mods that were setup to run even when disabled to only run when enabled.",
|
||||
OnSelected = (GUITickBox tick) =>
|
||||
{
|
||||
GameMain.LuaCs.Config.TreatForcedModsAsNormal = tick.Selected;
|
||||
GameMain.LuaCs.WriteSettings();
|
||||
|
||||
GameMain.LuaCs.TreatForcedModsAsNormal?.TrySetValue(tick.Selected);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
new GUITickBox(new RectTransform(new Vector2(0.8f, 0.1f), list.Content.RectTransform), "Prefer To Use Workshop Lua Setup")
|
||||
{
|
||||
Selected = GameMain.LuaCs.Config.PreferToUseWorkshopLuaSetup,
|
||||
Selected = GameMain.LuaCs.PreferToUseWorkshopLuaSetup?.Value ?? false,
|
||||
ToolTip = "This makes Lua look first for the Lua/LuaSetup.lua located in the Workshop package instead of the one located locally.",
|
||||
OnSelected = (GUITickBox tick) =>
|
||||
{
|
||||
GameMain.LuaCs.Config.PreferToUseWorkshopLuaSetup = tick.Selected;
|
||||
GameMain.LuaCs.WriteSettings();
|
||||
|
||||
GameMain.LuaCs.PreferToUseWorkshopLuaSetup?.TrySetValue(tick.Selected);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
new GUITickBox(new RectTransform(new Vector2(0.8f, 0.1f), list.Content.RectTransform), "Disable Error GUI Overlay")
|
||||
{
|
||||
Selected = GameMain.LuaCs.Config.DisableErrorGUIOverlay,
|
||||
Selected = GameMain.LuaCs.DisableErrorGUIOverlay?.Value ?? false,
|
||||
ToolTip = "",
|
||||
OnSelected = (GUITickBox tick) =>
|
||||
{
|
||||
GameMain.LuaCs.Config.DisableErrorGUIOverlay = tick.Selected;
|
||||
GameMain.LuaCs.WriteSettings();
|
||||
|
||||
GameMain.LuaCs.DisableErrorGUIOverlay?.TrySetValue(tick.Selected);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
new GUITickBox(new RectTransform(new Vector2(0.8f, 0.1f), list.Content.RectTransform), "Hide usernames In Error Logs")
|
||||
{
|
||||
Selected = GameMain.LuaCs.Config.HideUserNames,
|
||||
Selected = GameMain.LuaCs.HideUserNamesInLogs?.Value ?? false,
|
||||
ToolTip = "Hides the operating system username when displaying error logs (eg your username on windows).",
|
||||
OnSelected = (GUITickBox tick) =>
|
||||
{
|
||||
GameMain.LuaCs.Config.HideUserNames = tick.Selected;
|
||||
GameMain.LuaCs.WriteSettings();
|
||||
|
||||
GameMain.LuaCs.HideUserNamesInLogs?.TrySetValue(tick.Selected);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -100,7 +90,6 @@ namespace Barotrauma
|
||||
OnClicked = (GUIButton button, object obj) =>
|
||||
{
|
||||
Close();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Barotrauma.CharacterEditor;
|
||||
using Barotrauma.LuaCs.Services;
|
||||
|
||||
// ReSharper disable ObjectCreationAsStatement
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -8,47 +15,42 @@ namespace Barotrauma
|
||||
{
|
||||
public void AddToGUIUpdateList()
|
||||
{
|
||||
if (!GameMain.LuaCs.Config.DisableErrorGUIOverlay)
|
||||
if (!DisableErrorGUIOverlay.Value)
|
||||
{
|
||||
LuaCsLogger.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckInitialize()
|
||||
{
|
||||
List<ContentPackage> csharpMods = new List<ContentPackage>();
|
||||
foreach (ContentPackage cp in ContentPackageManager.EnabledPackages.All)
|
||||
{
|
||||
if (Directory.Exists(cp.Dir + "/CSharp") || Directory.Exists(cp.Dir + "/bin"))
|
||||
{
|
||||
csharpMods.Add(cp);
|
||||
}
|
||||
}
|
||||
private partial bool ShouldRunCs() => IsCsEnabled.Value;
|
||||
|
||||
public IStylesManagementService StylesManagementService => _servicesProvider.TryGetService<IStylesManagementService>(out var svc)
|
||||
? svc : throw new NullReferenceException("Networking Manager service not found!");
|
||||
|
||||
if (csharpMods.Count == 0 || ShouldRunCs)
|
||||
{
|
||||
Initialize();
|
||||
public void CheckCsEnabled()
|
||||
{
|
||||
|
||||
var csharpMods = PackageManagementService.Assemblies
|
||||
.GroupBy(ass => ass.OwnerPackage)
|
||||
.Select(grp => grp.Key)
|
||||
.Where(ContentPackageManager.EnabledPackages.All.Contains)
|
||||
.ToImmutableArray();
|
||||
|
||||
if (!csharpMods.Any())
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
foreach (ContentPackage cp in csharpMods)
|
||||
{
|
||||
if (cp.UgcId.TryUnwrap(out ContentPackageId id))
|
||||
{
|
||||
sb.AppendLine($"- {cp.Name} ({id})");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine($"- {cp.Name} (Not On Workshop)");
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.Client == null || GameMain.Client.IsServerOwner)
|
||||
{
|
||||
new GUIMessageBox("", $"You have CSharp mods enabled but don't have the CSharp Scripting enabled, those mods might not work, go to the Main Menu, click on LuaCs Settings and check Enable CSharp Scripting.\n\n{sb}");
|
||||
Initialize();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -59,17 +61,51 @@ namespace Barotrauma
|
||||
|
||||
msg.Buttons[0].OnClicked = (GUIButton button, object obj) =>
|
||||
{
|
||||
Initialize(true);
|
||||
msg.Close();
|
||||
this.IsCsEnabled.TrySetValue(true);
|
||||
return true;
|
||||
};
|
||||
|
||||
msg.Buttons[1].OnClicked = (GUIButton button, object obj) =>
|
||||
{
|
||||
Initialize();
|
||||
msg.Close();
|
||||
this.IsCsEnabled.TrySetValue(false);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (screen)
|
||||
{
|
||||
// menus and navigation states
|
||||
case MainMenuScreen:
|
||||
case ModDownloadScreen:
|
||||
case ServerListScreen:
|
||||
SetRunState(RunState.Configuration);
|
||||
break;
|
||||
// running lobby or editor states
|
||||
case CampaignEndScreen:
|
||||
case CharacterEditorScreen:
|
||||
case EventEditorScreen:
|
||||
case GameScreen:
|
||||
case LevelEditorScreen:
|
||||
case NetLobbyScreen:
|
||||
case ParticleEditorScreen:
|
||||
case RoundSummaryScreen:
|
||||
case SpriteEditorScreen:
|
||||
case SubEditorScreen:
|
||||
case TestScreen: // notes: TestScreen is a Linux edge case editor screen and is deprecated.
|
||||
CheckCsEnabled();
|
||||
SetRunState(RunState.Running);
|
||||
break;
|
||||
default:
|
||||
Logger.LogError($"{nameof(LuaCsSetup)}: Received an unknown screen {screen?.GetType().Name ?? "'null screen'"}. Retarding load state to 'unloaded'.");
|
||||
SetRunState(RunState.Unloaded);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface IStylesManagementService : IReusableService
|
||||
{
|
||||
Task<FluentResults.Result> LoadStylesAsync(ImmutableArray<IStylesResourceInfo> styles);
|
||||
FluentResults.Result<IStylesService> GetStylesService(ContentPackage package);
|
||||
Task<FluentResults.Result> DisposeAllStyles();
|
||||
Task<FluentResults.Result> DisposeStylesForPackage(ContentPackage package);
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
// TODO: Rework interface to support resource infos.
|
||||
/// <summary>
|
||||
@@ -7,43 +9,48 @@
|
||||
public interface IStylesService : IReusableService
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries to load the styles file for the given contentpackage and path into a new UIStylesProcessor instance.
|
||||
/// Tries to load the styles file for the given <see cref="ContentPackage"/> and path into a new <see cref="UIStyleProcessor"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="package"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
FluentResults.Result LoadStylesFile(ContentPackage package, ContentPath path);
|
||||
Task<FluentResults.Result> LoadStylesFileAsync(ContentPackage package, ContentPath path);
|
||||
|
||||
/// <summary>
|
||||
/// Unloads all styles assets and UIStyleProcessor instances.
|
||||
/// Unloads all styles assets and <see cref="UIStyleProcessor"/> instances.
|
||||
/// </summary>
|
||||
FluentResults.Result UnloadAllStyles();
|
||||
|
||||
/// <summary>
|
||||
/// Tries to the get the font asset by xml asset name, returns null on failure.
|
||||
/// Tries to the get the <see cref="GUIFont"/> asset by xml asset name, returns null on failure.
|
||||
/// </summary>
|
||||
/// <param name="fontName">XML Name of the asset.</param>
|
||||
/// <returns>The asset or null if none are found.</returns>
|
||||
GUIFont GetFont(string fontName);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to the get the sprite asset by xml asset name, returns null on failure.
|
||||
/// Tries to the get the <see cref="GUISprite"/> asset by xml asset name, returns null on failure.
|
||||
/// </summary>
|
||||
/// <param name="spriteName">XML Name of the asset.</param>
|
||||
/// <returns>The asset or null if none are found.</returns>
|
||||
GUISprite GetSprite(string spriteName);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to the get the sprite sheet asset by xml asset name, returns null on failure.
|
||||
/// Tries to the get the <see cref="GUISpriteSheet"/> asset by xml asset name, returns null on failure.
|
||||
/// </summary>
|
||||
/// <param name="spriteSheetName">XML Name of the asset.</param>
|
||||
/// <returns>The asset or null if none are found.</returns>
|
||||
GUISpriteSheet GetSpriteSheet(string spriteSheetName);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to the get the cursor asset by xml asset name, returns null on failure.
|
||||
/// Tries to the get the <see cref="GUICursor"/> asset by xml asset name, returns null on failure.
|
||||
/// </summary>
|
||||
/// <param name="cursorName">XML Name of the asset.</param>
|
||||
/// <returns>The asset or null if none are found.</returns>
|
||||
GUICursor GetCursor(string cursorName);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to the get the color asset by xml asset name, returns null on failure.
|
||||
/// Tries to the get the <see cref="GUIColor"/> asset by xml asset name, returns null on failure.
|
||||
/// </summary>
|
||||
/// <param name="colorName">XML Name of the asset.</param>
|
||||
/// <returns>The asset or null if none are found.</returns>
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.LuaCs.Services.Processing;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public partial class PackageService : IStylesResourcesInfo
|
||||
{
|
||||
private readonly Lazy<IStylesService> _stylesService;
|
||||
public IStylesService Styles => _stylesService.Value;
|
||||
|
||||
public PackageService(
|
||||
Lazy<IModConfigCreatorService> configParserService,
|
||||
Lazy<ILuaScriptService> luaScriptService,
|
||||
Lazy<ILocalizationService> localizationService,
|
||||
Lazy<IPluginService> pluginService,
|
||||
Lazy<IStylesService> stylesService,
|
||||
Lazy<IConfigService> configService,
|
||||
IPackageManagementService packageManagementService,
|
||||
IStorageService storageService,
|
||||
ILoggerService loggerService)
|
||||
{
|
||||
_configParserService = configParserService;
|
||||
_luaScriptService = luaScriptService;
|
||||
_localizationService = localizationService;
|
||||
_pluginService = pluginService;
|
||||
_stylesService = stylesService;
|
||||
_configService = configService;
|
||||
_packageManagementService = packageManagementService;
|
||||
_storageService = storageService;
|
||||
_loggerService = loggerService;
|
||||
}
|
||||
|
||||
public ImmutableArray<IStylesResourceInfo> StylesResourceInfos => ModConfigInfo?.StylesResourceInfos ?? ImmutableArray<IStylesResourceInfo>.Empty;
|
||||
|
||||
public FluentResults.Result LoadStyles([NotNull]IStylesResourcesInfo stylesInfo)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Barotrauma.LuaCs.Services.Processing;
|
||||
|
||||
public class StylesManagementService : IStylesManagementService
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,16 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using FluentResults;
|
||||
using FluentResults.LuaCs;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
// TODO: Complete rewrite
|
||||
public class StylesService : IStylesService
|
||||
{
|
||||
private readonly Dictionary<string, UIStyleProcessor> _loadedProcessors = new();
|
||||
private readonly ConcurrentDictionary<string, UIStyleProcessor> _loadedProcessors = new();
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly ILoggerService _loggerService;
|
||||
|
||||
@@ -19,40 +22,11 @@ public class StylesService : IStylesService
|
||||
_storageService = storageService;
|
||||
_loggerService = loggerService;
|
||||
}
|
||||
|
||||
public FluentResults.Result LoadStylesFile(ContentPackage package, ContentPath path)
|
||||
|
||||
|
||||
public async Task<FluentResults.Result> LoadStylesFileAsync(ContentPackage package, ContentPath path)
|
||||
{
|
||||
//check if file already in dict
|
||||
if (_loadedProcessors.ContainsKey(path.FullPath))
|
||||
{
|
||||
return FluentResults.Result.Ok();
|
||||
}
|
||||
//check if file exists
|
||||
if (_storageService.FileExists(path.FullPath) is {} result
|
||||
&& result.IsFailed | (result.IsSuccess & result.Value == false))
|
||||
{
|
||||
return FluentResults.Result.Fail(result.Errors)
|
||||
.WithError(new Error($"{nameof(StylesService)}.{nameof(LoadStylesFile)} file does not exist!")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, package));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var styleProcessor = new UIStyleProcessor(package, path);
|
||||
styleProcessor.LoadFile();
|
||||
_loadedProcessors.Add(path.FullPath, styleProcessor);
|
||||
}
|
||||
catch (InvalidDataException exception)
|
||||
{
|
||||
return FluentResults.Result.Fail(new Error($"{nameof(StylesService)}.{nameof(LoadStylesFile)} failed for ContentPackage {package.Name}: Exception: {exception.Message}")
|
||||
.WithMetadata(MetadataType.ExceptionDetails, exception.Message)
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, package)
|
||||
.WithMetadata(MetadataType.StackTrace, exception.StackTrace));
|
||||
}
|
||||
|
||||
return FluentResults.Result.Ok();
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public FluentResults.Result UnloadAllStyles()
|
||||
@@ -134,7 +108,7 @@ public class StylesService : IStylesService
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool NoProcessorsLoaded => _loadedProcessors.Count < 1;
|
||||
private bool NoProcessorsLoaded => _loadedProcessors.IsEmpty;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
@@ -146,4 +120,6 @@ public class StylesService : IStylesService
|
||||
{
|
||||
return UnloadAllStyles();
|
||||
}
|
||||
|
||||
public bool IsDisposed { get; private set; }
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.LuaCs.Events;
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Steam;
|
||||
using Microsoft.Xna.Framework;
|
||||
@@ -118,7 +119,6 @@ namespace Barotrauma
|
||||
ContentPackageManager.EnabledPackages.SetRegular(regularPackages);
|
||||
}
|
||||
GameMain.NetLobbyScreen.Select();
|
||||
GameMain.LuaCs.CheckInitialize();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ namespace Barotrauma
|
||||
ContentPackageManager.EnabledPackages.BackUp();
|
||||
ContentPackageManager.EnabledPackages.SetCore(corePackage);
|
||||
ContentPackageManager.EnabledPackages.SetRegular(regularPackages);
|
||||
|
||||
|
||||
//see if any of the packages we enabled contain subs that we were missing previously, and update their paths
|
||||
foreach (var serverSub in GameMain.Client.ServerSubmarines)
|
||||
{
|
||||
@@ -379,7 +379,6 @@ namespace Barotrauma
|
||||
}
|
||||
GameMain.NetLobbyScreen.UpdateSubList(GameMain.NetLobbyScreen.SubList, GameMain.Client.ServerSubmarines);
|
||||
GameMain.NetLobbyScreen.Select();
|
||||
GameMain.LuaCs.CheckInitialize();
|
||||
}
|
||||
}
|
||||
else if (GameMain.Client.FileReceiver.ActiveTransfers.None())
|
||||
|
||||
@@ -13,6 +13,7 @@ using System.Xml.Linq;
|
||||
using MoonSharp.Interpreter;
|
||||
using System.Net;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.LuaCs.Events;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -34,7 +35,8 @@ namespace Barotrauma
|
||||
set { world = value; }
|
||||
}
|
||||
|
||||
public static LuaCsSetup LuaCs;
|
||||
private static LuaCsSetup _luaCs;
|
||||
public static LuaCsSetup LuaCs => _luaCs ??= new LuaCsSetup();
|
||||
|
||||
public static GameServer Server;
|
||||
public static NetworkMember NetworkMember
|
||||
@@ -364,7 +366,7 @@ namespace Barotrauma
|
||||
TaskPool.Update();
|
||||
CoroutineManager.Update(paused: false, (float)Timing.Step);
|
||||
|
||||
GameMain.LuaCs.Update();
|
||||
LuaCs.EventService.PublishEvent<IEventUpdate>(sub => sub.OnUpdate(Timing.Step));
|
||||
performanceCounterTimer.Stop();
|
||||
if (GameMain.LuaCs.PerformanceCounter.EnablePerformanceCounter)
|
||||
{
|
||||
|
||||
22
Barotrauma/BarotraumaServer/ServerSource/LuaCs/LuaCsSetup.cs
Normal file
22
Barotrauma/BarotraumaServer/ServerSource/LuaCs/LuaCsSetup.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma;
|
||||
|
||||
partial class LuaCsSetup
|
||||
{
|
||||
/// <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)
|
||||
{
|
||||
// the server is always in the running state unless explicitly stopped.
|
||||
if (screen == UnimplementedScreen.Instance)
|
||||
SetRunState(RunState.Unloaded);
|
||||
SetRunState(RunState.Running);
|
||||
}
|
||||
|
||||
private partial bool ShouldRunCs() => IsCsEnabled.Value ||
|
||||
(GetPackage(new SteamWorkshopId(CsForBarotraumaSteamId.Value), false, false) is { }
|
||||
&& GameMain.Server.ServerPeer is LidgrenServerPeer);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Barotrauma.LuaCs.Services.Processing;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public partial class PackageService
|
||||
{
|
||||
public PackageService(
|
||||
Lazy<IModConfigCreatorService> configParserService,
|
||||
Lazy<ILuaScriptService> luaScriptService,
|
||||
Lazy<ILocalizationService> localizationService,
|
||||
Lazy<IPluginService> pluginService,
|
||||
Lazy<IConfigService> configService,
|
||||
IPackageManagementService packageManagementService,
|
||||
IStorageService storageService,
|
||||
ILoggerService loggerService)
|
||||
{
|
||||
_configParserService = configParserService;
|
||||
_luaScriptService = luaScriptService;
|
||||
_localizationService = localizationService;
|
||||
_pluginService = pluginService;
|
||||
_configService = configService;
|
||||
_packageManagementService = packageManagementService;
|
||||
_storageService = storageService;
|
||||
_loggerService = loggerService;
|
||||
}
|
||||
}
|
||||
@@ -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,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();
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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