Skip to content

Commit 9a99615

Browse files
committed
Add Discord Presence Extension + Restructure SharedStatic.Generic
This changes wouldn't introduce breaking changes and still works on legacy APIs
1 parent 2cd83ec commit 9a99615

File tree

8 files changed

+584
-158
lines changed

8 files changed

+584
-158
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
using Hi3Helper.Plugin.Core.Management.PresetConfig;
2+
using Hi3Helper.Plugin.Core.Utility;
3+
using System;
4+
using System.Runtime.InteropServices;
5+
using System.Runtime.InteropServices.Marshalling;
6+
using static Hi3Helper.Plugin.Core.SharedStaticV1Ext;
7+
8+
namespace Hi3Helper.Plugin.Core.DiscordPresence;
9+
10+
/// <summary>
11+
/// This extension provides a method to get the Discord Presence information for the current game region.
12+
/// </summary>
13+
/// <remarks>
14+
/// This extension IS ONLY SUPPOSEDLY BE USED by the launcher, NOT by the plugin.
15+
/// </remarks>
16+
public static class DiscordPresenceExtension
17+
{
18+
/// <summary>
19+
/// A context used to manage the context of Discord Presence information.
20+
/// </summary>
21+
public unsafe class DiscordPresenceContext : IDisposable
22+
{
23+
// Fields
24+
private DiscordPresenceInfo* _data;
25+
26+
internal DiscordPresenceContext(IPluginPresetConfig presetConfig) : this(0, presetConfig)
27+
{
28+
}
29+
30+
public DiscordPresenceContext(nint pluginHandle, IPluginPresetConfig presetConfig)
31+
{
32+
if (pluginHandle == nint.Zero)
33+
{
34+
return;
35+
}
36+
37+
if (!pluginHandle.TryGetExport("GetCurrentDiscordPresenceInfo",
38+
out GetCurrentDiscordPresenceInfoDelegate method))
39+
{
40+
return;
41+
}
42+
43+
DiscordPresenceInfo** info = null;
44+
45+
void* ptr = ComInterfaceMarshaller<IPluginPresetConfig>.ConvertToUnmanaged(presetConfig);
46+
HResult result = method(ptr, info);
47+
48+
if (result == HResult.Ok && info != null && *info != null)
49+
{
50+
_data = *info;
51+
}
52+
}
53+
54+
/// <summary>
55+
/// Indicates whether the Discord Presence feature is available.
56+
/// </summary>
57+
public bool IsFeatureAvailable => _data != null;
58+
59+
/// <inheritdoc cref="DiscordPresenceInfo.PresenceId"/>
60+
public ulong PresenceId => _data != null ? _data->PresenceId : 0;
61+
62+
/// <inheritdoc cref="DiscordPresenceInfo.LargeIconUrl"/>
63+
public string? LargeIconUrl
64+
{
65+
get
66+
{
67+
if (_data == null || _data->LargeIconUrl == null)
68+
{
69+
return null;
70+
}
71+
72+
return field ??= Utf16StringMarshaller.ConvertToManaged(_data->LargeIconUrl);
73+
}
74+
}
75+
76+
/// <inheritdoc cref="DiscordPresenceInfo.LargeIconTooltip"/>
77+
public string? LargeIconTooltip
78+
{
79+
get
80+
{
81+
if (_data == null || _data->LargeIconTooltip == null)
82+
{
83+
return null;
84+
}
85+
86+
return field ??= Utf16StringMarshaller.ConvertToManaged(_data->LargeIconTooltip);
87+
}
88+
}
89+
90+
/// <inheritdoc cref="DiscordPresenceInfo.SmallIconUrl"/>
91+
public string? SmallIconUrl
92+
{
93+
get
94+
{
95+
if (_data == null || _data->SmallIconUrl == null)
96+
{
97+
return null;
98+
}
99+
100+
return field ??= Utf16StringMarshaller.ConvertToManaged(_data->SmallIconUrl);
101+
}
102+
}
103+
104+
/// <inheritdoc cref="DiscordPresenceInfo.SmallIconTooltip"/>
105+
public string? SmallIconTooltip
106+
{
107+
get
108+
{
109+
if (_data == null || _data->SmallIconTooltip == null)
110+
{
111+
return null;
112+
}
113+
114+
return field ??= Utf16StringMarshaller.ConvertToManaged(_data->SmallIconTooltip);
115+
}
116+
}
117+
118+
/// <inheritdoc cref="IDisposable.Dispose"/>
119+
public void Dispose()
120+
{
121+
if (_data == null)
122+
{
123+
return;
124+
}
125+
126+
_data->Dispose();
127+
NativeMemory.Free(_data);
128+
129+
_data = null;
130+
131+
GC.SuppressFinalize(this);
132+
}
133+
}
134+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System;
2+
using System.Runtime.InteropServices;
3+
using System.Runtime.InteropServices.Marshalling;
4+
using Hi3Helper.Plugin.Core.Utility;
5+
6+
namespace Hi3Helper.Plugin.Core.DiscordPresence;
7+
8+
/// <summary>
9+
/// Represents presence information for a Discord user, including logo URLs and tooltips for display in rich presence
10+
/// integrations.
11+
/// </summary>
12+
[StructLayout(LayoutKind.Sequential)]
13+
public unsafe struct DiscordPresenceInfo : IDisposable
14+
{
15+
/// <summary>
16+
/// Represents the UID of the Discord Application ID.
17+
/// </summary>
18+
public ulong PresenceId; // 00 - 08
19+
20+
/// <summary>
21+
/// Represents which logo to be used for large-icon display in Discord.<br/>
22+
/// The format of the URL can be just the name of the asset inside the Discord Application -> Rich Presence -> Art Assets menu or a full URL to an external logo (max: 256 chars).<br/>
23+
/// </summary>
24+
public ushort* LargeIconUrl; // 08 - 16
25+
26+
/// <summary>
27+
/// The tooltip text to be displayed when hovering over the large-icon in Discord.
28+
/// </summary>
29+
public ushort* LargeIconTooltip; // 16 - 24
30+
31+
/// <summary>
32+
/// Represents which logo to be used for small-icon display in Discord.<br/>
33+
/// The format of the URL can be just the name of the asset inside the Discord Application -> Rich Presence -> Art Assets menu or a full URL to an external logo (max: 256 chars).<br/>
34+
/// </summary>
35+
public ushort* SmallIconUrl; // 24 - 32
36+
37+
/// <summary>
38+
/// The tooltip text to be displayed when hovering over the small-icon in Discord.
39+
/// </summary>
40+
public ushort* SmallIconTooltip; // 32 - 40
41+
42+
/// <summary>
43+
/// Reserved for future use. Should be unused for now.
44+
/// </summary>
45+
public void* Reserved; // 40 - 48 (For future use)
46+
47+
/// <inheritdoc cref="IDisposable.Dispose"/>
48+
public void Dispose()
49+
{
50+
Utf16StringMarshaller.Free(LargeIconUrl);
51+
Utf16StringMarshaller.Free(LargeIconTooltip);
52+
Utf16StringMarshaller.Free(SmallIconUrl);
53+
Utf16StringMarshaller.Free(SmallIconTooltip);
54+
55+
if (Reserved != null)
56+
{
57+
Mem.Free(Reserved);
58+
}
59+
}
60+
}

SharedStatic.V1Ext.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using Hi3Helper.Plugin.Core.DiscordPresence;
2+
using Hi3Helper.Plugin.Core.Management;
3+
using Hi3Helper.Plugin.Core.Utility;
4+
using System;
5+
6+
namespace Hi3Helper.Plugin.Core;
7+
8+
public class SharedStaticV1Ext : SharedStatic
9+
{
10+
// Update1
11+
internal delegate HResult LaunchGameFromGameManagerAsyncDelegate(nint gameManagerP, nint pluginP, nint presetConfigP, nint printGameLogCallbackP, nint arguments, int argumentsLen, int runBoostedInt, int processPriorityInt, ref Guid cancelToken, out nint taskResult);
12+
internal delegate HResult WaitRunningGameAsyncDelegate(nint gameManagerP, nint pluginP, nint presetConfigP, ref Guid cancelToken, out nint taskResult);
13+
internal delegate HResult IsGameRunningDelegate(nint gameManagerP, nint presetConfigP, out int isGameRunning, out DateTime processStartTime);
14+
15+
// Update2
16+
internal unsafe delegate HResult GetCurrentDiscordPresenceInfoDelegate(void* presetConfigP,
17+
DiscordPresenceInfo** presenceInfoP);
18+
19+
}
20+
21+
/// <summary>
22+
/// Inherited <see cref="SharedStatic"/> with additional supports for API extensions which require call or property access to derived exports.
23+
/// </summary>
24+
public partial class SharedStaticV1Ext<T> : SharedStaticV1Ext where T : SharedStatic, new()
25+
{
26+
private static readonly SharedStaticV1Ext<T> ThisExtensionExport;
27+
28+
static SharedStaticV1Ext()
29+
{
30+
ThisExtensionExport = new SharedStaticV1Ext<T>();
31+
32+
/* ----------------------------------------------------------------------
33+
* Plugin extension exports
34+
* ----------------------------------------------------------------------
35+
* These exports are optional and can be removed if it's not necessarily
36+
* used. These optional exports are included under additional
37+
* functionalities used as a subset of v0.1, which is called "update1"
38+
* feature sets.
39+
*/
40+
InitExtension_Update1Exports();
41+
InitExtension_Update2Exports();
42+
}
43+
44+
/// <summary>
45+
/// Specify which <see cref="IPlugin"/> instance to load and use in this plugin.
46+
/// </summary>
47+
/// <typeparam name="TPlugin">A member of COM Interface of <see cref="IPlugin"/>.</typeparam>
48+
protected new static void Load<TPlugin>(GameVersion interceptDllVersionTo = default)
49+
where TPlugin : class, IPlugin, new()
50+
=> SharedStatic.Load<TPlugin>(interceptDllVersionTo);
51+
}

0 commit comments

Comments
 (0)