Discord Rich Presence: New Super Mario Bros U Deluxe (#130)

Add RPC support for NSMBUD play reports that generate on main menu and after finishing a course.

Tracked things:

Main menu
Last played course

Examples:

Main menu
![image](/attachments/9f1506bd-fc8c-4eca-930d-64f15a7c650d)

After finishing a course (By dying or by beating it)
![image](/attachments/a4af3f4f-230d-47ac-977a-20281a103cb6)

In the future should I be doing batch PRs for RPC related things? Yes.

Co-authored-by: Awesomeangotti <143439211+Awesomeangotti@users.noreply.github.com>
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/130
This commit is contained in:
awesomeangotti
2026-06-25 20:59:56 +00:00
committed by sh0inx
parent 8b1b015572
commit be5881f100
4 changed files with 308 additions and 0 deletions

View File

@@ -1,10 +1,12 @@
using Gommon;
using Humanizer;
using MsgPack;
using Ryujinx.Common;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
namespace Ryujinx.Ava.Systems.PlayReport
{
@@ -1116,5 +1118,87 @@ namespace Ryujinx.Ava.Systems.PlayReport
_ => "Wandering"
};
}
private static FormattedValue NsmbudRpc(SparseMultiValue values)
{
if (values.Matched.TryGetValue("WorldNo", out Value world) && values.Matched.TryGetValue("CourseNo", out Value course) | values.Matched.TryGetValue("GameModeType", out Value gamemode))
{
string worldstr = world.ToString();
string coursestr = course.ToString();
int courseint = Int32.Parse(coursestr);
string gamemodestr = gamemode.ToString();
try
{
Dictionary<string, Dictionary<string, Dictionary<string, string>>> output;
string data;
data = EmbeddedResources.ReadAllText("Ryujinx/Assets/RPCData/nsmbud.json");
output = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, Dictionary<string, string>>>>(data);
if (SpecialMapNames(courseint) == "Hazard")
{
return $"Last Played: Course {worldstr}-Hazard";
}
string outputloc = output[MarioOrLuigiGamemode(gamemodestr)][worldstr][coursestr];
return $"Last Played: Course {worldstr}-{SpecialMapNames(courseint)} | {outputloc}";
}
catch
{
return FormattedValue.ForceReset;
}
}
if (values.Matched.TryGetValue("RlId", out Value RlId) | values.Matched.TryGetValue("TotalPlayTime", out Value TotalPlayTime))
{
return "At the main menu";
}
static string MarioOrLuigiGamemode(string? gamemode) => gamemode switch
{
"0" => "mario",
"1" => "luigi",
"4" => "mario",
"5" => "mario",
_ => gamemode
};
static string OtherGameMode(string? gamemode) => gamemode switch
{
"2" => "Boost Rush",
"3" => "Challenges",
"4" => "Coin Battle",
"5" => "Coin Battle Editor",
_ => ""
};
static string SpecialMapNames(int? course) => course switch
{
>= 1 and <= 9 => course.ToString(),
13 => "Shortcut",
14 => "Shortcut",
15 => "Shortcut",
16 => "Shortcut",
17 => "Shortcut",
20 => "Ghost",
21 => "Tower",
22 => "Tower",
23 => "Castle",
37 => "Airship",
42 => "Castle",
43 => "Castle",
_ => "Hazard"
};
// For future reference
// Tower course = 21, Castle course = 23,Haunted Mansion/ship = 20
// Tower course 2 (rock candy) = 22
// Peach castle 1 = 42, Peach final battle = 43
// airship = 37, jungle beetles = 17
// Glacier seals = 16, water leaf = 15
// desert ice = 14, acorn squid = 13
// all other course numbers are to be considered a hazard
return "";
}
}
}

View File

@@ -138,6 +138,12 @@ namespace Ryujinx.Ava.Systems.PlayReport
.WithDescription("based on gold count, report info only in the mii selector, and gamestage (progression)")
.AddSparseMultiValueFormatter(["gold", "secret", "stage"], MiitopiaRPC)
)
.AddSpec(
"0100ea80032ea000", // New Super Mario Bros U Deluxe
spec => spec
.WithDescription("based on world map return info.")
.AddSparseMultiValueFormatter(["WorldNo", "CourseNo", "RlId", "TotalPlayTime", "GameModeType"], NsmbudRpc)
)
);
private static string Playing(string game) => $"Playing {game}";