This commit is contained in:
zznty
2025-05-11 02:37:57 +07:00
commit 947fcde9ca
14 changed files with 754 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/

13
.idea/.idea.Plugin.ClientModLoader/.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/projectSettingsUpdater.xml
/modules.xml
/contentModel.xml
/.idea.Plugin.ClientModLoader.iml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GitToolBoxBlameSettings">
<option name="version" value="2" />
</component>
</project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -0,0 +1,13 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AImGui_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fea9f3616f41243ab9c7f97ef274feb9c4d400_003F9d_003Fc14ad916_003FImGui_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMemoryExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F76803a04e31b4fee99d90bcbfc5a6bdde8e930_003Fdd_003Fd6f3a4b9_003FMemoryExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMyDefinitionManager_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F34f70babce284fbd8bd6b4a545e86609a17e00_003F15_003Fdda8a4a9_003FMyDefinitionManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMyObjectBuilder_005FCheckpoint_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5fc89e31eedc4d00a398eb23fa21b4c13ffe00_003Fc4_003F46a79233_003FMyObjectBuilder_005FCheckpoint_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMyWorkshop_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F34f70babce284fbd8bd6b4a545e86609a17e00_003F27_003F40d62d28_003FMyWorkshop_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARange_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F36b5151d8b20b97ce4d68ff12cc15eb23cd5e58474e6282da7a2fa67363512_003FRange_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASteamUGCQueryCompleted_005Ft_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F0057baf19dee4a4b8d37fd0c46f1b3325ba00_003Fb4_003F9a9c7cd1_003FSteamUGCQueryCompleted_005Ft_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMyLanguagesEnum_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F1eb8878a2e6343dc94c75733597801dea0400_003Fdf_003Fd3272324_003FMyLanguagesEnum_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACallResult_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F0057baf19dee4a4b8d37fd0c46f1b3325ba00_003F90_003Faeefa61e_003FCallResult_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASteamUGCDetails_005Ft_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F0057baf19dee4a4b8d37fd0c46f1b3325ba00_003F2a_003F258d424e_003FSteamUGCDetails_005Ft_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMyTexts_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F1eb8878a2e6343dc94c75733597801dea0400_003Fa1_003F7f551730_003FMyTexts_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fc7102cd0ffb8973777e61b1942c3fffac7e14016a511d055c3adf73ff91748_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>

View File

@@ -0,0 +1,3 @@
<Solution>
<Project Path="Plugin.ClientModLoader/Plugin.ClientModLoader.csproj" />
</Solution>

View File

@@ -0,0 +1,87 @@
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using HarmonyLib;
using Sandbox.Definitions;
using Sandbox.Engine.Networking;
using Sandbox.Game.World;
using VRage.Game;
namespace Plugin.ClientModLoader;
[HarmonyPatch]
internal static class ModInjector
{
public static HashSet<ulong> Mods = [];
private static readonly List<MyObjectBuilder_Checkpoint.ModItem> AdditionalFilledModItems = [];
[HarmonyPatch(typeof(MyWorkshop), nameof(MyWorkshop.DownloadWorldModsBlockingInternal))]
[HarmonyPrefix]
private static void DownloadModsBlockingPrefix(ref List<MyObjectBuilder_Checkpoint.ModItem> mods, ref List<MyObjectBuilder_Checkpoint.ModItem> __state)
{
AdditionalFilledModItems.Clear();
__state = mods;
AppendToList(ref mods);
}
[HarmonyPatch(typeof(MyWorkshop), nameof(MyWorkshop.DownloadWorldModsBlockingInternal))]
[HarmonyPostfix]
private static void DownloadModsBlockingPostfix(List<MyObjectBuilder_Checkpoint.ModItem> mods, List<MyObjectBuilder_Checkpoint.ModItem> __state)
{
foreach (var mod in mods)
{
var index = __state.FindIndex(b =>
b.PublishedFileId == mod.PublishedFileId && b.PublishedServiceName == mod.PublishedServiceName);
if (index != -1)
{
var stateMod = __state[index];
stateMod.SetModData(mod.GetModData());
__state[index] = stateMod;
}
else if (Mods.Contains(mod.PublishedFileId))
AdditionalFilledModItems.Add(mod);
else
__state.Add(mod);
}
}
[HarmonyPatch(typeof(MyDefinitionManager), nameof(MyDefinitionManager.LoadData))]
[HarmonyPrefix]
private static void LoadDefinitionsPrefix(ref List<MyObjectBuilder_Checkpoint.ModItem> mods) => AppendToList(ref mods);
[HarmonyPatch(typeof(MyScriptManager), nameof(MyScriptManager.LoadData))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> LoadScriptsTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var staticGetter = AccessTools.PropertyGetter(typeof(MySession), nameof(MySession.Static));
var modsGetter = AccessTools.PropertyGetter(typeof(MySession), nameof(MySession.Mods));
return new CodeMatcher(instructions, generator)
.Start()
.DeclareLocal(typeof(List<MyObjectBuilder_Checkpoint.ModItem>), out var modsLocal)
.CreateLabel(out var start)
.InsertAndAdvance(
new(OpCodes.Call, staticGetter),
new(OpCodes.Call, modsGetter),
new(OpCodes.Stloc, modsLocal),
new(OpCodes.Ldloc, modsLocal),
new(OpCodes.Brfalse, start),
new(OpCodes.Ldloca, modsLocal),
CodeInstruction.Call(typeof(ModInjector), nameof(AppendToList))
)
.MatchStartForward(CodeMatch.Calls(staticGetter), CodeMatch.Calls(modsGetter))
.Repeat(a => a
.SetAndAdvance(OpCodes.Ldloc, modsLocal)
.SetAndAdvance(OpCodes.Nop, null))
.Instructions();
}
private static void AppendToList(ref List<MyObjectBuilder_Checkpoint.ModItem> mods)
{
// copy
mods = mods.ToList();
mods.AddRange(AdditionalFilledModItems.Count > 0
? AdditionalFilledModItems
: Mods.Select(mod => new MyObjectBuilder_Checkpoint.ModItem(mod, "Steam")));
}
}

