- Added publicized assemblies to LuaCsForBarotrauma package via ModConfig.xml

- Added XmlAttribute tags for ModConfig.xml defined properties.
- GetType and GetImplementingTypes<T> rework.
This commit is contained in:
MapleWheels
2026-02-09 21:32:57 -05:00
parent fb4648d759
commit 30149b504d
10 changed files with 114 additions and 48 deletions

View File

@@ -2,4 +2,7 @@
<ModConfig>
<Lua File="%ModDir%/Lua/LuaSetup.lua" IsAutorun="true" />
<Config File="%ModDir%/Config/SettingsShared.xml"/>
<Assembly File="%ModDir%/Publicized/BarotraumaCore.dll" IsReferenceModeOnly="true"/>
<Assembly File="%ModDir%/Publicized/Barotrauma.dll" Target="Client" IsReferenceModeOnly="true"/>
<Assembly File="%ModDir%/Publicized/DedicatedServer.dll" Target="Server" IsReferenceModeOnly="true"/>
</ModConfig>

View File

@@ -6,6 +6,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml.Linq;
using System.Xml.Serialization;
using Barotrauma.LuaCs;
using Barotrauma.Steam;
using OneOf;
@@ -44,6 +45,7 @@ public record AssemblyResourceInfo : BaseResourceInfo, IAssemblyResourceInfo
public string FriendlyName { get; init; }
public bool IsScript { get; init; }
public bool UseInternalAccessName { get; init; }
public bool IsReferenceModeOnly { get; init; }
}
/// <summary>
@@ -81,9 +83,12 @@ public record ConfigInfo : IConfigInfo
public record ConfigProfileInfo : IConfigProfileInfo
{
/// <summary>
/// Profile name.
/// </summary>
public string InternalName { get; init; }
public ContentPackage OwnerPackage { get; init; }
public IReadOnlyList<(string ConfigName, XElement Element)> ProfileValues { get; init; }
public IReadOnlyList<(string SettingName, XElement Element)> ProfileValues { get; init; }
}
#endregion

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Xml.Serialization;
namespace Barotrauma.LuaCs.Data;
@@ -24,12 +25,14 @@ public interface IPlatformInfo
/// Platforms that these localization files should be loaded for.
/// </summary>
[Required]
[XmlAttribute("Platform")]
Platform SupportedPlatforms { get; }
/// <summary>
/// Targets that these localization files should be loaded for.
/// </summary>
[Required]
[XmlAttribute("Target")]
Target SupportedTargets { get; }
}
@@ -44,6 +47,7 @@ public interface IResourceInfo : IPlatformInfo
/// Specifies the loading order for all assets of the same type (ie. styles, assemblies, etc.) from
/// the same <see cref="ContentPackage"/>. Lower number is higher priority, see <see cref="System.Linq.Enumerable.OrderBy{TSource,TKey}(IEnumerable{TSource}, Func{TSource,TKey})"/>
/// </summary>
[XmlAttribute("LoadPriority")]
int LoadPriority { get; }
/// <summary>
@@ -56,5 +60,6 @@ public interface IResourceInfo : IPlatformInfo
/// 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>
[XmlAttribute("Optional")]
bool Optional { get; }
}

View File

@@ -5,5 +5,5 @@ namespace Barotrauma.LuaCs.Data;
public interface IConfigProfileInfo : IDataInfo
{
IReadOnlyList<(string ConfigName, XElement Element)> ProfileValues { get; }
IReadOnlyList<(string SettingName, XElement Element)> ProfileValues { get; }
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
namespace Barotrauma.LuaCs.Data;
@@ -11,6 +12,7 @@ public interface IDataInfo : IEqualityComparer<IDataInfo>, IEquatable<IDataInfo>
/// <summary>
/// Internal name unique within the resources inside a package.
/// </summary>
[XmlAttribute("Name")]
string InternalName { get; }
/// <summary>
/// The package this information belongs to.

View File

@@ -2,6 +2,7 @@
using System.Collections.Immutable;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Xml.Serialization;
namespace Barotrauma.LuaCs.Data;
@@ -18,6 +19,7 @@ public interface ILuaScriptResourceInfo : IBaseResourceInfo
/// <summary>
/// Should this script be run automatically.
/// </summary>
[XmlAttribute("IsAutorun")]
public bool IsAutorun { get; }
}
@@ -27,17 +29,27 @@ public interface IAssemblyResourceInfo : IBaseResourceInfo
/// 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>
[XmlAttribute("FriendlyName")]
public string FriendlyName { get; }
/// <summary>
/// Is this entry referring to a script file collection.
/// </summary>
[XmlAttribute("IsScript")]
public bool IsScript { get; }
/// <summary>
/// <b>[Required(IsScript: true)] Whether the internal compiled assembly name should be named to enabled use of the
/// <see cref="InternalsVisibleToAttribute"/> attribute.</b>
/// </summary>
[XmlAttribute("UseInternalAccessName")]
public bool UseInternalAccessName { get; }
/// <summary>
/// Should the following resources only be used for Compilation MetadataReference.
/// NOTE: Affects the entire package's assembly resources, meant for internal use only.
/// </summary>
[XmlAttribute("IsReferenceModeOnly")]
public bool IsReferenceModeOnly { get; }
}

View File

@@ -208,6 +208,10 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
AreOperationRunning = true;
foreach (var data in _loadedAssemblyData.Values)
{
if (data.AssemblyReference is null)
{
continue;
}
yield return data.AssemblyReference;
}
AreOperationRunning = false;
@@ -356,10 +360,10 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
if (additionalDependencyPaths.Any())
{
var r = AddDependencyPaths(additionalDependencyPaths);
if (!r.IsFailed)
if (r.IsFailed)
{
// we have errors, loading may not work.
return FluentResults.Result.Fail(new Error($"Failed to load dependency paths.")
return FluentResults.Result.Fail(new Error($"Failed to load dependency paths for '{assemblyFilePath}' with paths: {additionalDependencyPaths.Aggregate((s, ac) => $"{ac}| P={s}")}.")
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, assemblyFilePath))
.WithErrors(r.Errors);
@@ -379,7 +383,7 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
try
{
var assembly = LoadFromAssemblyPath(sanitizedFilePath);
_loadedAssemblyData[assembly] = new AssemblyData(assembly, sanitizedFilePath);
_loadedAssemblyData[assembly] = new AssemblyData(assembly, assembly.Location);
return new Result<Assembly>().WithSuccess($"Loaded assembly '{assembly.GetName()}'").WithValue(assembly);
}
catch (FileNotFoundException fnfe)

View File

@@ -79,7 +79,8 @@ public sealed class ModConfigFileParserService :
// Type Specific
FriendlyName = src.Element.GetAttributeString("FriendlyName", string.Empty),
IsScript = src.Element.GetAttributeBool("IsScript", false),
UseInternalAccessName = src.Element.GetAttributeBool("UseInternalAccessName", false)
UseInternalAccessName = src.Element.GetAttributeBool("UseInternalAccessName", false),
IsReferenceModeOnly = src.Element.GetAttributeBool("IsReferenceModeOnly", false)
};
}
@@ -211,8 +212,8 @@ public sealed class ModConfigFileParserService :
private (Platform Platform, Target Target) GetRuntimeEnvironment(XElement element)
{
return (
Platform: element.GetAttributeEnum("Platform", Platform.Windows | Platform.Linux | Platform.OSX),
Target: element.GetAttributeEnum("Target", Target.Client | Target.Server));
Platform: element.GetAttributeEnum("Platform", Platform.Any),
Target: element.GetAttributeEnum("Target", Target.Any));
}
private async Task<ImmutableArray<Result<T>>> TryParseGenericResourcesAsync<T>(IEnumerable<ResourceParserInfo> sources)

View File

@@ -264,7 +264,8 @@ public sealed class ModConfigService : IModConfigService
FriendlyName = $"{src.Name}.{searchPathways.SubFolder.Replace('/','.')}",
IncompatiblePackages = ImmutableArray<Identifier>.Empty,
RequiredPackages = ImmutableArray<Identifier>.Empty,
IsScript = false
IsScript = false,
IsReferenceModeOnly = false
});
}
}
@@ -304,7 +305,8 @@ public sealed class ModConfigService : IModConfigService
IncompatiblePackages = ImmutableArray<Identifier>.Empty,
RequiredPackages = ImmutableArray<Identifier>.Empty,
UseInternalAccessName = true,
IsScript = true
IsScript = true,
IsReferenceModeOnly = false
});
}
}

View File

