This is a C# implementation of the Discord RPC library which was originally written in C++. This avoids having to use the official C++ and instead provides a managed way of using the Rich Presence within the .NET environment*.
While the official C++ library has been deprecated, this library has continued support and development for all your Rich Presence need, without requiring the Game SDK.
Here are some key features of this library:
- Message Queuing
- Threaded Reads
- Managed Pipes*
- Error Handling & Error Checking with automatic reconnects
- Events from Discord (such as presence update and join requests)
- Full Rich Presence Implementation (including Join / Spectate)
- Inline Documented (for all your IntelliSense needs)
- Helper Functionality (eg: AvatarURL generator from Join Requests)
- Ghost Prevention (Tells Discord to clear the RP on disposal)
- Full Unity3D Editor (Contains all the tools, inspectors and helpers for a Unity3D game all in one package).
All the documentation can be found lachee.github.io/discord-rpc-csharp/docs/
Dependencies:
- .NET 8.0
For projects that target either .NET Core or .NETFX, you can get the package on nuget:
PM> Install-Package DiscordRichPresence
You can also Download or Build your own version of the library if you have more specific requirements.
Unity Package is being moved to Lachee/Discord-RPC-Unity. Please check the releases / documentation there.
The Discord.Example project within the solution contains example code, showing how to use all available features. For Unity Specific examples, check out the example project included. There are 3 important stages of usage, Initialization, Invoking and Deinitialization. It's important you follow all 3 stages to ensure proper behaviour of the library.
Initialization
This stage will setup the connection to Discord and establish the events. Once you have done the initialization you can call SetPresence
and other variants as many times as you wish throughout your code. Please note that ideally this should only run once, otherwise conflicts may occur with them trying to access the same Discord client at the same time.
public DiscordRpcClient client;
//Called when your application first starts.
//For example, just before your main loop, on OnEnable for unity.
void Initialize()
{
/*
Create a Discord client
NOTE: If you are using Unity3D, you must use the full constructor and define
the pipe connection.
*/
client = new DiscordRpcClient("my_client_id");
//Set the logger
client.Logger = new ConsoleLogger() { Level = LogLevel.Warning };
//Subscribe to events
client.OnReady += (sender, e) =>
{
Console.WriteLine("Received Ready from user {0}", e.User.Username);
};
client.OnPresenceUpdate += (sender, e) =>
{
Console.WriteLine("Received Update! {0}", e.Presence);
};
//Connect to the RPC
client.Initialize();
//Set the rich presence
//Call this as many times as you want and anywhere in your code.
client.SetPresence(new RichPresence()
{
Details = "Example Project",
State = "csharp example",
Assets = new Assets()
{
LargeImageKey = "image_large",
LargeImageText = "Lachee's Discord IPC Library",
SmallImageKey = "image_small"
}
});
}
Invoking
Invoking is optional. Use this when thread safety is paramount.
The client will store messages from the pipe and won't invoke them until you call Invoke()
or DequeueMessages()
. It does this because the pipe is working on another thread, and manually invoking ensures proper thread safety and order of operations (especially important in Unity3D applications).
In order to enable this method of event calling, you need to set it in the constructor of the DiscordRpcClient under autoEvents
.
//The main loop of your application, or some sort of timer. Literally the Update function in Unity3D
void Update()
{
//Invoke all the events, such as OnPresenceUpdate
client.Invoke();
};
Here is an example on how a Timer could be used to invoke the events for a WinForm
var timer = new System.Timers.Timer(150);
timer.Elapsed += (sender, args) => { client.Invoke(); };
timer.Start();
Deinitialization
It's important that you dispose your client before your application terminates. This will stop the threads, abort the pipe reads, and tell Discord to clear the presence. Failure to do so may result in a memory leak!
//Called when your application terminates.
//For example, just after your main loop, on OnDisable for unity.
void Deinitialize()
{
client.Dispose();
}
DiscordRPC Library
dotnet build -c Release
Unity3D
If you wish to have barebones Unity3D implementation, you need to build the DiscordRPC.dll
, the Unity Named Pipes Library and the UnityNamedPipe.cs. Put these in your own Unity Project and the .dll
s in a folder called Plugins
.
UWP / .NET MAUI / WIN UI 3
For now, the library doesn't work on UWP applications until we find the issue and fix it.
In order to make this library work with the WIN UI 3 related applications such as .NET MAUI, you need to define runFullTrust
Capability inside Package.appxmanifest
.
Here is an example of how to add runFullTrust
to your WIN UI 3 application:
Package.appxmanifest
:
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
...
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>
If you use .NET MAUI or WIN UI 3 template for C#, it automatically puts runFullTrust
capability.
To add a new command you have to edit/create some files. Here is a list of the files you need to edit:
Command.cs
- Check if the enum value exists. Remove[Obsolete("...", true)]
if necessary or add a new enum value to theCommand
enum.Events.cs
- Add a new event of following form:
public delegate void CommandNameEvent(object sender, VoiceSettingsMessage args);
MessageType.cs
- Add a new message type to the enum. Normally asCommandName
.- create
CommandNameCommand.cs
- Create a new class that inherits fromICommand
and implements thePreparePayload
method. This method should contain all Command arguments. Also create an internal classCommandNameResponse
that contains all fields from the response. Make it serializable. - create
CommandNameMessage.cs
- Create a new class that inherits fromMessage
and implements theMessageType
property. This class should contain all Command response arguments.
Following changes are necessary in DiscordRpcClient.cs
:
- Add a new event to the
DiscordRpcClient
class of the form:
public event CommandNameEvent OnCommandName;
- Add the Command logic of following form:
public void CommandName(...)
{
if (IsDisposed)
throw new ObjectDisposedException("Discord IPC Client");
if (connection == null)
throw new ObjectDisposedException("Connection", "Cannot initialize as the connection has been deinitialized");
if (!IsInitialized)
throw new UninitializedException();
connection.EnqueueCommand(new CommandNameCommand());
}
- Add in the
ProcessMessage
method the new MessageType case, so it triggers the event:
case MessageType.CommandName:
OnAuthorize?.Invoke(this, message as AuthorizeMessage);
break;
Following changes are necessary in RpcConnection.cs
:
- In the
ProcessFrame
method add a new case for the new MessageType:
case Command.CommandEnum:
var commandResponse = response.GetObject<CommandNameResponse>();
EnqueueMessage(new CommandNameMessage(commandResponse));
break;
Following changes are necessary in JsonSerializationContext.cs
:
- Add
JsonSerializable(typeof(CommandNameCommand)),
- Add
JsonSerializable(typeof(ArgumentPayload<CommandNameCommand>)),
- Add
JsonSerializable(typeof(CommandNameResponse)),
- Add
JsonSerializable(typeof(CommandNameMessage)),