View File

@@ -0,0 +1,321 @@
using System.Collections.Immutable;
using System.Net.Http.Json;
using System.Runtime.InteropServices;
using System.Text.Json;
using CringePlugins.Abstractions;
using ImGuiNET;
using Plugin.ClientModLoader.Utils;
using Steamworks;
using VRage;
using static ImGuiNET.ImGui;
namespace Plugin.ClientModLoader;
public class ModListComponent : IRenderComponent
{
private readonly string _configPath;
private readonly JsonSerializerOptions _options = new(JsonSerializerDefaults.Web);
private bool _visible;
private string _searchQuery = string.Empty;
private Task? _searchTask;
private PublishedFileId_t[] _mods = [];
private ImmutableDictionary<ulong, WhitelistItem> _whitelist = ImmutableDictionary<ulong, WhitelistItem>.Empty;
private List<ModItem> _modList = [];
private ModItem? _selected;
public HashSet<ulong> Installed { get; }
public bool Visible
{
get => _visible;
set => _visible = value;
}
public ModListComponent(string configPath)
{
_configPath = configPath;
if (File.Exists(configPath))
{
using var stream = File.OpenRead(configPath);
Installed = JsonSerializer.Deserialize<HashSet<ulong>>(stream, _options)!;
}
else Installed = [];
_searchTask = DownloadWhitelist();
}
private async Task DownloadWhitelist()
{
using var client = new HttpClient();
var response =
await client.GetFromJsonAsync<Whitelist>(
"https://github.com/sepluginloader/sepluginloader.github.io/raw/refs/heads/main/plugins.json");
if (response is null) return;
var mods = new List<PublishedFileId_t>();
var builder = ImmutableDictionary.CreateBuilder<ulong, WhitelistItem>();
foreach (var item in response.Mods)
{
if (!ulong.TryParse(item.Id, out var id)) continue;
mods.Add(new PublishedFileId_t(id));
builder.Add(id, item);
}
_mods = mods.ToArray();
_whitelist = builder.ToImmutable();
await RefreshAsync();
}
public void OnFrame()
{
if (!Visible) return;
SetNextWindowSize(new(700, 500), ImGuiCond.FirstUseEver);
if (!Begin("Mod List", ref _visible)) return;
try
{
InputText("##searchbox", ref _searchQuery, 256);
SameLine();
if (Button("Search"))
{
_searchTask = RefreshAsync();
return;
}
Spacing();
switch (_searchTask)
{
case { IsCompleted: false }:
TextDisabled("Loading...");
return;
case { IsCompletedSuccessfully: false }:
{
TextDisabled("Failed to load plugins list");
if (_searchTask.Exception is null) return;
foreach (var exception in _searchTask.Exception.InnerExceptions)
{
TextWrapped($"{exception.GetType()}: {exception.Message}");
}
return;
}
}
if (_modList.Count == 0)
{
TextDisabled("Nothing found");
return;
}
BeginChild("List", new(400, 0), ImGuiChildFlags.Border | ImGuiChildFlags.ResizeX);
{
if (BeginTable("AvailableModsTable", 3,
ImGuiTableFlags.ScrollY | ImGuiTableFlags.Resizable | ImGuiTableFlags.SizingStretchProp))
{
TableSetupColumn("Name", ImGuiTableColumnFlags.None, .75f);
TableSetupColumn("Installed", ImGuiTableColumnFlags.None, .25f);
TableHeadersRow();
foreach (var mod in _modList)
{
TableNextRow();
TableNextColumn();
var selected = _selected == mod;
if (Selectable(mod.Name, ref selected, ImGuiSelectableFlags.SpanAllColumns))
{
_selected = selected ? mod : null;
}
if (!string.IsNullOrEmpty(mod.Summary) && IsItemHovered(ImGuiHoveredFlags.ForTooltip))
{
SetTooltip(mod.Summary);
}
TableNextColumn();
var installed = Installed.Contains(mod.Id);
TextColored(installed ? new(0f, 1f, 0f, 1f) : new(1f, 0f, 0f, 1f),
installed ? "Installed" : "Not Installed");
}
EndTable();
}
EndChild();
}
SameLine();
BeginGroup();
BeginChild("Mod View", new(0, -GetFrameHeightWithSpacing())); // Leave room for 1 line below us
if (_selected is not null)
{
Text(_selected.Name);
Separator();
if (BeginTabBar("##ModViewTabs"))
{
if (!string.IsNullOrEmpty(_selected.Description) && BeginTabItem("Description"))
{
TextWrapped(_selected.Description);
EndTabItem();
}
if (BeginTabItem("Mod Description"))
{
TextWrapped(string.IsNullOrEmpty(_selected.ModDescription) ? "Nothing." : _selected.ModDescription);
EndTabItem();
}
if (BeginTabItem("Details"))
{
TextLinkOpenURL("Mod URL", _selected.Url);
Text("Author:");
SameLine();
TextWrapped(_selected.Author);
Text("Created:");
SameLine();
TextWrapped(DateTimeOffset.FromUnixTimeSeconds(_selected.Created).ToString("g"));
Text("Updated:");
SameLine();
TextWrapped(DateTimeOffset.FromUnixTimeSeconds(_selected.Updated).ToString("g"));
EndTabItem();
}
EndTabBar();
}
}
EndChild();
if (_selected is not null)
{
var installed = Installed.Contains(_selected.Id);
if (Button(installed ? "Uninstall" : "Install"))
{
if (installed)
Installed.Remove(_selected.Id);
else
Installed.Add(_selected.Id);
Save();
}
}
EndGroup();
}
finally
{
End();
}
}
private void Save()
{
using var stream = File.Create(_configPath);
JsonSerializer.Serialize(stream, Installed, _options);
}
private async Task RefreshAsync()
{
var handle = SteamUGC.CreateQueryUGCDetailsRequest(_mods, (uint)_mods.Length);
if (handle.m_UGCQueryHandle == ulong.MaxValue)
throw new Exception("Failed to create UGC request");
try
{
SteamUGC.SetSearchText(handle, _searchQuery);
SteamUGC.SetReturnLongDescription(handle, true);
// TODO font support for other languages
// SteamUGC.SetLanguage(handle, MapLanguage(MySandboxGame.Config.Language));
var call = SteamUGC.SendQueryUGCRequest(handle);
var result = await CallResultAsync<SteamUGCQueryCompleted_t>.Create(call);
var list = new List<ModItem>((int)result.m_unNumResultsReturned);
for (uint i = 0; i < result.m_unNumResultsReturned; i++)
{
if (!SteamUGC.GetQueryUGCResult(handle, i, out var details))
break;
if (details.m_eResult != EResult.k_EResultOK)
continue;
var title = details.m_rgchTitle;
if (!string.IsNullOrEmpty(_searchQuery) && !title.Contains(_searchQuery, StringComparison.OrdinalIgnoreCase))
continue;
var item = _whitelist[details.m_nPublishedFileId.m_PublishedFileId];
var description = details.m_rgchDescription;
list.Add(new(details.m_nPublishedFileId.m_PublishedFileId, title,
string.IsNullOrEmpty(item.Tooltip)
? description[..Math.Min(description.Length - 1, 255)]
: item.Tooltip,
item.Description,
description, $"https://steamcommunity.com/sharedfiles/filedetails/?id={details.m_nPublishedFileId.m_PublishedFileId}",
details.m_rtimeCreated, details.m_rtimeUpdated, item.Author));
}
_modList = list;
}
finally
{
SteamUGC.ReleaseQueryUGCRequest(handle);
}
}
private static string MapLanguage(MyLanguagesEnum lang) =>
lang switch
{
MyLanguagesEnum.Czech => "czech",
MyLanguagesEnum.German => "german",
MyLanguagesEnum.Russian => "russian",
MyLanguagesEnum.Spanish_Spain => "spanish",
MyLanguagesEnum.French => "french",
MyLanguagesEnum.Italian => "italian",
MyLanguagesEnum.Danish => "danish",
MyLanguagesEnum.Dutch => "dutch",
MyLanguagesEnum.Polish => "polish",
MyLanguagesEnum.Finnish => "finnish",
MyLanguagesEnum.Hungarian => "hungarian",
MyLanguagesEnum.Portuguese_Brazil => "brazilian",
MyLanguagesEnum.Norwegian => "norwegian",
MyLanguagesEnum.Spanish_HispanicAmerica => "latam",
MyLanguagesEnum.Swedish => "swedish",
MyLanguagesEnum.Romanian => "romanian",
MyLanguagesEnum.Ukrainian => "ukrainian",
MyLanguagesEnum.Turkish => "turkish",
MyLanguagesEnum.ChineseChina => "schinese",
MyLanguagesEnum.Japanese => "japanese",
_ => "english",
};
private record ModItem(ulong Id, string Name, string Summary, string? Description, string ModDescription, string Url, long Created, long Updated, string Author);
private record WhitelistItem(string Id, string? Tooltip, string Author, string? Description);
private record Whitelist(ImmutableArray<WhitelistItem> Mods);
}

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<UseWindowsForms>True</UseWindowsForms>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<RestoreAdditionalProjectSources>https://ng.zznty.ru/v3/index.json</RestoreAdditionalProjectSources>
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CringePlugins" Version="*" ExcludeAssets="runtime; native"/>
<PackageReference Include="Steamworks.NET" Version="20.1.0" ExcludeAssets="runtime; native" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,34 @@
using CringePlugins.Render;
using HarmonyLib;
using VRage.Plugins;
namespace Plugin.ClientModLoader;
public class Plugin : IPlugin
{
private ModListComponent? _component;
private readonly DirectoryInfo _dir =
new(Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"CringeLauncher"));
public void Dispose()
{
}
public void Init(object gameInstance)
{
new Harmony("Plugin.ClientModLoader").PatchAll(typeof(Plugin).Assembly);
RenderHandler.Current.RegisterComponent(_component = new ModListComponent(Path.Join(_dir.FullName, "mods.json")));
ModInjector.Mods = _component.Installed;
}
public void Update()
{
}
public void OpenConfigDialog()
{
if (_component != null) _component.Visible = true;
}
}

View File

@@ -0,0 +1,32 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Steamworks;
namespace Plugin.ClientModLoader.Utils;
public static class CallResultAsync<T> where T : struct
{
public static async Task<T> Create(SteamAPICall_t call)
{
var tcs = new TaskCompletionSource<T>();
using var callResult = CallResult<T>.Create((args, failed) =>
{
if (failed)
{
ref var result = ref Unsafe.As<T, GenericCallResult>(ref args);
tcs.TrySetException(new Exception($"CallResult failed: {result.m_eResult}"));
}
else tcs.TrySetResult(args);
});
callResult.Set(call);
return await tcs.Task;
}
[StructLayout(LayoutKind.Sequential, Pack = 8)]
private struct GenericCallResult
{
public UGCQueryHandle_t m_handle;
public EResult m_eResult;
}
}

View File

@@ -0,0 +1,201 @@
{
"version": 1,
"dependencies": {
"net9.0-windows7.0": {
"CringePlugins": {
"type": "Direct",
"requested": "[*, )",
"resolved": "0.1.51",
"contentHash": "6v6FfWlrLge3E67R75P/svFtvbAodzffwMyISUYFrZK6XTYsLPLExegnHswPNMC1PFbwD5ukhXYF9eUgIluubQ==",
"dependencies": {
"ImGui.NET.DirectX": "1.91.0.1",
"Lib.Harmony.Thin": "2.3.4-torch",
"NLog": "5.4.0",
"NuGet": "0.1.51",
"SharedCringe": "0.1.51",
"SpaceEngineersDedicated.ReferenceAssemblies": "1.206.30",
"dnlib": "4.4.0"
}
},
"Steamworks.NET": {
"type": "Direct",
"requested": "[20.1.0, )",
"resolved": "20.1.0",
"contentHash": "+GntwnyJ5tCNvUIaQxv2+ehDvZJzGUqlSB5xRBk1hTj1qqBJ6s4vK/OfGD/jae7aTmXiGSm8wpJORosNtQevJQ=="
},
"CringeBootstrap.Abstractions": {
"type": "Transitive",
"resolved": "0.1.51",
"contentHash": "dpA0Tr7Ir+6WzlVq98on0kxcEwpUr/arl0+8EX3QPsO2tXYTuRJRDMqy4SlMjN5t+xDfT/iILzkcUldIfogE8A=="
},
"dnlib": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "cKHI720q+zfEEvzklWVGt6B0TH3AibAyJbpUJl4U6KvTP13tycfnqJpkGHRZ/oQ45BTIoIxIwltHIJVDN+iCqQ=="
},
"ImGui.NET.DirectX": {
"type": "Transitive",
"resolved": "1.91.0.1",
"contentHash": "PpW1gQ9g97h6Hm/h/tkSBOmsBYgGwN8wKNmlJomcQFD/zRY1HPkJZz18XRSfRLHPmH2eeh4hhhZv1KHug7dF9g==",
"dependencies": {
"System.Buffers": "4.5.1",
"System.Numerics.Vectors": "4.5.0",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"Lib.Harmony.Thin": {
"type": "Transitive",
"resolved": "2.3.4-torch",
"contentHash": "UnLUnLLiXfHZdKa1zhi6w8cl8tJTrpVixLtvjFEVtlDA6Rkf06OcZ2gSidcbcgKjTcR+fk5Qsdos3mU5oohzfg==",
"dependencies": {
"MonoMod.Core": "1.2.2",
"System.Text.Json": "9.0.0"
}
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg=="
},
"Mono.Cecil": {
"type": "Transitive",
"resolved": "0.11.5",
"contentHash": "fxfX+0JGTZ8YQeu1MYjbBiK2CYTSzDyEeIixt+yqKKTn7FW8rv7JMY70qevup4ZJfD7Kk/VG/jDzQQTpfch87g=="
},
"MonoMod.Backports": {
"type": "Transitive",
"resolved": "1.1.2",
"contentHash": "baYlNy8n8kmaNhNvqmZ/dIPOeO1r9//dG1i2WbunMWtWZ2EKtIgmXaS+ZzphzTsikkGnoD4Jwr5g0TVdpDjgpw==",
"dependencies": {
"MonoMod.ILHelpers": "1.1.0"
}
},
"MonoMod.Core": {
"type": "Transitive",
"resolved": "1.2.2",
"contentHash": "3R9mQ7TxeQQBFLtGTR2VIDSBGx/I7d8RYpxvW1Mot0/PB6+UOnHr+lkw7dj8cus8YSnLdCFT+CG4JW89daEvjg==",
"dependencies": {
"Mono.Cecil": "0.11.5",
"MonoMod.Backports": "1.1.2",
"MonoMod.ILHelpers": "1.1.0",
"MonoMod.Utils": "25.0.8"
}
},
"MonoMod.ILHelpers": {
"type": "Transitive",
"resolved": "1.1.0",
"contentHash": "L2FWjhTrv7tcIxshfZ+M3OcaNr4cNw0IwiVZEgwqRnZ5QAN3+RrNJ8ZwCzwXUWyPDqooJxMcjjg8PsSYUiNBjQ=="
},
"MonoMod.Utils": {
"type": "Transitive",
"resolved": "25.0.8",
"contentHash": "8wfyawgvGqUvfchAj7/gPazAjXO8CWNyXH+C0az+hgJH/80zVL5LxDhQMz0NQTyoCEJWU6gNBGc7CL+G7vCbaQ==",
"dependencies": {
"Mono.Cecil": "0.11.5",
"MonoMod.Backports": "1.1.2",
"MonoMod.ILHelpers": "1.1.0"
}
},
"NLog": {
"type": "Transitive",
"resolved": "5.4.0",
"contentHash": "LwMcGSW3soF3/SL68rlJN3Eh3ktrAPycC3zZR/07OYBPraZUu0bygEC7kIN10lUQgMXT4s84Fi1chglGdGrQEg=="
},
"NuGet": {
"type": "Transitive",
"resolved": "0.1.51",
"contentHash": "fwUsNKy/EQB26JocU3suqYqFqbQ8mb63Ri5NGf7OPy7zLooxHNUYaN+MuBhbshveP+8IKdlPfc8UkrFyzKK8PA==",
"dependencies": {
"NuGet.Frameworks": "6.13.2",
"NuGet.Versioning": "6.13.2",
"System.Linq.Async": "6.0.1"
}
},
"NuGet.Frameworks": {
"type": "Transitive",
"resolved": "6.13.2",
"contentHash": "SQKUf56j7IdJL//fU7fcmn234awjRA2qLvgbfs/TxmY7oJrNOxaaDxASyCbS4eJW3LCOd6ONHMZ/cPlbQh4xVg=="
},
"NuGet.Versioning": {
"type": "Transitive",
"resolved": "6.13.2",
"contentHash": "pGYNyvCVM+Z9jITTiJiuxFC8oJXFdh2k25ZDV4tSAOSuKyAWvh1VcfJy0WZGWdI6J7Avkbl0qra7XENYFSy4Ng=="
},
"protobuf-net": {
"type": "Transitive",
"resolved": "1.0.0",
"contentHash": "kTGOK0E87473sOImOjgZOnz3kTC2aMLffoRWQLYNuBLJnwNNmjanF9IkevZ9Q7yYLeABQfcF3BpeepuMntMVNw=="
},
"SharedCringe": {
"type": "Transitive",
"resolved": "0.1.51",
"contentHash": "9QargvU/hAVUXt1QZaeCHExmoYfrtHP9Vq9X5oMbIX8lOCmjulZx0rSuwoDR46/I1eu4+xVvM78/u+F4UiP6uw==",
"dependencies": {
"CringeBootstrap.Abstractions": "0.1.51",
"NLog": "5.4.0",
"SpaceEngineersDedicated.ReferenceAssemblies": "1.206.30"
}
},
"SharpDX": {
"type": "Transitive",
"resolved": "4.2.0-keen-cringe",
"contentHash": "LaJN3h1Gi1FWVdef2I5WtOH9gwzKCBniH0CragarbkN2QheYY6Lqm+91PcOfp1w/4wdVb+k8Kjv3sO393Tphtw=="
},
"SpaceEngineersDedicated.ReferenceAssemblies": {
"type": "Transitive",
"resolved": "1.206.30",
"contentHash": "xk/EgMhbG7oT4fPzW1DcFT8tYkxJFPK3+j+t4vms9a/wz8cCmszbilA2Y+JWIpmauUDcfovX8eqAOKlgz3dpcg==",
"dependencies": {
"SharpDX": "4.2.0-keen-cringe",
"protobuf-net": "1.0.0"
}
},
"System.Buffers": {
"type": "Transitive",
"resolved": "4.5.1",
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
},
"System.Linq.Async": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "6.0.0"
}
},
"System.Numerics.Vectors": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
},
"System.Text.Json": {
"type": "Transitive",
"resolved": "9.0.0",
"contentHash": "js7+qAu/9mQvnhA4EfGMZNEzXtJCDxgkgj8ohuxq/Qxv+R56G+ljefhiJHOxTNiw54q8vmABCWUwkMulNdlZ4A=="
}
},
"net9.0-windows7.0/win-x64": {
"Steamworks.NET": {
"type": "Direct",
"requested": "[20.1.0, )",
"resolved": "20.1.0",
"contentHash": "+GntwnyJ5tCNvUIaQxv2+ehDvZJzGUqlSB5xRBk1hTj1qqBJ6s4vK/OfGD/jae7aTmXiGSm8wpJORosNtQevJQ=="
},
"ImGui.NET.DirectX": {
"type": "Transitive",
"resolved": "1.91.0.1",
"contentHash": "PpW1gQ9g97h6Hm/h/tkSBOmsBYgGwN8wKNmlJomcQFD/zRY1HPkJZz18XRSfRLHPmH2eeh4hhhZv1KHug7dF9g==",
"dependencies": {
"System.Buffers": "4.5.1",
"System.Numerics.Vectors": "4.5.0",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
}
}
}
}