-- Squash:
- In progress implementation of services model.
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.LuaCs.Configuration;
|
||||
|
||||
public class DisplayableData : IDisplayableData
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
public string ModName { get; private set; }
|
||||
public string DisplayName { get; private set; }
|
||||
public string DisplayModName { get; private set; }
|
||||
public string DisplayCategory { get; private set; }
|
||||
public string Tooltip { get; private set; }
|
||||
public string ImageIcon { get; private set; }
|
||||
public Point IconResolution { get; private set; }
|
||||
public bool ShowWhenNotLoaded { get; private set; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using Barotrauma.LuaCs.Configuration;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
|
||||
namespace Barotrauma.LuaCs.Configuration;
|
||||
|
||||
public partial interface IConfigBase : IDisplayableData, IDisplayableInitialize { }
|
||||
@@ -0,0 +1,66 @@
|
||||
using System.Numerics;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.LuaCs.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the Display Data for use with Menus.
|
||||
/// </summary>
|
||||
public interface IDisplayableData
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal name of the instance.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
/// <summary>
|
||||
/// Internal mod name of the instance. ContentPackage name will be used by default.
|
||||
/// </summary>
|
||||
string ModName { get; }
|
||||
/// <summary>
|
||||
/// The name to display in GUIs and Menus.
|
||||
/// </summary>
|
||||
string DisplayName { get; }
|
||||
/// <summary>
|
||||
/// The mod name to display in GUIs and Menus.
|
||||
/// </summary>
|
||||
string DisplayModName { get; }
|
||||
/// <summary>
|
||||
/// Category this instance falls under. Used by menus when filtering by category.
|
||||
/// </summary>
|
||||
string DisplayCategory { get; }
|
||||
/// <summary>
|
||||
/// The tooltip shown on hover.
|
||||
/// </summary>
|
||||
string Tooltip { get; }
|
||||
/// <summary>
|
||||
/// The fully qualified filepath to the image icon for this config.
|
||||
/// </summary>
|
||||
string ImageIcon { get; }
|
||||
/// <summary>
|
||||
/// Required if ImageIcon is set. X,Y resolution of the image.
|
||||
/// </summary>
|
||||
Point IconResolution { get; }
|
||||
/// <summary>
|
||||
/// Whether to show the entry in the menu when not loaded.
|
||||
/// </summary>
|
||||
bool ShowWhenNotLoaded { get; }
|
||||
}
|
||||
|
||||
public interface IDisplayableInitialize
|
||||
{
|
||||
void Initialize(IDisplayableData values);
|
||||
|
||||
// copy this as needed
|
||||
/*public void Initialize(IDisplayableData values)
|
||||
{
|
||||
this.Name = values.Name;
|
||||
this.ModName = values.ModName;
|
||||
this.DisplayName = values.DisplayName;
|
||||
this.DisplayModName = values.DisplayModName;
|
||||
this.DisplayCategory = values.DisplayCategory;
|
||||
this.Tooltip = values.Tooltip;
|
||||
this.ImageIcon = values.ImageIcon;
|
||||
this.IconResolution = values.IconResolution;
|
||||
this.ShowWhenNotLoaded = values.ShowWhenNotLoaded;
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public partial record ModConfigInfo : IModConfigInfo
|
||||
{
|
||||
public ImmutableArray<IStylesResourceInfo> StylesResourceInfos { get; init; }
|
||||
}
|
||||
|
||||
public record StylesResourceInfo : IStylesResourceInfo
|
||||
{
|
||||
public Platform SupportedPlatforms { get; init; }
|
||||
public Target SupportedTargets { get; init; }
|
||||
public int LoadPriority { get; init; }
|
||||
public ImmutableArray<string> FilePaths { get; init; }
|
||||
public bool Optional { get; init; }
|
||||
public ImmutableArray<CultureInfo> SupportedCultures { get; init; }
|
||||
public string InternalName { get; init; }
|
||||
public ImmutableArray<IPackageDependencyInfo> Dependencies { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
using Barotrauma.LuaCs.Configuration;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public partial interface IConfigInfo : IDisplayableData { }
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public partial interface IModConfigInfo : IStylesResourcesInfo { }
|
||||
|
||||
public interface IStylesResourceInfo : IResourceInfo, IResourceCultureInfo, ILoadableResourceInfo, IPackageDependenciesInfo { }
|
||||
|
||||
public interface IStylesResourcesInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Collection of loadable styles data.
|
||||
/// </summary>
|
||||
ImmutableArray<IStylesResourceInfo> StylesResourceInfos { get; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public interface IStylesInfo
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface IClientLoggerService : IService
|
||||
{
|
||||
void AddToGUIUpdateList();
|
||||
void ShowErrorOverlay(string message, float time = 5f, float duration = 1.5f);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
// TODO: Rework interface to support resource infos.
|
||||
/// <summary>
|
||||
/// Loads XML Style assets from the given content package.
|
||||
/// </summary>
|
||||
public interface IStylesService : IService
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries to load the styles file for the given contentpackage and path into a new UIStylesProcessor instance.
|
||||
/// </summary>
|
||||
/// <param name="package"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
bool TryLoadStylesFile(ContentPackage package, ContentPath path);
|
||||
/// <summary>
|
||||
/// Unloads all styles assets and UIStyleProcessor instances.
|
||||
/// </summary>
|
||||
void UnloadAllStyles();
|
||||
|
||||
/// <summary>
|
||||
/// Tries to the get the font 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.
|
||||
/// </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.
|
||||
/// </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.
|
||||
/// </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.
|
||||
/// </summary>
|
||||
/// <param name="colorName">XML Name of the asset.</param>
|
||||
/// <returns>The asset or null if none are found.</returns>
|
||||
GUIColor GetColor(string colorName);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public partial class LoggerService : ILoggerService, IClientLoggerService
|
||||
{
|
||||
private GUIFrame _overlayFrame;
|
||||
private GUITextBlock _textBlock;
|
||||
private double _showTimer = 0;
|
||||
|
||||
|
||||
private void CreateOverlay(string message)
|
||||
{
|
||||
_overlayFrame = new GUIFrame(new RectTransform(new Vector2(0.4f, 0.03f), null), null, new Color(50, 50, 50, 100))
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
GUILayoutGroup layout =
|
||||
new GUILayoutGroup(
|
||||
new RectTransform(new Vector2(0.8f, 0.8f), _overlayFrame.RectTransform, Anchor.CenterLeft), false,
|
||||
Anchor.Center);
|
||||
|
||||
_textBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0f), layout.RectTransform), message);
|
||||
_overlayFrame.RectTransform.MinSize = new Point((int)(_textBlock.TextSize.X * 1.2), 0);
|
||||
|
||||
layout.Recalculate();
|
||||
}
|
||||
|
||||
public void AddToGUIUpdateList()
|
||||
{
|
||||
if (_overlayFrame != null && Timing.TotalTime <= _showTimer)
|
||||
{
|
||||
_overlayFrame.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowErrorOverlay(string message, float time = 5f, float duration = 1.5f)
|
||||
{
|
||||
if (Timing.TotalTime <= _showTimer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CreateOverlay(message);
|
||||
|
||||
_overlayFrame.Flash(Color.Red, duration, true);
|
||||
_showTimer = Timing.TotalTime + time;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
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 PackageService(
|
||||
Lazy<IXmlModConfigConverterService> converterService,
|
||||
Lazy<ILegacyConfigService> legacyConfigService,
|
||||
Lazy<ILuaScriptService> luaScriptService,
|
||||
Lazy<ILocalizationService> localizationService,
|
||||
Lazy<IPluginService> pluginService,
|
||||
Lazy<IStylesService> stylesService,
|
||||
Lazy<IConfigService> configService,
|
||||
IPackageManagementService packageManagementService,
|
||||
IStorageService storageService,
|
||||
ILoggerService loggerService)
|
||||
{
|
||||
_modConfigConverterService = converterService;
|
||||
_legacyConfigService = legacyConfigService;
|
||||
_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 void LoadStyles([NotNull]IStylesResourcesInfo stylesInfo)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services.Processing;
|
||||
|
||||
#region XmlToResourceParsers
|
||||
public interface IXmlStylesToResConverterService : IXmlResourceConverterService<IStylesResourceInfo> { }
|
||||
|
||||
#endregion
|
||||
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public class StylesService : IStylesService
|
||||
{
|
||||
private readonly Dictionary<string, UIStyleProcessor> _loadedProcessors = new();
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly ILoggerService _loggerService;
|
||||
|
||||
public StylesService(IStorageService storageService, ILoggerService loggerService)
|
||||
{
|
||||
_storageService = storageService;
|
||||
_loggerService = loggerService;
|
||||
}
|
||||
|
||||
public bool TryLoadStylesFile(ContentPackage package, ContentPath path)
|
||||
{
|
||||
//check if file already in dict
|
||||
if (_loadedProcessors.ContainsKey(path.FullPath))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//check if file exists
|
||||
if (_storageService.FileExists(path.FullPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
var styleProcessor = new UIStyleProcessor(package, path);
|
||||
styleProcessor.LoadFile();
|
||||
_loadedProcessors.Add(path.FullPath, styleProcessor);
|
||||
}
|
||||
catch (InvalidDataException exception)
|
||||
{
|
||||
_loggerService.LogError($"XmlAssetService.TryLoadStylesFile failed for ContentPackage {package.Name}: Exception: {exception.Message}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void UnloadAllStyles()
|
||||
{
|
||||
if (NoProcessorsLoaded)
|
||||
return;
|
||||
|
||||
foreach (var processor in _loadedProcessors)
|
||||
{
|
||||
processor.Value.UnloadFile();
|
||||
}
|
||||
_loadedProcessors.Clear();
|
||||
}
|
||||
|
||||
public GUIFont GetFont(string fontName)
|
||||
{
|
||||
if (NoProcessorsLoaded)
|
||||
return null;
|
||||
foreach (var processor in _loadedProcessors.Values)
|
||||
{
|
||||
if (processor.Fonts.TryGetValue(fontName, out var asset))
|
||||
return asset;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public GUISprite GetSprite(string spriteName)
|
||||
{
|
||||
if (NoProcessorsLoaded)
|
||||
return null;
|
||||
foreach (var processor in _loadedProcessors.Values)
|
||||
{
|
||||
if (processor.Sprites.TryGetValue(spriteName, out var asset))
|
||||
return asset;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public GUISpriteSheet GetSpriteSheet(string spriteSheetName)
|
||||
{
|
||||
if (NoProcessorsLoaded)
|
||||
return null;
|
||||
foreach (var processor in _loadedProcessors.Values)
|
||||
{
|
||||
if (processor.SpriteSheets.TryGetValue(spriteSheetName, out var asset))
|
||||
return asset;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public GUICursor GetCursor(string cursorName)
|
||||
{
|
||||
if (NoProcessorsLoaded)
|
||||
return null;
|
||||
foreach (var processor in _loadedProcessors.Values)
|
||||
{
|
||||
if (processor.Cursors.TryGetValue(cursorName, out var asset))
|
||||
return asset;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public GUIColor GetColor(string colorName)
|
||||
{
|
||||
if (NoProcessorsLoaded)
|
||||
return null;
|
||||
foreach (var processor in _loadedProcessors.Values)
|
||||
{
|
||||
if (processor.Colors.TryGetValue(colorName, out var asset))
|
||||
return asset;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool NoProcessorsLoaded => _loadedProcessors.Count < 1;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
UnloadAllStyles();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
UnloadAllStyles();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public class UIStyleProcessor : HashlessFile
|
||||
{
|
||||
private readonly UIStyleFile _fake;
|
||||
public readonly Dictionary<string, GUIFont> Fonts = new();
|
||||
public readonly Dictionary<string, GUISprite> Sprites = new();
|
||||
public readonly Dictionary<string, GUISpriteSheet> SpriteSheets = new();
|
||||
public readonly Dictionary<string, GUICursor> Cursors = new();
|
||||
public readonly Dictionary<string, GUIColor> Colors = new();
|
||||
|
||||
public UIStyleProcessor(ContentPackage contentPackage, ContentPath path) : base(contentPackage, path)
|
||||
{
|
||||
_fake = new UIStyleFile(contentPackage, path);
|
||||
}
|
||||
|
||||
public override void LoadFile()
|
||||
{
|
||||
var element = XMLExtensions.TryLoadXml(path: Path)?.Root?.FromPackage(ContentPackage);
|
||||
if (element is null)
|
||||
throw new InvalidDataException($"UIStyleProcessor: Failed to load UI style file: {Path}");
|
||||
|
||||
var styleElement = element.Name.LocalName.ToLowerInvariant() == "style" ? element : element.GetChildElement("style");
|
||||
if (styleElement is null)
|
||||
throw new InvalidDataException($"UIStyleProcessor: no 'style' XmlElement found in file: {Path}");
|
||||
|
||||
var childElements = styleElement.GetChildElements("Font");
|
||||
if (childElements is not null)
|
||||
AddToList<GUIFont, GUIFontPrefab>(Fonts, childElements, _fake);
|
||||
|
||||
childElements = styleElement.GetChildElements("Sprite");
|
||||
if (childElements is not null)
|
||||
AddToList<GUISprite, GUISpritePrefab>(Sprites, childElements, _fake);
|
||||
|
||||
childElements = styleElement.GetChildElements("Spritesheet");
|
||||
if (childElements is not null)
|
||||
AddToList<GUISpriteSheet, GUISpriteSheetPrefab>(SpriteSheets, childElements, _fake);
|
||||
|
||||
childElements = styleElement.GetChildElements("Cursor");
|
||||
if (childElements is not null)
|
||||
AddToList<GUICursor, GUICursorPrefab>(Cursors, childElements, _fake);
|
||||
|
||||
childElements = styleElement.GetChildElements("Color");
|
||||
if (childElements is not null)
|
||||
AddToList<GUIColor, GUIColorPrefab>(Colors, childElements, _fake);
|
||||
|
||||
|
||||
void AddToList<T1, T2>(Dictionary<string, T1> dict, IEnumerable<ContentXElement> ele, UIStyleFile file) where T1 : GUISelector<T2> where T2 : GUIPrefab
|
||||
{
|
||||
foreach (ContentXElement prefabElement in ele)
|
||||
{
|
||||
string name = prefabElement.GetAttributeString("name", string.Empty);
|
||||
if (name != string.Empty)
|
||||
{
|
||||
var prefab = (T2)Activator.CreateInstance(typeof(T2), new object[]{ prefabElement, file })!;
|
||||
if (!dict.ContainsKey(name))
|
||||
dict[name] = (T1)Activator.CreateInstance(typeof(T1), new object[] { name })!;
|
||||
dict[name].Prefabs.Add(prefab, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void UnloadFile()
|
||||
{
|
||||
Fonts.Values.ForEach(p => p.Prefabs.RemoveByFile(_fake));
|
||||
Sprites.Values.ForEach(p => p.Prefabs.RemoveByFile(_fake));
|
||||
SpriteSheets.Values.ForEach(p => p.Prefabs.RemoveByFile(_fake));
|
||||
Cursors.Values.ForEach(p => p.Prefabs.RemoveByFile(_fake));
|
||||
Colors.Values.ForEach(p => p.Prefabs.RemoveByFile(_fake));
|
||||
|
||||
Fonts.Clear();
|
||||
Sprites.Clear();
|
||||
SpriteSheets.Clear();
|
||||
Cursors.Clear();
|
||||
Colors.Clear();
|
||||
}
|
||||
|
||||
public override void Sort()
|
||||
{
|
||||
Fonts.Values.ForEach(p => p.Prefabs.Sort());
|
||||
Sprites.Values.ForEach(p => p.Prefabs.Sort());
|
||||
SpriteSheets.Values.ForEach(p => p.Prefabs.Sort());
|
||||
Cursors.Values.ForEach(p => p.Prefabs.Sort());
|
||||
Colors.Values.ForEach(p => p.Prefabs.Sort());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
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<IXmlModConfigConverterService> converterService,
|
||||
Lazy<ILegacyConfigService> legacyConfigService,
|
||||
Lazy<ILuaScriptService> luaScriptService,
|
||||
Lazy<ILocalizationService> localizationService,
|
||||
Lazy<IPluginService> pluginService,
|
||||
Lazy<IConfigService> configService,
|
||||
IPackageManagementService packageManagementService,
|
||||
IStorageService storageService,
|
||||
ILoggerService loggerService)
|
||||
{
|
||||
_modConfigConverterService = converterService;
|
||||
_legacyConfigService = legacyConfigService;
|
||||
_luaScriptService = luaScriptService;
|
||||
_localizationService = localizationService;
|
||||
_pluginService = pluginService;
|
||||
_configService = configService;
|
||||
_packageManagementService = packageManagementService;
|
||||
_storageService = storageService;
|
||||
_loggerService = loggerService;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
<PackageReference Include="MonoMod.RuntimeDetour" Version="25.2.3" />
|
||||
<PackageReference Include="HarmonyX" Version="2.14.0" />
|
||||
<PackageReference Include="Sigil" Version="5.0.0" />
|
||||
<PackageReference Include="LightInject" Version="6.6.4" />
|
||||
<PackageReference Include="QuikGraph" Version="2.5.0" />
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Libraries\moonsharp\MoonSharp.Interpreter\MoonSharp.Interpreter.csproj" />
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Libraries\moonsharp\MoonSharp.VsCodeDebugger\MoonSharp.VsCodeDebugger.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma.LuaCs.Configuration;
|
||||
|
||||
public partial interface IConfigBase : IVarId
|
||||
{
|
||||
bool IsInitialized { get; }
|
||||
string GetValue();
|
||||
bool TrySetValue(string value);
|
||||
bool IsAssignable(string value);
|
||||
Type GetValueType();
|
||||
void Initialize(IVarId id, string defaultValue);
|
||||
}
|
||||
|
||||
public interface IVarId
|
||||
{
|
||||
Guid InstanceId { get; }
|
||||
string InternalName { get; }
|
||||
ContentPackage OwnerPackage { get; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using Barotrauma.LuaCs.Networking;
|
||||
|
||||
namespace Barotrauma.LuaCs.Configuration;
|
||||
|
||||
public interface IConfigEntry<T> : IConfigBase, INetVar where T : IConvertible, IEquatable<T>
|
||||
{
|
||||
T Value { get; }
|
||||
bool TrySetValue(T value);
|
||||
bool IsAssignable(T value);
|
||||
void Initialize(IVarId id, T defaultValue);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Barotrauma.LuaCs.Networking;
|
||||
|
||||
namespace Barotrauma.LuaCs.Configuration;
|
||||
|
||||
public interface IConfigList : IConfigBase, INetVar
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.LuaCs.Configuration;
|
||||
|
||||
public interface IConfigRangeEntry<T> : IConfigEntry<T> where T : IConvertible, IEquatable<T>
|
||||
{
|
||||
T MinValue { get; }
|
||||
T MaxValue { get; }
|
||||
|
||||
int GetStepCount();
|
||||
float GetRangeMin();
|
||||
float GetRangeMax();
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
#region ModConfigurationInfo
|
||||
|
||||
public partial record ModConfigInfo : IModConfigInfo
|
||||
{
|
||||
public ContentPackage Package { get; init; }
|
||||
public string PackageName { get; init; }
|
||||
public TargetRunMode RunModes { get; init; }
|
||||
|
||||
public ImmutableArray<CultureInfo> SupportedCultures { get; init; }
|
||||
public ImmutableArray<IAssemblyResourceInfo> Assemblies { get; init; }
|
||||
public ImmutableArray<ILocalizationResourceInfo> Localizations { get; init; }
|
||||
public ImmutableArray<ILuaResourceInfo> LuaScripts { get; init; }
|
||||
public ImmutableArray<IConfigResourceInfo> Configs { get; init; }
|
||||
public ImmutableArray<IConfigProfileResourceInfo> ConfigProfiles { get; init; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DataContracts
|
||||
|
||||
public record AssemblyResourceInfo : IAssemblyResourceInfo
|
||||
{
|
||||
public ContentPackage OwnerPackage { get; init; }
|
||||
public string FriendlyName { get; init; }
|
||||
public bool IsScript { get; init; }
|
||||
public string InternalName { get; init; }
|
||||
public bool LazyLoad { get; init; }
|
||||
public Platform SupportedPlatforms { get; init; }
|
||||
public Target SupportedTargets { get; init; }
|
||||
public int LoadPriority { get; init; }
|
||||
public ImmutableArray<string> FilePaths { get; init; }
|
||||
public ImmutableArray<CultureInfo> SupportedCultures { get; init; }
|
||||
public ImmutableArray<IPackageDependencyInfo> Dependencies { get; init; }
|
||||
public bool Optional { get; init; }
|
||||
}
|
||||
|
||||
public record DependencyInfo : IPackageDependencyInfo
|
||||
{
|
||||
public ContentPackage OwnerPackage { get; init; }
|
||||
public string FolderPath { get; init; }
|
||||
public string PackageName { get; init; }
|
||||
public ulong SteamWorkshopId { get; init; }
|
||||
public ContentPackage DependencyPackage { get; init; }
|
||||
}
|
||||
|
||||
public record LocalizationResourceInfo : ILocalizationResourceInfo
|
||||
{
|
||||
public ContentPackage OwnerPackage { get; init; }
|
||||
public CultureInfo TargetCulture { get; init; }
|
||||
public Platform SupportedPlatforms { get; init; }
|
||||
public Target SupportedTargets { get; init; }
|
||||
public int LoadPriority { get; init; }
|
||||
public ImmutableArray<string> FilePaths { get; init; }
|
||||
public ImmutableArray<CultureInfo> SupportedCultures { get; init; }
|
||||
public ImmutableArray<IPackageDependencyInfo> Dependencies { get; init; }
|
||||
public bool Optional { get; init; }
|
||||
}
|
||||
|
||||
public readonly struct LuaScriptResourceInfo : ILuaResourceInfo
|
||||
{
|
||||
public ContentPackage OwnerPackage { get; init; }
|
||||
public Platform SupportedPlatforms { get; init; }
|
||||
public Target SupportedTargets { get; init; }
|
||||
public int LoadPriority { get; init; }
|
||||
public ImmutableArray<string> FilePaths { get; init; }
|
||||
public ImmutableArray<CultureInfo> SupportedCultures { get; init; }
|
||||
public ImmutableArray<IPackageDependencyInfo> Dependencies { get; init; }
|
||||
public bool Optional { get; init; }
|
||||
public string InternalName { get; init; }
|
||||
public bool LazyLoad { get; init; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
[Flags]
|
||||
public enum Platform
|
||||
{
|
||||
Linux=0x1,
|
||||
OSX=0x2,
|
||||
Windows=0x4
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum Target
|
||||
{
|
||||
Client=0x1,
|
||||
Server=0x2
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum TargetRunMode
|
||||
{
|
||||
ClientEnabled = 0x1,
|
||||
ClientAlways = 0x2,
|
||||
ServerEnabled = 0x4,
|
||||
ServerAlways = 0x8
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public interface IPlatformInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Platforms that these localization files should be loaded for.
|
||||
/// </summary>
|
||||
[Required]
|
||||
Platform SupportedPlatforms { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Targets that these localization files should be loaded for.
|
||||
/// </summary>
|
||||
[Required]
|
||||
Target SupportedTargets { get; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Which package does the following data belong to?
|
||||
/// </summary>
|
||||
public interface IPackageInfo
|
||||
{
|
||||
ContentPackage OwnerPackage { get; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ResourceInfos contain metadata about a resource.
|
||||
/// </summary>
|
||||
public interface IResourceInfo : IPlatformInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// [Optional]
|
||||
/// Allows you to specify the loading order for all assets of the same type (ie. styles, assemblies, etc.).
|
||||
/// </summary>
|
||||
int LoadPriority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Resource absolute file paths.
|
||||
/// </summary>
|
||||
[Required]
|
||||
ImmutableArray<string> FilePaths { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Marks this resource as optional (ie. Cross-CP content). Setting this to true will allow the dependency system to
|
||||
/// try and order the loading but not fail if it runs into circular dependency issues.
|
||||
/// </summary>
|
||||
bool Optional { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Information about supported cultures. It is intended to be ignored if the array is ImmutableArray.Empty .
|
||||
/// </summary>
|
||||
public interface IResourceCultureInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// List of supported cultures by this resource.
|
||||
/// </summary>
|
||||
ImmutableArray<CultureInfo> SupportedCultures { get; }
|
||||
}
|
||||
|
||||
|
||||
public interface ILoadableResourceInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// [UNIQUE] The name that will be used when trying to reference this resource for execution or loading.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string InternalName { get; }
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
// TODO: Finish
|
||||
public partial interface IConfigInfo
|
||||
{
|
||||
string Name { get; }
|
||||
string PackageName { get; }
|
||||
ConfigDataType Type { get; }
|
||||
string DefaultValue { get; }
|
||||
ClientPermissions RequiredPermissions { get; }
|
||||
}
|
||||
|
||||
public enum ConfigDataType
|
||||
{
|
||||
Boolean, Int32, Int64, Single, Double, String,
|
||||
Color, Vector2, Vector3, List,
|
||||
RangeInt32, RangeSingle, ControlInput
|
||||
}
|
||||
|
||||
public enum NetSync
|
||||
{
|
||||
None, TwoWay, ServerAuthority, ClientOneWay
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public interface IConfigProfileInfo
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public interface ILocalizationInfo
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public partial interface IModConfigInfo : IResourceCultureInfo, IAssembliesResourcesInfo,
|
||||
ILocalizationsResourcesInfo, ILuaScriptsResourcesInfo, IConfigsResourcesInfo,
|
||||
IConfigProfilesResourcesInfo
|
||||
{
|
||||
// package info
|
||||
ContentPackage Package { get; }
|
||||
string PackageName { get; }
|
||||
// configuration
|
||||
TargetRunMode RunModes { get; }
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public interface IPackageDependencyInfo : IPackageInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Root folder of the content package.
|
||||
/// </summary>
|
||||
public string FolderPath { get; }
|
||||
/// <summary>
|
||||
/// Name of the package.
|
||||
/// </summary>
|
||||
public string PackageName { get; }
|
||||
/// <summary>
|
||||
/// Steam ID of the package.
|
||||
/// </summary>
|
||||
public ulong SteamWorkshopId { get; }
|
||||
/// <summary>
|
||||
/// The dependency package, if found in the ALL Packages List.
|
||||
/// </summary>
|
||||
public ContentPackage DependencyPackage { get; }
|
||||
}
|
||||
|
||||
public interface IPackageDependenciesInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// List of required packages.
|
||||
/// </summary>
|
||||
ImmutableArray<IPackageDependencyInfo> Dependencies { get; }
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
public interface IConfigResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IPackageInfo { }
|
||||
public interface IConfigProfileResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IPackageInfo { }
|
||||
public interface ILocalizationResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, IPackageInfo { }
|
||||
/// <summary>
|
||||
/// Represents loadable Lua files.
|
||||
/// </summary>
|
||||
public interface ILuaResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, ILoadableResourceInfo, IPackageInfo { }
|
||||
public interface IAssemblyResourceInfo : IResourceInfo, IResourceCultureInfo, IPackageDependenciesInfo, ILoadableResourceInfo, IPackageInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The friendly name of the assembly. Script files belonging to the same assembly should all have the same name.
|
||||
/// Legacy scripts will all be given the sanitized name of the Content Package they belong to.
|
||||
/// </summary>
|
||||
public string FriendlyName { get; }
|
||||
/// <summary>
|
||||
/// Is this entry referring to a script file collection.
|
||||
/// </summary>
|
||||
public bool IsScript { get; }
|
||||
}
|
||||
|
||||
|
||||
#region Collections
|
||||
|
||||
public interface IAssembliesResourcesInfo
|
||||
{
|
||||
ImmutableArray<IAssemblyResourceInfo> Assemblies { get; }
|
||||
}
|
||||
|
||||
public interface ILocalizationsResourcesInfo
|
||||
{
|
||||
ImmutableArray<ILocalizationResourceInfo> Localizations { get; }
|
||||
}
|
||||
|
||||
public interface ILuaScriptsResourcesInfo
|
||||
{
|
||||
ImmutableArray<ILuaResourceInfo> LuaScripts { get; }
|
||||
}
|
||||
|
||||
public interface IConfigsResourcesInfo
|
||||
{
|
||||
ImmutableArray<IConfigResourceInfo> Configs { get; }
|
||||
}
|
||||
|
||||
public interface IConfigProfilesResourcesInfo
|
||||
{
|
||||
ImmutableArray<IConfigProfileResourceInfo> ConfigProfiles { get; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Legacy data contract for the old run configuration system. Should be deprecated
|
||||
/// once no longer needed.
|
||||
/// </summary>
|
||||
public interface IRunConfig
|
||||
{
|
||||
bool UseNonPublicizedAssemblies { get; }
|
||||
bool AutoGenerated { get; }
|
||||
bool UseInternalAssemblyName { get; }
|
||||
string Client { get; }
|
||||
string Server { get; }
|
||||
|
||||
bool IsForced();
|
||||
bool IsStandard();
|
||||
bool IsForcedOrStandard();
|
||||
}
|
||||
@@ -19,7 +19,7 @@ namespace Barotrauma
|
||||
|
||||
#if SERVER
|
||||
private const string LogPrefix = "SV";
|
||||
private const int NetMaxLength = 1024;
|
||||
private const int NetMaxLength = 1024; // character limit of vanilla Barotrauma's chat system.
|
||||
private const int NetMaxMessages = 60;
|
||||
|
||||
// This is used so its possible to call logging functions inside the serverLog
|
||||
|
||||
@@ -11,6 +11,7 @@ using MoonSharp.VsCodeDebugger;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.LuaCs.Services;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -99,7 +100,7 @@ namespace Barotrauma
|
||||
public CsPackageManager PluginPackageManager => _pluginPackageManager ??= new CsPackageManager(AssemblyManager, this);
|
||||
|
||||
public LuaCsModStore ModStore { get; private set; }
|
||||
private LuaRequire require { get; set; }
|
||||
private LuaRequire Require { get; set; }
|
||||
public LuaCsSetupConfig Config { get; private set; }
|
||||
public MoonSharpVsCodeDebugServer DebugServer { get; private set; }
|
||||
public bool IsInitialized { get; private set; }
|
||||
@@ -320,6 +321,7 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
PluginPackageManager.UnloadPlugins();
|
||||
@@ -395,7 +397,7 @@ namespace Barotrauma
|
||||
Lua.Options.CheckThreadAccess = false;
|
||||
Script.GlobalOptions.ShouldPCallCatchException = (Exception ex) => { return true; };
|
||||
|
||||
require = new LuaRequire(Lua);
|
||||
Require = new LuaRequire(Lua);
|
||||
|
||||
Game = new LuaGame();
|
||||
Networking = new LuaCsNetworking();
|
||||
@@ -428,7 +430,7 @@ namespace Barotrauma
|
||||
|
||||
Lua.Globals["dofile"] = (Func<string, Table, string, DynValue>)DoFile;
|
||||
Lua.Globals["loadfile"] = (Func<string, Table, string, DynValue>)LoadFile;
|
||||
Lua.Globals["require"] = (Func<string, Table, DynValue>)require.Require;
|
||||
Lua.Globals["require"] = (Func<string, Table, DynValue>)Require.Require;
|
||||
|
||||
Lua.Globals["dostring"] = (Func<string, Table, string, DynValue>)Lua.DoString;
|
||||
Lua.Globals["load"] = (Func<string, Table, string, DynValue>)Lua.LoadString;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.LuaCs.Networking;
|
||||
|
||||
public partial interface INetCallback
|
||||
{
|
||||
public ushort CallbackId { get; }
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
public partial interface INetCallback
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
using Barotrauma.LuaCs.Configuration;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.LuaCs.Networking;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma.LuaCs.Networking;
|
||||
|
||||
public interface INetVar : IVarId
|
||||
{
|
||||
/// <summary>
|
||||
/// Synchronized network id, uninitialized if value is zero/0. Used by Networking service.
|
||||
/// </summary>
|
||||
ushort NetId { get; }
|
||||
/// <summary>
|
||||
/// Synchronization type
|
||||
/// </summary>
|
||||
NetSync SyncType { get; }
|
||||
/// <summary>
|
||||
/// Permissions needed by clients to send net-events or receive net messages.
|
||||
/// </summary>
|
||||
ClientPermissions WritePermissions { get; }
|
||||
void ReadNetMessage(INetReadMessage message);
|
||||
void WriteNetMessage(INetWriteMessage message);
|
||||
void Initialize(ushort netId, NetSync syncMode, ClientPermissions writePermissions);
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.LuaCs.Networking;
|
||||
|
||||
#region Wrapper-IWriteMessage
|
||||
|
||||
/// <summary>
|
||||
/// Literally just exists because Barotrauma.IWriteMessage is internal only.
|
||||
/// </summary>
|
||||
public interface INetWriteMessage
|
||||
{
|
||||
internal IWriteMessage Message { get; }
|
||||
internal void SetMessage(IWriteMessage msg);
|
||||
|
||||
void WriteBoolean(bool val) => Message.WriteBoolean(val);
|
||||
|
||||
void WritePadBits() => Message.WritePadBits();
|
||||
|
||||
void WriteByte(byte val) => Message.WriteByte(val);
|
||||
|
||||
void WriteInt16(short val) => Message.WriteInt16(val);
|
||||
|
||||
void WriteUInt16(ushort val) => Message.WriteUInt16(val);
|
||||
|
||||
void WriteInt32(int val) => Message.WriteInt32(val);
|
||||
|
||||
void WriteUInt32(uint val) => Message.WriteUInt32(val);
|
||||
|
||||
void WriteInt64(long val) => Message.WriteInt64(val);
|
||||
|
||||
void WriteUInt64(ulong val) => Message.WriteUInt64(val);
|
||||
|
||||
void WriteSingle(float val) => Message.WriteSingle(val);
|
||||
|
||||
void WriteDouble(double val) => Message.WriteDouble(val);
|
||||
|
||||
void WriteColorR8G8B8(Color val) => Message.WriteColorR8G8B8(val);
|
||||
|
||||
void WriteColorR8G8B8A8(Color val) => Message.WriteColorR8G8B8A8(val);
|
||||
|
||||
void WriteVariableUInt32(uint val) => Message.WriteVariableUInt32(val);
|
||||
|
||||
void WriteString(string val) => Message.WriteString(val);
|
||||
|
||||
void WriteIdentifier(Identifier val) => Message.WriteIdentifier(val);
|
||||
|
||||
void WriteRangedInteger(int val, int min, int max) => Message.WriteRangedInteger(val, min, max);
|
||||
|
||||
void WriteRangedSingle(float val, float min, float max, int bitCount) =>
|
||||
Message.WriteRangedSingle(val, min, max, bitCount);
|
||||
|
||||
void WriteBytes(byte[] val, int startIndex, int length) => Message.WriteBytes(val, startIndex, length);
|
||||
|
||||
byte[] PrepareForSending(bool compressPastThreshold, out bool isCompressed, out int outLength) =>
|
||||
Message.PrepareForSending(compressPastThreshold, out isCompressed, out outLength);
|
||||
|
||||
int BitPosition
|
||||
{
|
||||
get => Message.BitPosition;
|
||||
set => Message.BitPosition = value;
|
||||
}
|
||||
|
||||
int BytePosition => Message.BytePosition;
|
||||
|
||||
byte[] Buffer => Message.Buffer;
|
||||
|
||||
int LengthBits
|
||||
{
|
||||
get => Message.LengthBits;
|
||||
set => Message.LengthBits = value;
|
||||
}
|
||||
|
||||
int LengthBytes => Message.LengthBytes;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Wrapper-IReadMessage
|
||||
|
||||
/// <summary>
|
||||
/// Literally just exists because Barotrauma.IReadMessage is internal only.
|
||||
/// </summary>
|
||||
public interface INetReadMessage
|
||||
{
|
||||
internal IReadMessage Message { get; }
|
||||
internal void SetMessage(IReadMessage msg);
|
||||
|
||||
bool ReadBoolean() => Message.ReadBoolean();
|
||||
void ReadPadBits() => Message.ReadPadBits();
|
||||
byte ReadByte() => Message.ReadByte();
|
||||
byte PeekByte() => Message.PeekByte();
|
||||
ushort ReadUInt16() => Message.ReadUInt16();
|
||||
short ReadInt16() => Message.ReadInt16();
|
||||
uint ReadUInt32() => Message.ReadUInt32();
|
||||
int ReadInt32() => Message.ReadInt32();
|
||||
ulong ReadUInt64() => Message.ReadUInt64();
|
||||
long ReadInt64() => Message.ReadInt64();
|
||||
float ReadSingle() => Message.ReadSingle();
|
||||
double ReadDouble() => Message.ReadDouble();
|
||||
uint ReadVariableUInt32() => Message.ReadVariableUInt32();
|
||||
string ReadString() => Message.ReadString();
|
||||
Identifier ReadIdentifier() => Message.ReadIdentifier();
|
||||
Color ReadColorR8G8B8() => Message.ReadColorR8G8B8();
|
||||
Color ReadColorR8G8B8A8() => Message.ReadColorR8G8B8A8();
|
||||
int ReadRangedInteger(int min, int max) => Message.ReadRangedInteger(min, max);
|
||||
float ReadRangedSingle(float min, float max, int bitCount) => Message.ReadRangedSingle(min, max, bitCount);
|
||||
byte[] ReadBytes(int numberOfBytes) => Message.ReadBytes(numberOfBytes);
|
||||
int BitPosition
|
||||
{
|
||||
get => Message.BitPosition;
|
||||
set => Message.BitPosition = value;
|
||||
}
|
||||
int BytePosition => Message.BytePosition;
|
||||
byte[] Buffer => Message.Buffer;
|
||||
int LengthBits
|
||||
{
|
||||
get => Message.LengthBits;
|
||||
set => Message.LengthBits = value;
|
||||
}
|
||||
int LengthBytes => Message.LengthBytes;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HelperImplementations
|
||||
|
||||
public class NetWriteMessage : INetWriteMessage
|
||||
{
|
||||
private IWriteMessage Message { get; set; }
|
||||
|
||||
IWriteMessage INetWriteMessage.Message => Message;
|
||||
|
||||
void INetWriteMessage.SetMessage(IWriteMessage msg)
|
||||
{
|
||||
Message = msg;
|
||||
}
|
||||
}
|
||||
|
||||
public class NetReadMessage : INetReadMessage
|
||||
{
|
||||
private IReadMessage Message { get; set; }
|
||||
IReadMessage INetReadMessage.Message => Message;
|
||||
|
||||
void INetReadMessage.SetMessage(IReadMessage msg)
|
||||
{
|
||||
Message = msg;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -8,6 +8,7 @@ using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Barotrauma.LuaCs.Services;
|
||||
using Barotrauma.Steam;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
@@ -149,23 +150,23 @@ public sealed class CsPackageManager : IDisposable
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not assemblies have been loaded.
|
||||
/// Whether assemblies have been loaded.
|
||||
/// </summary>
|
||||
public bool AssembliesLoaded { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not loaded plugins had their preloader run.
|
||||
/// Whether loaded plugins had their preloader run.
|
||||
/// </summary>
|
||||
public bool PluginsPreInit { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not plugins' types have been instantiated.
|
||||
/// Whether plugins' types have been instantiated.
|
||||
/// </summary>
|
||||
public bool PluginsInitialized { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not plugins are fully loaded.
|
||||
/// Whether plugins are fully loaded.
|
||||
/// </summary>
|
||||
public bool PluginsLoaded { get; private set; }
|
||||
|
||||
@@ -966,7 +967,7 @@ public sealed class CsPackageManager : IDisposable
|
||||
/// <param name="cannotLoadPackages">Packages with errors or cyclic dependencies. Element is error message. Null if empty.</param>
|
||||
/// <param name="packageChecksPredicate">Optional: Allows for a custom checks to be performed on each package.
|
||||
/// Returns a bool indicating if the package is ready to load.</param>
|
||||
/// <returns>Whether or not the process produces a usable list.</returns>
|
||||
/// <returns>Whether the process produces a usable list.</returns>
|
||||
private static bool OrderAndFilterPackagesByDependencies(
|
||||
Dictionary<ContentPackage, ImmutableList<ContentPackage>> packages,
|
||||
out IEnumerable<ContentPackage> readyToLoad,
|
||||
|
||||
@@ -7,13 +7,14 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Loader;
|
||||
using Barotrauma.LuaCs.Services;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalse
|
||||
|
||||
[assembly: InternalsVisibleTo("CompiledAssembly")]
|
||||
|
||||
namespace Barotrauma;
|
||||
namespace Barotrauma.LuaCs;
|
||||
|
||||
/// <summary>
|
||||
/// AssemblyLoadContext to compile from syntax trees in memory and to load from disk/file. Provides dependency resolution.
|
||||
@@ -31,11 +32,11 @@ public class MemoryFileAssemblyContextLoader : AssemblyLoadContext
|
||||
// internal
|
||||
private readonly Dictionary<string, AssemblyDependencyResolver> _dependencyResolvers = new(); // path-folder, resolver
|
||||
protected bool IsResolving; //this is to avoid circular dependency lookup.
|
||||
private AssemblyManager _assemblyManager;
|
||||
private IAssemblyManagementService _assemblyManager;
|
||||
public bool IsTemplateMode { get; set; }
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
public MemoryFileAssemblyContextLoader(AssemblyManager assemblyManager) : base(isCollectible: true)
|
||||
public MemoryFileAssemblyContextLoader(IAssemblyManagementService assemblyManager) : base(isCollectible: true)
|
||||
{
|
||||
this._assemblyManager = assemblyManager;
|
||||
this.IsDisposed = false;
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Xml.Serialization;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
|
||||
namespace Barotrauma;
|
||||
|
||||
[Serializable]
|
||||
public sealed class RunConfig
|
||||
public sealed class RunConfig : IRunConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// How should scripts be run on the server.
|
||||
/// </summary>
|
||||
[XmlElement(ElementName = "Server")]
|
||||
[DefaultValue("Standard")]
|
||||
public string Server;
|
||||
public string Server { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// How should scripts be run on the client.
|
||||
/// </summary>
|
||||
[XmlElement(ElementName = "Client")]
|
||||
[DefaultValue("Standard")]
|
||||
public string Client;
|
||||
public string Client { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of dependencies by either Steam Workshop ID or by Partial Inclusive Name (ie. "ModDep" will match a mod named "A ModDependency").
|
||||
|
||||
@@ -14,7 +14,7 @@ using Microsoft.CodeAnalysis.CSharp;
|
||||
// ReSharper disable EventNeverSubscribedTo.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace Barotrauma;
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
/***
|
||||
* Note: This class was written to be thread-safe in order to allow parallelization in loading in the future if the need
|
||||
@@ -25,36 +25,14 @@ namespace Barotrauma;
|
||||
/// Provides functionality for the loading, unloading and management of plugins implementing IAssemblyPlugin.
|
||||
/// All plugins are loaded into their own AssemblyLoadContext along with their dependencies.
|
||||
/// </summary>
|
||||
public class AssemblyManager
|
||||
public class AssemblyManager : IAssemblyManagementService
|
||||
{
|
||||
#region ExternalAPI
|
||||
|
||||
/// <summary>
|
||||
/// Called when an assembly is loaded.
|
||||
/// </summary>
|
||||
|
||||
public event Action<Assembly> OnAssemblyLoaded;
|
||||
|
||||
/// <summary>
|
||||
/// Called when an assembly is marked for unloading, before unloading begins. You should use this to cleanup
|
||||
/// any references that you have to this assembly.
|
||||
/// </summary>
|
||||
public event Action<Assembly> OnAssemblyUnloading;
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever an exception is thrown. First arg is a formatted message, Second arg is the Exception.
|
||||
/// </summary>
|
||||
public event Action<string, Exception> OnException;
|
||||
|
||||
/// <summary>
|
||||
/// For unloading issue debugging. Called whenever MemoryFileAssemblyContextLoader [load context] is unloaded.
|
||||
/// </summary>
|
||||
public event Action<Guid> OnACLUnload;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// [DEBUG ONLY]
|
||||
/// Returns a list of the current unloading ACLs.
|
||||
/// </summary>
|
||||
public ImmutableList<WeakReference<MemoryFileAssemblyContextLoader>> StillUnloadingACLs
|
||||
{
|
||||
get
|
||||
@@ -70,12 +48,6 @@ public class AssemblyManager
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ReSharper disable once MemberCanBePrivate.Global
|
||||
/// <summary>
|
||||
/// Checks if there are any AssemblyLoadContexts still in the process of unloading.
|
||||
/// </summary>
|
||||
public bool IsCurrentlyUnloading
|
||||
{
|
||||
get
|
||||
@@ -95,21 +67,6 @@ public class AssemblyManager
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Old API compatibility
|
||||
public IEnumerable<Type> GetSubTypesInLoadedAssemblies<T>()
|
||||
{
|
||||
return GetSubTypesInLoadedAssemblies<T>(false);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Allows iteration over all non-interface types in all loaded assemblies in the AsmMgr that are assignable to the given type (IsAssignableFrom).
|
||||
/// Warning: care should be used when using this method in hot paths as performance may be affected.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to compare against</typeparam>
|
||||
/// <param name="rebuildList">Forces caches to clear and for the lists of types to be rebuilt.</param>
|
||||
/// <returns>An Enumerator for matching types.</returns>
|
||||
public IEnumerable<Type> GetSubTypesInLoadedAssemblies<T>(bool rebuildList)
|
||||
{
|
||||
Type targetType = typeof(T);
|
||||
@@ -165,14 +122,6 @@ public class AssemblyManager
|
||||
OpsLockLoaded.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get types assignable to type from the ACL given the Guid.
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="types"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public bool TryGetSubTypesFromACL<T>(Guid id, out IEnumerable<Type> types)
|
||||
{
|
||||
Type targetType = typeof(T);
|
||||
@@ -188,13 +137,6 @@ public class AssemblyManager
|
||||
types = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get types from the ACL given the Guid.
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="types"></param>
|
||||
/// <returns></returns>
|
||||
public bool TryGetSubTypesFromACL(Guid id, out IEnumerable<Type> types)
|
||||
{
|
||||
if (TryGetACL(id, out var acl))
|
||||
@@ -206,14 +148,6 @@ public class AssemblyManager
|
||||
types = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Allows iteration over all types, including interfaces, in all loaded assemblies in the AsmMgr who's names match the string.
|
||||
/// Note: Will return the by-reference equivalent type if the type name is prefixed with "out " or "ref ".
|
||||
/// </summary>
|
||||
/// <param name="typeName">The string name of the type to search for.</param>
|
||||
/// <returns>An Enumerator for matching types. List will be empty if bad params are supplied.</returns>
|
||||
public IEnumerable<Type> GetTypesByName(string typeName)
|
||||
{
|
||||
List<Type> types = new();
|
||||
@@ -299,12 +233,6 @@ public class AssemblyManager
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows iteration over all types (including interfaces) in all loaded assemblies managed by the AsmMgr.
|
||||
/// Warning: High usage may result in performance issues.
|
||||
/// </summary>
|
||||
/// <returns>An Enumerator for iteration.</returns>
|
||||
public IEnumerable<Type> GetAllTypesInLoadedAssemblies()
|
||||
{
|
||||
OpsLockLoaded.EnterReadLock();
|
||||
@@ -325,13 +253,6 @@ public class AssemblyManager
|
||||
OpsLockLoaded.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all loaded ACLs.
|
||||
/// WARNING: References to these ACLs outside of the AssemblyManager should be kept in a WeakReference in order
|
||||
/// to avoid causing issues with unloading/disposal.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<LoadedACL> GetAllLoadedACLs()
|
||||
{
|
||||
OpsLockLoaded.EnterReadLock();
|
||||
@@ -358,36 +279,14 @@ public class AssemblyManager
|
||||
|
||||
#region InternalAPI
|
||||
|
||||
/// <summary>
|
||||
/// [Unsafe] Warning: only for use in nested threading functions. Requires care to manage access.
|
||||
/// Does not make any guarantees about the state of the ACL after the list has been returned.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.NoInlining)]
|
||||
internal ImmutableList<LoadedACL> UnsafeGetAllLoadedACLs()
|
||||
ImmutableList<LoadedACL> IAssemblyManagementService.UnsafeGetAllLoadedACLs()
|
||||
{
|
||||
if (LoadedACLs.IsEmpty)
|
||||
return ImmutableList<LoadedACL>.Empty;
|
||||
return LoadedACLs.Select(kvp => kvp.Value).ToImmutableList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by content package and plugin management to stop unloading of a given ACL until all plugins have gracefully closed.
|
||||
/// </summary>
|
||||
public event System.Func<LoadedACL, bool> IsReadyToUnloadACL;
|
||||
|
||||
/// <summary>
|
||||
/// Compiles an assembly from supplied references and syntax trees into the specified AssemblyContextLoader.
|
||||
/// A new ACL will be created if the Guid supplied is Guid.Empty.
|
||||
/// </summary>
|
||||
/// <param name="compiledAssemblyName"></param>
|
||||
/// <param name="syntaxTree"></param>
|
||||
/// <param name="externalMetadataReferences"></param>
|
||||
/// <param name="compilationOptions"></param>
|
||||
/// <param name="friendlyName">A non-unique name for later reference. Optional, set to null if unused.</param>
|
||||
/// <param name="id">The guid of the assembly </param>
|
||||
/// <param name="externFileAssemblyRefs"></param>
|
||||
/// <returns></returns>
|
||||
public AssemblyLoadingSuccessState LoadAssemblyFromMemory([NotNull] string compiledAssemblyName,
|
||||
[NotNull] IEnumerable<SyntaxTree> syntaxTree,
|
||||
IEnumerable<MetadataReference> externalMetadataReferences,
|
||||
@@ -440,14 +339,6 @@ public class AssemblyManager
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Switches the ACL with the given Guid to Template Mode, which disables assembly name resolution for any assemblies loaded in it.
|
||||
/// These ACLs are intended to be used to host Assemblies for information only and not for code execution.
|
||||
/// WARNING: This process is irreversible.
|
||||
/// </summary>
|
||||
/// <param name="guid">Guid of the ACL.</param>
|
||||
/// <returns>Whether or not an ACL was found with the given ID.</returns>
|
||||
public bool SetACLToTemplateMode(Guid guid)
|
||||
{
|
||||
if (!TryGetACL(guid, out var acl))
|
||||
@@ -455,16 +346,6 @@ public class AssemblyManager
|
||||
acl.Acl.IsTemplateMode = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to load all assemblies at the supplied file paths list into the ACl with the given Guid.
|
||||
/// If the supplied Guid is Empty, then a new ACl will be created and the Guid will be assigned to it.
|
||||
/// </summary>
|
||||
/// <param name="filePaths">List of assemblies to try and load.</param>
|
||||
/// <param name="friendlyName">A non-unique name for later reference. Optional.</param>
|
||||
/// <param name="id">Guid of the ACL or Empty if none specified. Guid of ACL will be assigned to this var.</param>
|
||||
/// <returns>Operation success messages.</returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public AssemblyLoadingSuccessState LoadAssembliesFromLocations([NotNull] IEnumerable<string> filePaths,
|
||||
string friendlyName, ref Guid id)
|
||||
{
|
||||
@@ -507,9 +388,7 @@ public class AssemblyManager
|
||||
|
||||
return AssemblyLoadingSuccessState.ACLLoadFailure;
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Synchronized)]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public bool TryBeginDispose()
|
||||
{
|
||||
OpsLockLoaded.EnterWriteLock();
|
||||
@@ -563,8 +442,6 @@ public class AssemblyManager
|
||||
OpsLockLoaded.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public bool FinalizeDispose()
|
||||
{
|
||||
@@ -606,15 +483,7 @@ public class AssemblyManager
|
||||
|
||||
return isUnloaded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to retrieve the LoadedACL with the given ID or null if none is found.
|
||||
/// WARNING: External references to this ACL with long lifespans should be kept in a WeakReference
|
||||
/// to avoid causing unloading/disposal issues.
|
||||
/// </summary>
|
||||
/// <param name="id">GUID of the ACL.</param>
|
||||
/// <param name="acl">The found ACL or null if none was found.</param>
|
||||
/// <returns>Whether or not an ACL was found.</returns>
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public bool TryGetACL(Guid id, out LoadedACL acl)
|
||||
{
|
||||
@@ -865,6 +734,16 @@ public class AssemblyManager
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
TryBeginDispose();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
TryBeginDispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AssemblyExtensions
|
||||
@@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
|
||||
public interface IAssemblyManagementService : IService
|
||||
{
|
||||
#region Public API
|
||||
|
||||
/// <summary>
|
||||
/// Called when an assembly is loaded.
|
||||
/// </summary>
|
||||
public event Action<Assembly> OnAssemblyLoaded;
|
||||
|
||||
/// <summary>
|
||||
/// Called when an assembly is marked for unloading, before unloading begins. You should use this to cleanup
|
||||
/// any references that you have to this assembly.
|
||||
/// </summary>
|
||||
public event Action<Assembly> OnAssemblyUnloading;
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever an exception is thrown. First arg is a formatted message, Second arg is the Exception.
|
||||
/// </summary>
|
||||
public event Action<string, Exception> OnException;
|
||||
|
||||
/// <summary>
|
||||
/// For unloading issue debugging. Called whenever MemoryFileAssemblyContextLoader [load context] is unloaded.
|
||||
/// </summary>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public event Action<Guid> OnACLUnload;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// [DEBUG ONLY]
|
||||
/// Returns a list of the current unloading ACLs.
|
||||
/// </summary>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public ImmutableList<WeakReference<MemoryFileAssemblyContextLoader>> StillUnloadingACLs { get; }
|
||||
|
||||
// ReSharper disable once MemberCanBePrivate.Global
|
||||
/// <summary>
|
||||
/// Checks if there are any AssemblyLoadContexts still in the process of unloading.
|
||||
/// </summary>
|
||||
public bool IsCurrentlyUnloading { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Allows iteration over all non-interface types in all loaded assemblies in the AsmMgr that are assignable to the given type (IsAssignableFrom).
|
||||
/// Warning: care should be used when using this method in hot paths as performance may be affected.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to compare against</typeparam>
|
||||
/// <param name="rebuildList">Forces caches to clear and for the lists of types to be rebuilt.</param>
|
||||
/// <returns>An Enumerator for matching types.</returns>
|
||||
public IEnumerable<Type> GetSubTypesInLoadedAssemblies<T>(bool rebuildList);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get types assignable to type from the ACL given the Guid.
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="types"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns>Operation success.</returns>
|
||||
public bool TryGetSubTypesFromACL<T>(Guid id, out IEnumerable<Type> types);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get types from the ACL given the Guid.
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="types"></param>
|
||||
/// <returns></returns>
|
||||
public bool TryGetSubTypesFromACL(Guid id, out IEnumerable<Type> types);
|
||||
|
||||
/// <summary>
|
||||
/// Allows iteration over all types, including interfaces, in all loaded assemblies in the AsmMgr who's names match the string.
|
||||
/// Note: Will return the by-reference equivalent type if the type name is prefixed with "out " or "ref ".
|
||||
/// </summary>
|
||||
/// <param name="typeName">The string name of the type to search for.</param>
|
||||
/// <returns>An Enumerator for matching types. List will be empty if bad params are supplied.</returns>
|
||||
public IEnumerable<Type> GetTypesByName(string typeName);
|
||||
|
||||
/// <summary>
|
||||
/// Allows iteration over all types (including interfaces) in all loaded assemblies managed by the AsmMgr.
|
||||
/// Warning: High usage may result in performance issues.
|
||||
/// </summary>
|
||||
/// <returns>An Enumerator for iteration.</returns>
|
||||
public IEnumerable<Type> GetAllTypesInLoadedAssemblies();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all loaded ACLs.
|
||||
/// WARNING: References to these ACLs outside the AssemblyManager should be kept in a WeakReference in order
|
||||
/// to avoid causing issues with unloading/disposal.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<AssemblyManager.LoadedACL> GetAllLoadedACLs();
|
||||
|
||||
#endregion
|
||||
|
||||
#region InternalAPI
|
||||
/*** Notes: Internal API uses the 'public' modifier because of the common and recommended use of publicized APIs
|
||||
* by third-party add-ins.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// [Unsafe] Warning: only for use in nested threading functions. Requires care to manage access.
|
||||
/// Does not make any guarantees about the state of the ACL after the list has been returned.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ImmutableList<AssemblyManager.LoadedACL> UnsafeGetAllLoadedACLs();
|
||||
|
||||
/// <summary>
|
||||
/// Used by content package and plugin management to stop unloading of a given ACL until all plugins have gracefully closed.
|
||||
/// </summary>
|
||||
public event System.Func<AssemblyManager.LoadedACL, bool> IsReadyToUnloadACL;
|
||||
|
||||
/// <summary>
|
||||
/// Compiles an assembly from supplied references and syntax trees into the specified AssemblyContextLoader.
|
||||
/// A new ACL will be created if the Guid supplied is Guid.Empty.
|
||||
/// </summary>
|
||||
/// <param name="compiledAssemblyName"></param>
|
||||
/// <param name="syntaxTree"></param>
|
||||
/// <param name="externalMetadataReferences"></param>
|
||||
/// <param name="compilationOptions"></param>
|
||||
/// <param name="friendlyName">A non-unique name for later reference. Optional, set to null if unused.</param>
|
||||
/// <param name="id">The guid of the assembly </param>
|
||||
/// <param name="externFileAssemblyRefs"></param>
|
||||
/// <returns></returns>
|
||||
public AssemblyLoadingSuccessState LoadAssemblyFromMemory([NotNull] string compiledAssemblyName,
|
||||
[NotNull] IEnumerable<SyntaxTree> syntaxTree,
|
||||
IEnumerable<MetadataReference> externalMetadataReferences,
|
||||
[NotNull] CSharpCompilationOptions compilationOptions,
|
||||
string friendlyName,
|
||||
ref Guid id,
|
||||
IEnumerable<Assembly> externFileAssemblyRefs = null);
|
||||
|
||||
/// <summary>
|
||||
/// Switches the ACL with the given Guid to Template Mode, which disables assembly name resolution for any assemblies loaded in it.
|
||||
/// These ACLs are intended to be used to host Assemblies for information only and not for code execution.
|
||||
/// WARNING: This process is irreversible.
|
||||
/// </summary>
|
||||
/// <param name="guid">Guid of the ACL.</param>
|
||||
/// <returns>Whether an ACL was found with the given ID.</returns>
|
||||
public bool SetACLToTemplateMode(Guid guid);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Tries to load all assemblies at the supplied file paths list into the ACl with the given Guid.
|
||||
/// If the supplied Guid is Empty, then a new ACl will be created and the Guid will be assigned to it.
|
||||
/// </summary>
|
||||
/// <param name="filePaths">List of assemblies to try and load.</param>
|
||||
/// <param name="friendlyName">A non-unique name for later reference. Optional.</param>
|
||||
/// <param name="id">Guid of the ACL or Empty if none specified. Guid of ACL will be assigned to this var.</param>
|
||||
/// <returns>Operation success messages.</returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public AssemblyLoadingSuccessState LoadAssembliesFromLocations([NotNull] IEnumerable<string> filePaths,
|
||||
string friendlyName, ref Guid id);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Tries to begin the disposal process of ACLs.
|
||||
/// </summary>
|
||||
/// <returns>Returns whether the unloading process could be initiated.</returns>
|
||||
public bool TryBeginDispose();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether unloading is completed and updates the styate of the unloading cache.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool FinalizeDispose();
|
||||
|
||||
/// <summary>
|
||||
/// Tries to retrieve the LoadedACL with the given ID or null if none is found.
|
||||
/// WARNING: External references to this ACL with long lifespans should be kept in a WeakReference
|
||||
/// to avoid causing unloading/disposal issues.
|
||||
/// </summary>
|
||||
/// <param name="id">GUID of the ACL.</param>
|
||||
/// <param name="acl">The found ACL or null if none was found.</param>
|
||||
/// <returns>Whether an ACL was found.</returns>
|
||||
public bool TryGetACL(Guid id, out AssemblyManager.LoadedACL acl);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Barotrauma.LuaCs.Configuration;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface IConfigService : IService
|
||||
{
|
||||
/*
|
||||
* Resource Files.
|
||||
*/
|
||||
bool TryAddConfigs(ImmutableArray<IConfigResourceInfo> configResources);
|
||||
bool TryAddConfigsProfiles(ImmutableArray<IConfigProfileResourceInfo> configProfileResources);
|
||||
void RemoveConfigs(ImmutableArray<IConfigResourceInfo> configResources);
|
||||
void RemoveConfigsProfiles(ImmutableArray<IConfigProfileResourceInfo> configProfilesResources);
|
||||
|
||||
|
||||
/*
|
||||
* Already processed
|
||||
*/
|
||||
bool TryAddConfigs(ImmutableArray<IConfigInfo> configs);
|
||||
bool TryAddConfigsProfiles(ImmutableArray<IConfigProfileInfo> configProfiles);
|
||||
void RemoveConfigs(ImmutableArray<IConfigInfo> configs);
|
||||
void RemoveConfigsProfiles(ImmutableArray<IConfigProfileInfo> configProfiles);
|
||||
|
||||
/*
|
||||
* Immediate mode, does not have displayable functionality
|
||||
*/
|
||||
IConfigEntry<T> AddConfigEntry<T>(ContentPackage package, string name,
|
||||
T defaultValue,
|
||||
NetSync syncMode = NetSync.None,
|
||||
ClientPermissions permissions = ClientPermissions.None,
|
||||
Func<T, bool> valueChangePredicate = null,
|
||||
Action<IConfigEntry<T>> onValueChanged = null) where T : IConvertible, IEquatable<T>;
|
||||
|
||||
IConfigList AddConfigList(ContentPackage package, string name,
|
||||
int defaultIndex, IReadOnlyList<string> values,
|
||||
NetSync syncMode = NetSync.None,
|
||||
ClientPermissions permissions = ClientPermissions.None,
|
||||
Func<IConfigList, int, bool> valueChangePredicate = null,
|
||||
Action<IConfigList, int> onValueChanged = null);
|
||||
|
||||
IReadOnlyDictionary<string, IConfigBase> GetConfigsForPackage(ContentPackage package);
|
||||
IReadOnlyDictionary<string, IConfigBase> GetConfigsForPackage(string packageName);
|
||||
IReadOnlyDictionary<(ContentPackage, string), IConfigBase> GetAllConfigs();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface IEventService
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface IHookManagementService : IService
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Barotrauma.LuaCs.Data;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface ILegacyConfigService : IService
|
||||
{
|
||||
bool TryBuildModConfigFromLegacy(ContentPackage package, out IModConfigInfo configInfo);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface ILocalizationService : IService
|
||||
{
|
||||
IReadOnlyCollection<CultureInfo> GetLoadedLocales();
|
||||
void Remove(ImmutableArray<ILocalizationResourceInfo> localizations);
|
||||
bool TrySetCurrentCulture(CultureInfo culture);
|
||||
bool TrySetCurrentCulture(string cultureName);
|
||||
bool TryLoadLocalizations(ImmutableArray<ILocalizationResourceInfo> localizationResources);
|
||||
string GetLocalizedString(string key, string fallback);
|
||||
string GetLocalizedString(string key, CultureInfo targetCulture);
|
||||
bool TryRegisterLocalizationResolver(CultureInfo targetCulture, Func<string, CultureInfo, string> factoryResolver);
|
||||
bool ReplaceSymbols(string text, string symbolExpr);
|
||||
bool IsCurrentCultureSupported(IResourceCultureInfo culturesInfo);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides console and debug logging services
|
||||
/// </summary>
|
||||
public interface ILoggerService : IService
|
||||
{
|
||||
void HandleException(Exception exception, string prefix = null);
|
||||
void LogError(string message);
|
||||
void LogWarning(string message);
|
||||
void LogMessage(string message, Color? serverColor = null, Color? clientColor = null);
|
||||
void Log(string message, Color? color = null, ServerLog.MessageType messageType = ServerLog.MessageType.ServerMessage);
|
||||
|
||||
#region DebugBuilds
|
||||
|
||||
void LogDebug(string message, Color? color = null);
|
||||
void LogDebugWarning(string message);
|
||||
void LogDebugError(string message);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
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 : IService
|
||||
{
|
||||
#region Script_File_Collector
|
||||
|
||||
/// <summary>
|
||||
/// Adds the script files to the runner but does not execute them.
|
||||
/// </summary>
|
||||
/// <param name="luaResource"></param>
|
||||
/// <returns></returns>
|
||||
bool TryAddScriptFiles(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>
|
||||
bool TryExecuteScripts(bool pauseExecutionOnScriptError = false, bool verboseLogging = false);
|
||||
ImmutableArray<ILuaResourceInfo> GetScriptResources();
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public interface ILuaScriptManagementService : IService
|
||||
{
|
||||
#region Script_File_Execution
|
||||
|
||||
bool TryExecuteLoadedScripts(ContentPackage package, bool pauseExecutionOnError = false, bool verboseLogging = false);
|
||||
bool TryExecuteLoadedScripts(ImmutableArray<ILuaResourceInfo> scripts, bool pauseExecutionOnError = false, bool verboseLogging = false);
|
||||
bool TryExecuteLoadedScripts(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
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.LuaCs.Networking;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface INetworkingService : IService
|
||||
{
|
||||
bool IsActive { get; }
|
||||
bool IsSynchronized { get; }
|
||||
bool TryRegisterVar(INetVar var, NetSync mode, ClientPermissions permissions);
|
||||
void UnregisterVar(Guid varId);
|
||||
bool SendEvent(Guid varId);
|
||||
void SendMessageGlobal(string id, string message);
|
||||
void Synchronize();
|
||||
|
||||
#region LegacyAPI
|
||||
|
||||
bool RestrictMessageSize { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface IPackageManagementService : IService
|
||||
{
|
||||
void AddPackages(ref ReadOnlySpan<(ContentPackage, bool)> packages,
|
||||
bool executeImmediately = false,
|
||||
bool errorOnFailures = false,
|
||||
bool errorOnExistingPackageFound = false);
|
||||
void LoadPackages(bool onlyUnloadedPackages = true, bool rescanPackages = false);
|
||||
void UnloadPackages(bool errorOnFailures = true);
|
||||
bool IsPackageLoaded(ContentPackage package);
|
||||
bool CheckDependencyLoaded(IPackageDependencyInfo info);
|
||||
bool CheckDependenciesLoaded(IEnumerable<IPackageDependencyInfo> infos, out IReadOnlyList<IPackageDependencyInfo> missingPackages);
|
||||
bool CheckEnvironmentSupported(IPlatformInfo platform);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface IPackageService : IService,
|
||||
// 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; }
|
||||
/// <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 and any information was found. Will return false for purely vanilla packages.</returns>
|
||||
bool TryLoadResourcesInfo([NotNull]ContentPackage 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>
|
||||
void LoadPlugins([NotNull]IAssembliesResourcesInfo assembliesInfo, bool ignoreDependencySorting = false);
|
||||
void LoadLocalizations([NotNull]ILocalizationsResourcesInfo localizationsInfo);
|
||||
void AddLuaScripts([NotNull]ILuaScriptsResourcesInfo luaScriptsInfo);
|
||||
#if CLIENT
|
||||
void LoadStyles([NotNull]IStylesResourcesInfo stylesInfo);
|
||||
#endif
|
||||
void LoadConfig([NotNull]IConfigsResourcesInfo configsResourcesInfo, [NotNull]IConfigProfilesResourcesInfo configProfilesResourcesInfo);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface IPluginManagementService : IService
|
||||
{
|
||||
bool IsAssemblyLoadedGlobal(string friendlyName);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Reflection;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface IPluginService : IService
|
||||
{
|
||||
bool IsAssemblyLoaded(string friendlyName);
|
||||
/// <summary>
|
||||
/// Loads the assemblies for the given information
|
||||
/// </summary>
|
||||
/// <param name="assemblyResourcesInfo"></param>
|
||||
/// <param name="injectServices"></param>
|
||||
/// <param name="typeInstances"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
bool TryLoadAndInstanceTypes<T>(IEnumerable<IAssemblyResourceInfo> assemblyResourcesInfo, bool injectServices, out ImmutableArray<T> typeInstances) where T : class, IAssemblyPlugin;
|
||||
ImmutableArray<T> GetLoadedPluginTypesInPackage<T>() where T : class, IAssemblyPlugin;
|
||||
/// <summary>
|
||||
/// Advances the loading/execution state of the plugin. IMPORTANT: You cannot set the execution state of plugins
|
||||
/// to 'Disposed'. You must instead call the 'DisposePlugins' method.
|
||||
/// </summary>
|
||||
/// <param name="newState"></param>
|
||||
/// <returns></returns>
|
||||
bool AdvancePluginStates(PluginRunState newState);
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of all running plugins hosted by the service and releases their references to allow unloading.
|
||||
/// </summary>
|
||||
/// <returns>Success of the operation. Returns false if any plugin threw errors during disposal.</returns>
|
||||
bool DisposePlugins();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current plugin execution state.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
PluginRunState GetPluginRunState();
|
||||
}
|
||||
|
||||
public enum PluginRunState
|
||||
{
|
||||
Instanced=0,
|
||||
PreInitialization=1,
|
||||
Initialized=2,
|
||||
LoadingCompleted=3,
|
||||
Disposed=4
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Base interface inherited by all services
|
||||
/// </summary>
|
||||
public interface IService : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the service to its original state (post-instantiation).
|
||||
/// Allows a service instance to be reused without disposing of the instance.
|
||||
/// </summary>
|
||||
void Reset();
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using LightInject;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides instancing and management of IServices.
|
||||
/// </summary>
|
||||
public interface IServicesProvider
|
||||
{
|
||||
#region Type_Registration
|
||||
|
||||
/// <summary>
|
||||
/// Registers a type as a service for a given interface.
|
||||
/// </summary>
|
||||
/// <param name="lifetime"></param>
|
||||
/// <param name="lifetimeInstance"></param>
|
||||
/// <typeparam name="TSvcInterface"></typeparam>
|
||||
/// <typeparam name="TService"></typeparam>
|
||||
void RegisterServiceType<TSvcInterface, TService>(ServiceLifetime lifetime, ILifetime lifetimeInstance = null) where TSvcInterface : class, IService where TService : class, IService, TSvcInterface, new();
|
||||
|
||||
/// <summary>
|
||||
/// Registers a type as a service for a given interface that can be requested by name.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="lifetime"></param>
|
||||
/// <param name="lifetimeInstance"></param>
|
||||
/// <typeparam name="TSvcInterface"></typeparam>
|
||||
/// <typeparam name="TService"></typeparam>
|
||||
void RegisterServiceType<TSvcInterface, TService>(string name, ServiceLifetime lifetime, ILifetime lifetimeInstance = null) where TSvcInterface : class, IService where TService : class, IService, TSvcInterface, new();
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever a new service type for a given interface is implemented.
|
||||
/// Args[0]: Interface type
|
||||
/// Args[1]: Implementing type
|
||||
/// </summary>
|
||||
event System.Action<Type, Type> OnServiceRegistered;
|
||||
|
||||
/// <summary>
|
||||
/// Runs compilation of registered services.
|
||||
/// </summary>
|
||||
public void Compile();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Services_Instancing_Injection
|
||||
|
||||
/// <summary>
|
||||
/// Injects services into the properties of already instanced objects.
|
||||
/// </summary>
|
||||
/// <param name="inst"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
void InjectServices<T>(T inst) where T : class;
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a service for the given interface, returns success/failure.
|
||||
/// </summary>
|
||||
/// <param name="service"></param>
|
||||
/// <param name="lifetime"></param>
|
||||
/// <typeparam name="TSvcInterface"></typeparam>
|
||||
/// <returns></returns>
|
||||
bool TryGetService<TSvcInterface>(out IService service) where TSvcInterface : class, IService;
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a service for the given name and interface, returns success/failure.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="service"></param>
|
||||
/// <param name="lifetime"></param>
|
||||
/// <typeparam name="TSvcInterface"></typeparam>
|
||||
/// <returns></returns>
|
||||
bool TryGetService<TSvcInterface>(string name, out IService service) where TSvcInterface : class, IService;
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever a new service is created/instanced.
|
||||
/// Args[0]: The interface type of the service.
|
||||
/// Args[1]: The instance of the service.
|
||||
/// </summary>
|
||||
event System.Action<Type, IService> OnServiceInstanced;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ActiveServices
|
||||
|
||||
/// <summary>
|
||||
/// Returns all services for the given interface.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSvc"></typeparam>
|
||||
/// <returns></returns>
|
||||
ImmutableArray<TSvc> GetAllServices<TSvc>() where TSvc : class, IService;
|
||||
|
||||
#endregion
|
||||
|
||||
// Notes: Left public due to the common use of Publicizers
|
||||
#region Internal_Use
|
||||
|
||||
/// <summary>
|
||||
/// Notes: Internal use only if hosted by LuaCsForBarotrauma. Disposes of all services and resets DI container. Warning: unable to dispose of services held by other objects.
|
||||
/// </summary>
|
||||
void DisposeAndReset();
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public enum ServiceLifetime
|
||||
{
|
||||
Transient, Singleton, PerThread, Invalid, Custom
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface IStorageService : IService
|
||||
{
|
||||
#region LocalGameData
|
||||
|
||||
bool TryLoadLocalXml(ContentPackage package, string localFilePath, out XDocument document);
|
||||
bool TryLoadLocalBinary(ContentPackage package, string localFilePath, out byte[] bytes);
|
||||
bool TryLoadLocalText(ContentPackage package, string localFilePath, out string text);
|
||||
bool FileExistsInLocalData(ContentPackage package, string localFilePath);
|
||||
|
||||
#endregion
|
||||
|
||||
#region ContentPackageData
|
||||
bool TryLoadPackageXml(ContentPackage package, string localFilePath, out XDocument document);
|
||||
bool TryLoadPackageBinary(ContentPackage package, string localFilePath, out byte[] bytes);
|
||||
bool TryLoadPackageText(ContentPackage package, string localFilePath, out string text);
|
||||
|
||||
ImmutableArray<bool> TryLoadPackageXmlFiles(ContentPackage package, ImmutableArray<string> localFilePath, out ImmutableArray<XDocument> document);
|
||||
ImmutableArray<bool> TryLoadPackageBinaryFiles(ContentPackage package, ImmutableArray<string> localFilePath, out ImmutableArray<byte[]> bytes);
|
||||
ImmutableArray<bool> TryLoadPackageTextFiles(ContentPackage package, ImmutableArray<string> localFilePath, out ImmutableArray<string> text);
|
||||
|
||||
bool FindFilesInPackage(ContentPackage package, string localSubfolder, string regexFilter, bool searchRecursively, out ImmutableArray<string> localFilePaths);
|
||||
bool FileExistsInPackage(ContentPackage package, string localFilePath);
|
||||
|
||||
#endregion
|
||||
|
||||
#region AbsolutePaths
|
||||
|
||||
bool TryLoadXml(string filePath, out XDocument document);
|
||||
bool TrySaveXml(string filePath, in XDocument document);
|
||||
bool TryLoadBinary(string filePath, out byte[] bytes);
|
||||
bool TrySaveBinary(string filePath, in byte[] bytes);
|
||||
bool TryLoadText(string filePath, out string text);
|
||||
bool TrySaveText(string filePath, string text);
|
||||
bool FileExists(string filePath);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MoonSharp.Interpreter;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public partial class LoggerService : ILoggerService
|
||||
{
|
||||
public bool HideUserNames = true;
|
||||
|
||||
#if SERVER
|
||||
private const string LogPrefix = "SV";
|
||||
private const int NetMaxLength = 1024; // character limit of vanilla Barotrauma's chat system.
|
||||
private const int NetMaxMessages = 60;
|
||||
|
||||
// This is used so it's possible to call logging functions inside the serverLog
|
||||
// hook without creating an infinite loop
|
||||
private bool _lockLog = false;
|
||||
#else
|
||||
private const string LogPrefix = "CL";
|
||||
#endif
|
||||
|
||||
public void HandleException(Exception exception, string prefix = null)
|
||||
{
|
||||
string errorString = "";
|
||||
switch (exception)
|
||||
{
|
||||
case NetRuntimeException netRuntimeException:
|
||||
if (netRuntimeException.DecoratedMessage == null)
|
||||
{
|
||||
errorString = $"{prefix ?? ""}{netRuntimeException.ToString()}";
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME: netRuntimeException.ToString() doesn't print the InnerException's stack trace...
|
||||
errorString = $"{prefix ?? ""}{netRuntimeException.DecoratedMessage}: {netRuntimeException}";
|
||||
}
|
||||
break;
|
||||
case InterpreterException interpreterException:
|
||||
if (interpreterException.DecoratedMessage == null)
|
||||
{
|
||||
errorString = $"{prefix ?? ""}{interpreterException.ToString()}";
|
||||
}
|
||||
else
|
||||
{
|
||||
errorString = $"{prefix ?? ""}{interpreterException.DecoratedMessage}";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
string s = exception.StackTrace != null ? exception.ToString() : $"{exception}\n{Environment.StackTrace}";
|
||||
errorString = $"{prefix ?? ""}{s}";
|
||||
break;
|
||||
}
|
||||
|
||||
LogError(prefix + Environment.UserName + " " + errorString);
|
||||
}
|
||||
|
||||
public void LogError(string message)
|
||||
{
|
||||
if (HideUserNames && !Environment.UserName.IsNullOrEmpty())
|
||||
{
|
||||
message = message.Replace(Environment.UserName, "USERNAME");
|
||||
}
|
||||
|
||||
Log($"{message}", Color.Red, ServerLog.MessageType.Error);
|
||||
}
|
||||
|
||||
public void LogWarning(string message)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void LogMessage(string message, Color? serverColor = null, Color? clientColor = null)
|
||||
{
|
||||
serverColor ??= Color.MediumPurple;
|
||||
clientColor ??= Color.Purple;
|
||||
|
||||
#if SERVER
|
||||
Log(message, serverColor);
|
||||
#else
|
||||
Log(message, clientColor);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Log(string message, Color? color = null, ServerLog.MessageType messageType = ServerLog.MessageType.ServerMessage)
|
||||
{
|
||||
DebugConsole.NewMessage(message, color);
|
||||
|
||||
#if SERVER
|
||||
void BroadcastMessage(string m)
|
||||
{
|
||||
foreach (var client in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
ChatMessage consoleMessage = ChatMessage.Create("", m, ChatMessageType.Console, null, textColor: color);
|
||||
GameMain.Server.SendDirectChatMessage(consoleMessage, client);
|
||||
|
||||
if (!GameMain.Server.ServerSettings.SaveServerLogs || !client.HasPermission(ClientPermissions.ServerLog))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ChatMessage logMessage = ChatMessage.Create(messageType.ToString(), "[LuaCs] " + m, ChatMessageType.ServerLog, null);
|
||||
GameMain.Server.SendDirectChatMessage(logMessage, client);
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
if (GameMain.Server.ServerSettings.SaveServerLogs)
|
||||
{
|
||||
string logMessage = "[LuaCs] " + message;
|
||||
GameMain.Server.ServerSettings.ServerLog.WriteLine(logMessage, messageType, false);
|
||||
|
||||
if (!_lockLog)
|
||||
{
|
||||
_lockLog = true;
|
||||
GameMain.LuaCs?.Hook?.Call("serverLog", logMessage, messageType);
|
||||
_lockLog = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < message.Length; i += NetMaxLength)
|
||||
{
|
||||
string subStr = message.Substring(i, Math.Min(1024, message.Length - i));
|
||||
BroadcastMessage(subStr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public void LogDebug(string message, Color? color = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void LogDebugWarning(string message)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void LogDebugError(string message)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
public void Reset() { }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public class LuaScriptService
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public class PackageManagementService : IPackageManagementService, IPluginManagementService
|
||||
{
|
||||
private readonly Func<IPackageService> _contentPackageServiceFactory;
|
||||
private readonly Lazy<IAssemblyManagementService> _assemblyManagementService;
|
||||
|
||||
public PackageManagementService(
|
||||
Func<IPackageService> getPackageService,
|
||||
Lazy<IAssemblyManagementService> assemblyManagementService)
|
||||
{
|
||||
this._contentPackageServiceFactory = getPackageService;
|
||||
this._assemblyManagementService = assemblyManagementService;
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// TODO release managed resources here
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsAssemblyLoadedGlobal(string friendlyName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void AddPackages(ref ReadOnlySpan<(ContentPackage, bool)> packages, bool executeImmediately = false, bool errorOnFailures = false,
|
||||
bool errorOnExistingPackageFound = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void LoadPackages(bool onlyUnloadedPackages = true, bool rescanPackages = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void UnloadPackages(bool errorOnFailures = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsPackageLoaded(ContentPackage package)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool CheckDependencyLoaded(IPackageDependencyInfo info)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool CheckDependenciesLoaded(IEnumerable<IPackageDependencyInfo> infos, out IReadOnlyList<IPackageDependencyInfo> missingPackages)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool CheckEnvironmentSupported(IPlatformInfo platform)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,627 @@
|
||||
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;
|
||||
|
||||
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<IXmlModConfigConverterService> _modConfigConverterService;
|
||||
private readonly Lazy<ILegacyConfigService> _legacyConfigService;
|
||||
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;
|
||||
|
||||
public bool ConfigsLoaded
|
||||
{
|
||||
get => GetThreadSafeBool(ref _configsLoaded);
|
||||
private set => SetThreadSafeBool(ref _configsLoaded, value);
|
||||
}
|
||||
public bool LocalizationsLoaded
|
||||
{
|
||||
get => GetThreadSafeBool(ref _localizationsLoaded);
|
||||
private set => SetThreadSafeBool(ref _localizationsLoaded, value);
|
||||
}
|
||||
public bool LuaScriptsLoaded
|
||||
{
|
||||
get => GetThreadSafeBool(ref _luaScriptsLoaded);
|
||||
private set => SetThreadSafeBool(ref _luaScriptsLoaded, value);
|
||||
}
|
||||
public bool PluginsLoaded
|
||||
{
|
||||
get => GetThreadSafeBool(ref _pluginsLoaded);
|
||||
private set => SetThreadSafeBool(ref _pluginsLoaded, value);
|
||||
}
|
||||
public bool IsDisposed
|
||||
{
|
||||
get => GetThreadSafeBool(ref _isDisposed);
|
||||
private set => SetThreadSafeBool(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#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 bool TryLoadResourcesInfo(ContentPackage package)
|
||||
{
|
||||
_operationsUsageLock.EnterWriteLock();
|
||||
LoadingOperationsRunning = true;
|
||||
try
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException($"This package service instance is disposed!");
|
||||
}
|
||||
|
||||
// try loading the ModConfig.xml. If it fails, use the Legacy loader to try and construct one from the package structure.
|
||||
if (_storageService.TryLoadPackageXml(package, "ModConfig.xml", out var configXml)
|
||||
&& configXml.Root is not null)
|
||||
{
|
||||
if (_modConfigConverterService.Value.TryParseResource(configXml.Root, out IModConfigInfo configInfo))
|
||||
{
|
||||
ModConfigInfo = configInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
_loggerService.LogError(
|
||||
$"Failed to parse ModConfig.xml for package {package.Name}, package mod content not loaded.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (_legacyConfigService.Value.TryBuildModConfigFromLegacy(package, out var legacyConfig))
|
||||
{
|
||||
ModConfigInfo = legacyConfig;
|
||||
}
|
||||
else
|
||||
{
|
||||
// vanilla mod or broken
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
LoadingOperationsRunning = false;
|
||||
_operationsUsageLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadPlugins([NotNull]IAssembliesResourcesInfo assembliesInfo, bool ignoreDependencySorting = false)
|
||||
{
|
||||
_operationsUsageLock.EnterReadLock();
|
||||
LoadingOperationsRunning = true;
|
||||
try
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException($"This package service instance is disposed!");
|
||||
}
|
||||
|
||||
SanitationChecksCore(assembliesInfo, "assemblies", nameof(LoadPlugins));
|
||||
SanitationChecksEnumerable(assembliesInfo.Assemblies, "assemblies", nameof(LoadPlugins));
|
||||
|
||||
#if DEBUG
|
||||
assembliesInfo.Assemblies.ForEach(ari =>
|
||||
{
|
||||
if (!this.Assemblies.Contains(ari))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Package Service: tried to load the assembly resource {ari.InternalName} for package {this.Package.Name} but it is not in the list for this package.");
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
// 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.TryLoadAndInstanceTypes<IAssemblyPlugin>(resources, true, out var instancedTypes))
|
||||
{
|
||||
throw new TypeLoadException($"PackageService: unable to load assemblies for package {this.Package.Name}! Aborting loading!");
|
||||
}
|
||||
|
||||
PluginsLoaded = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
LoadingOperationsRunning = false;
|
||||
_operationsUsageLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadLocalizations([NotNull]ILocalizationsResourcesInfo localizationsInfo)
|
||||
{
|
||||
_operationsUsageLock.EnterReadLock();
|
||||
LoadingOperationsRunning = true;
|
||||
try
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException($"This package service instance is disposed!");
|
||||
}
|
||||
|
||||
SanitationChecksCore(localizationsInfo, "localizations", nameof(LoadLocalizations));
|
||||
SanitationChecksEnumerable(localizationsInfo.Localizations, "localizations", nameof(LoadLocalizations));
|
||||
|
||||
#if DEBUG
|
||||
localizationsInfo.Localizations.ForEach(ri =>
|
||||
{
|
||||
if (!this.Localizations.Contains(ri))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Package Service: tried to load the localization resource for package {this.Package.Name} but it is not in the list for this package.");
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
if (!_localizationService.Value.TryLoadLocalizations(localizationsInfo.Localizations))
|
||||
{
|
||||
throw new FileLoadException($"Package Service: unable to load localizations for package {this.Package.Name}! Aborting!");
|
||||
}
|
||||
|
||||
LocalizationsLoaded = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
LoadingOperationsRunning = false;
|
||||
_operationsUsageLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddLuaScripts([NotNull]ILuaScriptsResourcesInfo luaScriptsInfo)
|
||||
{
|
||||
_operationsUsageLock.EnterReadLock();
|
||||
LoadingOperationsRunning = true;
|
||||
try
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException($"This package service instance is disposed!");
|
||||
}
|
||||
|
||||
SanitationChecksCore(luaScriptsInfo, "luaScripts", nameof(AddLuaScripts));
|
||||
SanitationChecksEnumerable(luaScriptsInfo.LuaScripts, "luaScripts", nameof(AddLuaScripts));
|
||||
|
||||
#if DEBUG
|
||||
luaScriptsInfo.LuaScripts.ForEach(ri =>
|
||||
{
|
||||
if (!this.LuaScripts.Contains(ri))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Package Service: tried to load the lua script resource for package {this.Package.Name} but it is not in the list for this package.");
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
if (!_luaScriptService.Value.TryAddScriptFiles(luaScriptsInfo.LuaScripts))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Package Service: unable to add lua files for package {this.Package.Name}! Aborting!");
|
||||
}
|
||||
|
||||
LuaScriptsLoaded = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
LoadingOperationsRunning = false;
|
||||
_operationsUsageLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadConfig(
|
||||
[NotNull]IConfigsResourcesInfo configsResourcesInfo,
|
||||
[NotNull]IConfigProfilesResourcesInfo configProfilesResourcesInfo)
|
||||
{
|
||||
_operationsUsageLock.EnterReadLock();
|
||||
LoadingOperationsRunning = true;
|
||||
try
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException($"This package service instance is disposed!");
|
||||
}
|
||||
|
||||
SanitationChecksCore(configsResourcesInfo, "config", nameof(LoadConfig));
|
||||
SanitationChecksCore(configProfilesResourcesInfo, "config profiles", nameof(LoadConfig));
|
||||
SanitationChecksEnumerable(configsResourcesInfo.Configs, "config", nameof(LoadConfig));
|
||||
SanitationChecksEnumerable(configProfilesResourcesInfo.ConfigProfiles, "config profiles", nameof(LoadConfig));
|
||||
|
||||
#if DEBUG
|
||||
configsResourcesInfo.Configs.ForEach(ri =>
|
||||
{
|
||||
if (!this.Configs.Contains(ri))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Package Service: tried to load the configs resource for package {this.Package.Name} but it is not in the list for this package.");
|
||||
}
|
||||
});
|
||||
|
||||
configProfilesResourcesInfo.ConfigProfiles.ForEach(ri =>
|
||||
{
|
||||
if (!this.ConfigProfiles.Contains(ri))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Package Service: tried to load the localization resource for package {this.Package.Name} but it is not in the list for this package.");
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
if (!_configService.Value.TryAddConfigs(configsResourcesInfo.Configs))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Package Service: unable to add configs for package {this.Package.Name}! Aborting!");
|
||||
}
|
||||
|
||||
if (!_configService.Value.TryAddConfigsProfiles(configProfilesResourcesInfo.ConfigProfiles))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Package Service: unable to add configs profiles for package {this.Package.Name}! Aborting!");
|
||||
}
|
||||
ConfigsLoaded = true;
|
||||
}
|
||||
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 void Reset()
|
||||
{
|
||||
_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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_operationsUsageLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INTERNAL
|
||||
|
||||
private void SanitationChecksCore(object o, string resTypeInfoName, string callerName)
|
||||
{
|
||||
if (o is null)
|
||||
{
|
||||
_loggerService.LogError($"Package Service: {resTypeInfoName} resources list is null!");
|
||||
throw new NullReferenceException($"Package Service: {resTypeInfoName} resources list is null!");
|
||||
}
|
||||
|
||||
if (this.Package is null)
|
||||
{
|
||||
_loggerService.LogError($"Package Service: package not set at {callerName}()!");
|
||||
throw new NullReferenceException($"Package Service: package not set at {callerName}()!");
|
||||
}
|
||||
}
|
||||
|
||||
private void 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;
|
||||
|
||||
// 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)
|
||||
{
|
||||
throw new ArgumentException($"Package Service: {resTypeInfoName} info for resource does not have a package name set! Run by {this.Package.Name}.");
|
||||
}
|
||||
|
||||
if (resourceInfo.OwnerPackage != this.Package)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Package Service: {resTypeInfoName} info does not belong to this package! Owned by {resourceInfo.OwnerPackage.Name} but is run by {this.Package.Name}.");
|
||||
}
|
||||
|
||||
// Check if external dependencies are loaded and if current environment is supported, throw if not
|
||||
if (resourceInfo.Dependencies.IsDefaultOrEmpty)
|
||||
continue;
|
||||
|
||||
bool resourceMissing = false;
|
||||
|
||||
resourceInfo.Dependencies.ForEach(pdi =>
|
||||
{
|
||||
// for clarification: assemblies passed to the function should always be loaded.
|
||||
// optional assemblies should be filtered out before the list is sent.
|
||||
// left this as a reminder :)
|
||||
/*if (pdi.Optional)
|
||||
return;*/
|
||||
if (!_packageManagementService.CheckDependencyLoaded(pdi))
|
||||
{
|
||||
resourceMissing = true;
|
||||
_loggerService.LogError(
|
||||
$"Package Service: the following dependency for package {resourceInfo.OwnerPackage.Name} is not loaded: {pdi.DependencyPackage?.Name ?? (pdi.PackageName.IsNullOrWhiteSpace() ? pdi.SteamWorkshopId.ToString() : pdi.PackageName)}");
|
||||
}
|
||||
});
|
||||
|
||||
if (!resourceMissing)
|
||||
{
|
||||
throw new FileLoadException($"Package Service: dependencies for package {resourceInfo.OwnerPackage.Name} are not loaded.");
|
||||
}
|
||||
|
||||
// check runtime platform
|
||||
if (!_packageManagementService.CheckEnvironmentSupported(resourceInfo))
|
||||
{
|
||||
throw new PlatformNotSupportedException($"Package service: the {resTypeInfoName} from {resourceInfo.OwnerPackage.Name} is not supported on this platform.");
|
||||
}
|
||||
|
||||
// check local culture
|
||||
if (!_localizationService.Value.IsCurrentCultureSupported(resourceInfo))
|
||||
{
|
||||
throw new PlatformNotSupportedException($"Package service: the {resTypeInfoName} from {resourceInfo.OwnerPackage.Name} is not supported in this culture.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool GetThreadSafeBool(ref int var) => Interlocked.CompareExchange(ref var, 1, 1) == 1;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void SetThreadSafeBool(ref int var, bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
Interlocked.CompareExchange(ref var, 1, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Interlocked.CompareExchange(ref var, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services.Processing;
|
||||
|
||||
#region TypeDef
|
||||
|
||||
// ReSharper disable once TypeParameterCanBeVariant
|
||||
public interface IConverterService<TSrc, TOut> : IService
|
||||
{
|
||||
bool TryParseResource(TSrc src, out TOut resources);
|
||||
bool TryParseResources(IEnumerable<TSrc> sources, out List<TOut> resources);
|
||||
}
|
||||
|
||||
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> { }
|
||||
|
||||
#endregion
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Barotrauma.LuaCs.Services.Safe;
|
||||
|
||||
public interface ILuaConfigService : ILuaService
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using MoonSharp.Interpreter;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services.Safe;
|
||||
|
||||
/// <summary>
|
||||
/// Service for providing stateful functions and in-memory storage for lua functions
|
||||
/// </summary>
|
||||
public interface ILuaDataService : ILuaService
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns stored table for the given object if it exists.
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="tableName"></param>
|
||||
/// <returns>The table data or null if none exists.</returns>
|
||||
Table GetObjectTable(object obj, string tableName);
|
||||
|
||||
/// <summary>
|
||||
/// Returns stored table data under the given name if it exists.
|
||||
/// </summary>
|
||||
/// <param name="tableName"></param>
|
||||
/// <returns>The table data or null if none exists.</returns>
|
||||
Table GetTable(string tableName);
|
||||
|
||||
/// <summary>
|
||||
/// Returns stored table data for the given object or creates a new table if one doesn't exist.
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="tableName"></param>
|
||||
/// <returns></returns>
|
||||
Table GetOrCreateObjectTable(object obj, string tableName);
|
||||
|
||||
/// <summary>
|
||||
/// Returns stored table data or creates a new table if one doesn't exist.
|
||||
/// </summary>
|
||||
/// <param name="tableName"></param>
|
||||
/// <returns></returns>
|
||||
Table GetOrCreateTable(string tableName);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Barotrauma.LuaCs.Services.Safe;
|
||||
|
||||
public interface ILuaEventService : ILuaService
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Barotrauma.LuaCs.Services.Safe;
|
||||
|
||||
public interface ILuaNetworkingService : ILuaService
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Barotrauma.LuaCs.Services.Safe;
|
||||
|
||||
public interface ILuaPackageManagementService : ILuaService
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Barotrauma.LuaCs.Services.Safe;
|
||||
|
||||
public interface ILuaPackageService : ILuaService
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Barotrauma.LuaCs.Services.Safe;
|
||||
|
||||
public interface ILuaService
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using LightInject;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
|
||||
public class ServicesProvider : IServicesProvider
|
||||
{
|
||||
private ServiceContainer _serviceContainerInst;
|
||||
private ServiceContainer ServiceContainer
|
||||
{
|
||||
get
|
||||
{
|
||||
// ReSharper disable once ConvertIfStatementToNullCoalescingExpression
|
||||
if (_serviceContainerInst is null)
|
||||
_serviceContainerInst = new ServiceContainer();
|
||||
return _serviceContainerInst;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly ReaderWriterLockSlim _serviceLock = new();
|
||||
|
||||
public void RegisterServiceType<TSvcInterface, TService>(ServiceLifetime lifetime, ILifetime lifetimeInstance = null) where TSvcInterface : class, IService where TService : class, IService, TSvcInterface, new()
|
||||
{
|
||||
if (lifetimeInstance is null)
|
||||
{
|
||||
switch (lifetime)
|
||||
{
|
||||
case ServiceLifetime.Singleton:
|
||||
lifetimeInstance = new PerContainerLifetime();
|
||||
break;
|
||||
case ServiceLifetime.PerThread:
|
||||
lifetimeInstance = new PerThreadLifetime();
|
||||
break;
|
||||
// treat these as transient
|
||||
case ServiceLifetime.Transient:
|
||||
case ServiceLifetime.Invalid:
|
||||
case ServiceLifetime.Custom: // lifetime should not be null here
|
||||
default:
|
||||
lifetimeInstance = new PerRequestLifeTime();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_serviceLock.EnterReadLock();
|
||||
ServiceContainer.Register<TSvcInterface, TService>(lifetimeInstance);
|
||||
ServiceContainer.Compile<TService>();
|
||||
OnServiceRegistered?.Invoke(typeof(TSvcInterface), typeof(TService));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_serviceLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterServiceType<TSvcInterface, TService>(string name, ServiceLifetime lifetime,
|
||||
ILifetime lifetimeInstance = null) where TSvcInterface : class, IService where TService : class, IService, TSvcInterface, new()
|
||||
{
|
||||
if (name.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new ArgumentNullException($"Tried to register a service of type {typeof(TService).Name} but the name provided is null or empty." );
|
||||
}
|
||||
|
||||
if (lifetimeInstance is null)
|
||||
{
|
||||
switch (lifetime)
|
||||
{
|
||||
case ServiceLifetime.Singleton:
|
||||
lifetimeInstance = new PerContainerLifetime();
|
||||
break;
|
||||
case ServiceLifetime.PerThread:
|
||||
lifetimeInstance = new PerThreadLifetime();
|
||||
break;
|
||||
// treat these as transient
|
||||
case ServiceLifetime.Transient:
|
||||
case ServiceLifetime.Invalid:
|
||||
case ServiceLifetime.Custom: // lifetime should not be null here
|
||||
default:
|
||||
lifetimeInstance = new PerRequestLifeTime();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_serviceLock.EnterReadLock();
|
||||
ServiceContainer.Register<TSvcInterface, TService>(name, lifetimeInstance);
|
||||
ServiceContainer.Compile<TService>();
|
||||
OnServiceRegistered?.Invoke(typeof(TSvcInterface), typeof(TService));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_serviceLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void Compile()
|
||||
{
|
||||
try
|
||||
{
|
||||
_serviceLock.EnterReadLock();
|
||||
ServiceContainer?.Compile();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_serviceLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public event Action<Type, Type> OnServiceRegistered;
|
||||
|
||||
public void InjectServices<T>(T inst) where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
_serviceLock.EnterReadLock();
|
||||
ServiceContainer.InjectProperties(inst);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_serviceLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetService<TSvcInterface>(out IService service) where TSvcInterface : class, IService
|
||||
{
|
||||
try
|
||||
{
|
||||
_serviceLock.EnterReadLock();
|
||||
service = ServiceContainer.TryGetInstance<TSvcInterface>();
|
||||
return service is not null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
service = null;
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_serviceLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetService<TSvcInterface>(string name, out IService service) where TSvcInterface : class, IService
|
||||
{
|
||||
try
|
||||
{
|
||||
_serviceLock.EnterReadLock();
|
||||
service = ServiceContainer.TryGetInstance<TSvcInterface>(name);
|
||||
return service is not null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
service = null;
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_serviceLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public event Action<Type, IService> OnServiceInstanced;
|
||||
|
||||
public ImmutableArray<TSvc> GetAllServices<TSvc>() where TSvc : class, IService
|
||||
{
|
||||
try
|
||||
{
|
||||
_serviceLock.EnterReadLock();
|
||||
return ServiceContainer.GetAllInstances<TSvc>().ToImmutableArray();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_serviceLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.NoInlining)]
|
||||
public void DisposeAndReset()
|
||||
{
|
||||
// Plugins should never be allowed to execute this.
|
||||
if (Assembly.GetCallingAssembly() != Assembly.GetExecutingAssembly())
|
||||
{
|
||||
throw new MethodAccessException(
|
||||
$"Assembly {Assembly.GetCallingAssembly().FullName} attempted to call DisposeAllServices().");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_serviceLock.EnterWriteLock();
|
||||
_serviceContainerInst?.Dispose();
|
||||
_serviceContainerInst = new ServiceContainer();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_serviceLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PerThreadLifetime : ILifetime
|
||||
{
|
||||
private readonly ThreadLocal<object> _instance = new();
|
||||
|
||||
public object GetInstance(Func<object> createInstance, Scope scope)
|
||||
{
|
||||
if (_instance.Value is null)
|
||||
{
|
||||
var inst = createInstance.Invoke();
|
||||
// IDisposable dispatch
|
||||
if (inst is IDisposable disposable)
|
||||
{
|
||||
if (scope is null)
|
||||
{
|
||||
throw new InvalidOperationException("Attempt disposable object without a valid scope.");
|
||||
}
|
||||
scope.TrackInstance(disposable);
|
||||
}
|
||||
|
||||
_instance.Value = inst;
|
||||
}
|
||||
|
||||
return _instance.Value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user