How Discord Rich Presence is Integrated into SharpEmu: Direct IPC Implementation Without External Libraries
SharpEmu implements Discord Rich Presence by directly communicating with Discord's local IPC protocol over a named pipe (discord-ipc-N), eliminating the need for external libraries like DiscordRPC.dll or the GameSDK.
SharpEmu is an open-source emulator project that includes built-in Discord Rich Presence support for its GUI launcher. According to the source code in the par274/sharpemu repository, the integration uses a lightweight, self-contained client that speaks Discord's native IPC protocol directly, ensuring the launcher never fails if Discord is absent.
Architecture Overview
The integration consists of three specialized components working together:
DiscordRichPresenceclass (src/SharpEmu.GUI/DiscordRichPresence.cs): A minimal client that manages the named pipe connection and formats JSON payloads.GuiSettingsconfiguration (src/SharpEmu.GUI/GuiSettings.cs): Stores the user preference flag and Discord application client ID.MainWindowdispatcher (src/SharpEmu.GUI/MainWindow.axaml.cs): Orchestrates presence updates based on application state changes.
The DiscordRichPresence Class: Core IPC Implementation
Located in src/SharpEmu.GUI/DiscordRichPresence.cs (lines 10–85), this class implements a custom Discord IPC client using .NET's NamedPipeClientStream to connect to Discord's local socket at \\.\pipe\discord-ipc-N.
Connection and Handshake Logic
The constructor initiates a background worker thread that maintains the pipe connection (lines 39–48). This design ensures non-blocking operation while keeping the connection alive for subsequent updates.
// From DiscordRichPresence.cs - constructor initializes the worker
public DiscordRichPresence(string clientId)
{
_clientId = clientId;
_worker = new Thread(WorkerLoop);
_worker.Start();
}
Activity Payload Construction
The SetPresence method (lines 55–78) constructs the JSON payload required by Discord's IPC protocol. It packages the activity data into a dictionary with the cmd field set to "SET_ACTIVITY", includes timestamps for elapsed time display, and writes the frame to the named pipe.
// Payload structure sent to Discord
var payload = new Dictionary<string, object>
{
["cmd"] = "SET_ACTIVITY",
["args"] = new Dictionary<string, object>
{
["activity"] = new Dictionary<string, object>
{
["details"] = details,
["state"] = state,
["timestamps"] = new Dictionary<string, long>
{
["start"] = startTimestamp
}
}
}
};
Configuration Management
User preferences are defined in src/SharpEmu.GUI/GuiSettings.cs (lines 44–48). The system uses two key settings:
DiscordRichPresence: A boolean flag enabling or disabling the feature.DiscordClientId: The Discord application ID required for authenticating with the IPC service.
// From GuiSettings.cs
public bool DiscordRichPresence { get; set; } = true;
public string DiscordClientId { get; set; } = "";
UI Integration and State Management
The MainWindow.axaml.cs file (lines 65–96) contains the UpdateDiscordPresence() method, which serves as the central dispatch point for presence updates.
Lazy Initialization Pattern
The implementation uses a null-coalescing assignment to instantiate the client only when needed:
// From MainWindow.axaml.cs
_discord ??= new DiscordRichPresence(_settings.DiscordClientId);
State Switching Logic
The method distinguishes between two states:
- Browsing State: When no game is running, it displays "Browsing the library" with the launcher start time.
- Running State: When a game is active, it shows the game name and title ID with the session elapsed time.
// From MainWindow.axaml.cs - UpdateDiscordPresence implementation
private void UpdateDiscordPresence()
{
if (!_settings.DiscordRichPresence || _settings.DiscordClientId.Length == 0)
return;
_discord ??= new DiscordRichPresence(_settings.DiscordClientId);
if (_isRunning && _runningGameName is { } name)
{
_discord.SetPresence($"Playing {name}", _runningGameTitleId, _runningSinceUnixSeconds);
}
else
{
_discord.SetPresence("Browsing the library",
$"{_allGames.Count} game(s)",
_launcherStartUnixSeconds);
}
}
Step-by-Step Integration Flow
The complete lifecycle of a Discord Rich Presence update follows this sequence:
- Configuration Check: At startup,
GuiSettings.Load()validates thatDiscordRichPresenceis enabled and aDiscordClientIdis present. - Client Creation:
UpdateDiscordPresence()lazily instantiates theDiscordRichPresenceclass, which spawns the background worker thread and opens the named pipe. - Payload Dispatch: When the emulator state changes,
SetPresenceformats the activity data as JSON and transmits it through the IPC channel. - Idle State Handling: If the user returns to the library, the presence resets to the browsing state with the original launcher timestamp.
- Graceful Teardown: When the application closes or the user disables the feature, the client calls
Dispose()to shut down the worker thread and close the pipe connection.
Practical Implementation Examples
Manual Client Instantiation
// Create client with your Discord application ID
var discord = new DiscordRichPresence("123456789012345678");
// Set browsing status
discord.SetPresence("Browsing the library",
"10 games loaded",
DateTimeOffset.UtcNow.ToUnixTimeSeconds());
// Update to running status when game starts
discord.SetPresence("Playing Final Fantasy",
"NPUB12345",
gameStartUnixSeconds);
// Cleanup resources
discord.Dispose();
Toggle Integration via UI
// Event handler for settings checkbox
DiscordToggle.IsCheckedChanged += (_, _) =>
{
_settings.DiscordRichPresence = DiscordToggle.IsChecked == true;
UpdateDiscordPresence(); // Refresh immediately
};
Summary
- Self-contained implementation: SharpEmu implements the Discord IPC protocol directly without external dependencies.
- Named pipe communication: Uses
NamedPipeClientStreamto connect todiscord-ipc-Nfor local IPC. - Silent failure mode: All connection errors are caught and ignored, ensuring the emulator launches successfully regardless of Discord's state.
- Lazy initialization: The
DiscordRichPresenceclient is only created when needed to conserve resources. - State-aware updates: The
UpdateDiscordPresence()method automatically switches between browsing and gaming states based on emulator activity.
Frequently Asked Questions
Does SharpEmu use the official Discord GameSDK or DiscordRPC library?
No. According to the source code in DiscordRichPresence.cs, SharpEmu implements Discord's local IPC protocol directly using .NET's NamedPipeClientStream. This approach eliminates external dependencies and reduces the distribution size.
What happens if Discord is not running when SharpEmu starts?
The integration is designed to fail silently. All exceptions during pipe connection or communication are caught and suppressed, allowing the launcher to function normally without Discord present.
Where is the Discord application client ID stored?
The client ID is stored in the DiscordClientId property of the GuiSettings class (lines 44–48 in GuiSettings.cs). This value is required for the IPC handshake and must be set by the user for the feature to function.
How does the presence update when switching between games?
The UpdateDiscordPresence() method in MainWindow.axaml.cs is called whenever the emulator state changes. It detects the current game through the _isRunning and _runningGameName fields, then calls SetPresence with the new game details and the Unix timestamp when the game started.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →