feature: first
All checks were successful
Build / Compute Version (push) Successful in 4s
Build / Build Nuget package (CringeBootstrap.Abstractions) (push) Successful in 2m47s
Build / Build Nuget package (CringePlugins) (push) Successful in 5m31s
Build / Build Nuget package (NuGet) (push) Successful in 6m2s
Build / Build Nuget package (SharedCringe) (push) Successful in 7m25s
Build / Build Launcher (push) Successful in 9m11s

This commit is contained in:
zznty
2022-10-28 01:58:54 +07:00
commit aa979e9519
81 changed files with 6162 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
using System.Collections.Immutable;
using System.Reflection;
using Basic.Reference.Assemblies;
using CringePlugins.Loader;
using CringePlugins.Utils;
using dnlib.DotNet;
using ImGuiNET;
using Microsoft.CodeAnalysis;
using NLog;
using NuGet.Frameworks;
using NuGet.Models;
using NuGet.Versioning;
using Sandbox.Game;
using SpaceEngineers.Game;
using VRage.Utils;
namespace CringePlugins.Resolver;
public static class BuiltInPackages
{
private const string SeReferenceAssemblies = "SpaceEngineersDedicated.ReferenceAssemblies";
private const string ImGui = "ImGui.NET.DirectX";
public static ImmutableArray<ResolvedPackage> GetPackages(NuGetFramework runtimeFramework)
{
var nlog = FromAssembly<LogFactory>(runtimeFramework, version: new(5, 3, 4));
Version seVersion = new MyVersion(MyPerGameSettings.BasicGameInfo.GameVersion!.Value);
var se = FromAssembly<SpaceEngineersGame>(runtimeFramework, [
nlog.AsDependency()
], SeReferenceAssemblies, new(seVersion));
var imGui = FromAssembly<ImGuiKey>(runtimeFramework, id: ImGui);
BuiltInSdkPackage MapSdkPackage(
(string FileName, byte[] ImageBytes, PortableExecutableReference Reference, Guid Mvid) r)
{
var def = ModuleDefMD.Load(r.ImageBytes, IntrospectionContext.Global.Context);
var attribute = def.CustomAttributes.Find(typeof(AssemblyFileVersionAttribute).FullName);
var version = attribute is null ? new(99, 0, 0) : NuGetVersion.Parse((string)attribute.ConstructorArguments[0].Value);
return new BuiltInSdkPackage(
new(0, Path.GetFileNameWithoutExtension(r.FileName), version, []), runtimeFramework,
new(Path.GetFileNameWithoutExtension(r.FileName), version, [new(runtimeFramework, [])], null, []));
}
return
[
..Net80.ReferenceInfos.AllValues.Select(MapSdkPackage),
..Net80Windows.ReferenceInfos.AllValues.Select(MapSdkPackage),
nlog,
se,
imGui,
FromAssembly<PluginsLifetime>(runtimeFramework, [se.AsDependency(), imGui.AsDependency()]),
];
}
private static Dependency AsDependency(this ResolvedPackage package) => new(package.Package.Id, new(package.Package.Version));
private static BuiltInPackage FromAssembly<T>(NuGetFramework runtimeFramework, ImmutableArray<Dependency>? dependencies = null, string? id = null, NuGetVersion? version = null)
{
var assembly = typeof(T).Assembly.GetName();
id ??= assembly.Name!;
version ??= new NuGetVersion(assembly.Version ?? new(0, 0, 0));
dependencies ??= [];
return new(
new(0, id, version, [..dependencies.Value.Select(b => b.Id)]),
runtimeFramework,
new(id, version, [
new(runtimeFramework, dependencies.Value)
], null, [])
);
}
}
public record BuiltInPackage(Package Package, NuGetFramework ResolvedFramework, CatalogEntry Entry) : ResolvedPackage(Package, ResolvedFramework, Entry);
public record BuiltInSdkPackage(Package Package, NuGetFramework ResolvedFramework, CatalogEntry Entry) : BuiltInPackage(Package, ResolvedFramework, Entry);

View File

@@ -0,0 +1,144 @@
using System.Collections.Immutable;
using System.IO.Compression;
using NuGet;
using NuGet.Frameworks;
using NuGet.Models;
using NuGet.Versioning;
namespace CringePlugins.Resolver;
public class PackageResolver(NuGetFramework runtimeFramework, ImmutableArray<PackageReference> references, PackageSourceMapping packageSources)
{
public async Task<ImmutableHashSet<ResolvedPackage>> ResolveAsync()
{
var order = 0;
var packages = new SortedDictionary<Package, CatalogEntry>();
foreach (var reference in references)
{
var client = await packageSources.GetClientAsync(reference.Id);
var registrationRoot = await client.GetPackageRegistrationRootAsync(reference.Id);
var items = registrationRoot.Items.SelectMany(page =>
page.Items.Where(b => b.CatalogEntry.PackageTypes is ["CringePlugin"]))
.ToImmutableDictionary(b => b.CatalogEntry.Version);
var version = reference.Range.FindBestMatch(items.Values.Select(b => b.CatalogEntry.Version));
if (version is null)
throw new Exception($"Unable to find version for package {reference.Id}");
var package = new Package(order, reference.Id, version, []); // todo resolve dependencies
if (packages.TryAdd(package, items[version].CatalogEntry))
continue;
if (!packages.TryGetValue(package, out _))
throw new Exception($"Duplicate package {package.Id}");
var existingPackage = packages.Keys.First(b => b.Version == package.Version && b.Id == package.Id);
if (package.Version < existingPackage.Version)
throw new Exception($"Package reference {package.Id} has lower version {package.Version} than already resolved {existingPackage.Version}");
if (package.Version == existingPackage.Version)
continue;
packages.Remove(existingPackage);
packages.Add(package with
{
Order = ++order
}, items[version].CatalogEntry);
}
var set = ImmutableHashSet<ResolvedPackage>.Empty.ToBuilder();
foreach (var (package, catalogEntry) in packages)
{
var client = await packageSources.GetClientAsync(package.Id);
var nearestGroup = NuGetFrameworkUtility.GetNearest(catalogEntry.DependencyGroups, runtimeFramework,
g => g.TargetFramework);
if (nearestGroup is null)
throw new Exception($"Unable to find compatible dependency group for package {package.Id}");
set.Add(new RemotePackage(package, nearestGroup.TargetFramework, client, catalogEntry));
}
return set.ToImmutable();
}
public async Task<ImmutableHashSet<CachedPackage>> DownloadPackagesAsync(DirectoryInfo baseDirectory,
IReadOnlySet<ResolvedPackage> resolvedPackages, IProgress<float>? progress = null)
{
var packages = ImmutableHashSet<CachedPackage>.Empty.ToBuilder();
var i = 0f;
foreach (var package in resolvedPackages)
{
switch (package)
{
case RemotePackage remotePackage:
{
var dir = new DirectoryInfo(Path.Join(baseDirectory.FullName, package.Package.Id, package.Package.Version.ToString()));
if (!dir.Exists)
{
dir.Create();
await using var stream = await remotePackage.Client.GetPackageContentStreamAsync(remotePackage.Package.Id, remotePackage.Package.Version);
using var memStream = new MemoryStream();
await stream.CopyToAsync(memStream);
memStream.Position = 0;
using var archive = new ZipArchive(memStream, ZipArchiveMode.Read);
archive.ExtractToDirectory(dir.FullName);
}
packages.Add(new CachedPackage(package.Package, package.ResolvedFramework, dir, package.Entry));
break;
}
case CachedPackage cachedPackage:
packages.Add(cachedPackage);
break;
}
progress?.Report(i++ / resolvedPackages.Count);
}
return packages.ToImmutable();
}
}
public record CachedPackage(Package Package, NuGetFramework ResolvedFramework, DirectoryInfo Directory, CatalogEntry Entry) : ResolvedPackage(Package, ResolvedFramework, Entry);
public record RemotePackage(Package Package, NuGetFramework ResolvedFramework, NuGetClient Client, CatalogEntry Entry) : ResolvedPackage(Package, ResolvedFramework, Entry);
public abstract record ResolvedPackage(Package Package, NuGetFramework ResolvedFramework, CatalogEntry Entry);
public record Package(int Order, string Id, NuGetVersion Version, ImmutableArray<string> Dependencies) : IComparable<Package>, IComparable
{
public int CompareTo(Package? other)
{
if (ReferenceEquals(this, other)) return 0;
if (other is null) return 1;
var orderComparison = Order.CompareTo(other.Order);
if (orderComparison != 0) return orderComparison;
return string.Compare(Id, other.Id, StringComparison.OrdinalIgnoreCase);
}
public int CompareTo(object? obj)
{
if (obj is null) return 1;
if (ReferenceEquals(this, obj)) return 0;
return obj is Package other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(Package)}");
}
public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Id);
public virtual bool Equals(Package? other)
{
if (other is null) return false;
return Id.Equals(other.Id, StringComparison.OrdinalIgnoreCase);
}
}
public record PackageReference(string Id, VersionRange Range);