@@ -86,6 +86,29 @@ public class PluginManagementService : IAssemblyManagementService
.ToString(),
ScriptParseOptions);
private ImmutableArray<MetadataReference> _baseMetadataReferences = ImmutableArray<MetadataReference>.Empty;
private IEnumerable<MetadataReference> BaseMetadataReferences
{
get
{
if (_baseMetadataReferences.IsDefaultOrEmpty)
{
_baseMetadataReferences = Basic.Reference.Assemblies.Net80.References.All
.Union(AssemblyLoadContext.Default.Assemblies
.Where(ass =>
!ass.IsDynamic &&
!ass.GetName().FullName.EndsWith("Barotrauma.Core") &&
!ass.GetName().FullName.EndsWith("Barotrauma") &&
!ass.GetName().FullName.EndsWith("DedicatedServer"))
.Select(MetadataReference (ass) => MetadataReference.CreateFromFile(ass.Location)))
.Where(ar => ar is not null)
.ToImmutableArray();
}
return _baseMetadataReferences;
}
}
#endregion
#region Disposal
@@ -213,14 +236,35 @@ public class PluginManagementService : IAssemblyManagementService
public Result<ImmutableArray<Type>> GetImplementingTypes<T>(bool includeInterfaces = false, bool includeAbstractTypes = false,
bool includeDefaultContext = true)
{
#if !DEBUG
throw new NotImplementedException();
#endif
if (includeInterfaces)
{
includeAbstractTypes = true;
}
using var lck = _operationsLock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
var builder = ImmutableArray.CreateBuilder<Type>();
foreach (var ass in AppDomain.CurrentDomain.GetAssemblies())
if (includeDefaultContext)
{
foreach (var type in ass.GetSafeTypes())
foreach (var ass in AssemblyLoadContext.Default.Assemblies)
{
AddTypesFromAssembly(ass);
}
}
foreach (var ass in _assemblyLoaders.Values.Where(al => !al.IsReferenceOnlyMode).SelectMany(al => al.Assemblies))
{
AddTypesFromAssembly(ass);
}
return builder.ToImmutable();
void AddTypesFromAssembly(Assembly assembly)
{
foreach (var type in assembly.GetSafeTypes())
{
if ((includeInterfaces || !type.IsInterface)
&& (includeAbstractTypes || !type.IsAbstract)
@@ -230,8 +274,6 @@ public class PluginManagementService : IAssemblyManagementService
}
}
}
return builder.ToImmutable();
}
public Type GetType(string typeName, bool isByRefType = false, bool includeInterfaces = false,
@@ -255,9 +297,21 @@ public class PluginManagementService : IAssemblyManagementService
return type;
}
foreach (var ass in AssemblyLoadContext.Default.Assemblies)
{
if (ass.GetType(typeName, false, false) is not {} type2 || (!includeInterfaces && type2.IsInterface))
{
continue;
}
return isByRefType ? type2.MakeByRefType() : type2;
}
}
foreach (var ass in AssemblyLoadContext.All.SelectMany(alc => alc.Assemblies))
foreach (var ass in AssemblyLoadContext.All
.Where(alc => alc != AssemblyLoadContext.Default)
.SelectMany(alc => alc.Assemblies))
{
if (ass.GetType(typeName, false, false) is not {} type || (!includeInterfaces && type.IsInterface))
{
@@ -426,7 +480,7 @@ public class PluginManagementService : IAssemblyManagementService
new IAssemblyLoaderService.LoaderInitData(
InstanceId: Guid.NewGuid(),
contentPackRes.Key.Name,
IsReferenceMode: false,
IsReferenceMode: contentPackRes.Any(r => r.IsReferenceModeOnly),
OwnerPackage: contentPackRes.Key,
OnUnload: OnAssemblyLoaderUnloading,
OnResolvingManaged: OnAssemblyLoaderResolvingManaged,
@@ -465,7 +519,7 @@ public class PluginManagementService : IAssemblyManagementService
new IAssemblyLoaderService.LoaderInitData(
InstanceId: Guid.NewGuid(),
contentPackRes.Key.Name,
IsReferenceMode: false,
IsReferenceMode: contentPackRes.Any(r => r.IsReferenceModeOnly),
OwnerPackage: contentPackRes.Key,
OnUnload: OnAssemblyLoaderUnloading,
OnResolvingManaged: OnAssemblyLoaderResolvingManaged,
@@ -550,35 +604,13 @@ public class PluginManagementService : IAssemblyManagementService
IEnumerable<MetadataReference> GetMetadataReferences()
{
#if !DEBUG
throw new NotImplementedException($"Needs to use publicized barotrauma assemblies and cache metadata.");
#endif
var publicizedDir = Path.Combine(Directory.GetCurrentDirectory(), "Publicized");
string[] publicizedAssemblies =
var builder = ImmutableArray.CreateBuilder<MetadataReference>();
builder.AddRange(BaseMetadataReferences);
foreach (var loaderService in _assemblyLoaders)
{
#if CLIENT
"Barotrauma",
#elif SERVER
"DedicatedServer",
#endif
"BarotraumaCore"
};
var publicizedRefs = publicizedAssemblies
.Select(name => Path.Combine(publicizedDir, $"{name}.dll"))
.Where(File.Exists)
.Select(path => MetadataReference.CreateFromFile(path));
var runtimeRefs = AppDomain.CurrentDomain.GetAssemblies()
.Where(ass =>
!string.IsNullOrWhiteSpace(ass.Location) &&
!publicizedAssemblies.Contains(ass.GetName().Name))
.Select(ass => MetadataReference.CreateFromFile(ass.Location));
return Basic.Reference.Assemblies.Net80.References.All
.Union(runtimeRefs)
.Union(publicizedRefs);
builder.AddRange(loaderService.Value.AssemblyReferences.Where(ar => ar is not null));
}
return builder.ToImmutable();
}
}