Appearance
C# (.NET)
C# and .NET applications consume the local Riot Client by spawning the riotclient CLI as a child process and deserializing its standard JSON output using System.Text.Json. Strong typing is provided by generating classes from the committed JSON Schemas using quicktype.
Setup
- Install the CLI globally using npm:
bash
npm install -g @valoranchi/riot-client- Verify your .NET project targets
.NET 8.0or later (System.Text.Jsonis built into the base class library).
Read
Spawn riotclient with ProcessStartInfo, capturing standard output. This example reads the player's identity (whoami) and owned collection (owned-items), printing the first owned skin per weapon:
csharp
public static async Task ReadAsync()
{
// 1. Read player identity
using (var whoami = Process.Start(CreateStartInfo("whoami"))!)
{
string output = await whoami.StandardOutput.ReadToEndAsync();
await whoami.WaitForExitAsync();
if (whoami.ExitCode == 0)
{
using var doc = JsonDocument.Parse(output);
var root = doc.RootElement;
Console.WriteLine($"Player: {root.GetProperty("gameName").GetString()}#{root.GetProperty("tagLine").GetString()} ({root.GetProperty("region").GetString()})");
}
}
// 2. Read owned collection with deserialization
using (var collection = Process.Start(CreateStartInfo("owned-items --language en-US"))!)
{
string output = await collection.StandardOutput.ReadToEndAsync();
await collection.WaitForExitAsync();
if (collection.ExitCode == 0)
{
var items = JsonSerializer.Deserialize<OwnedItems>(output, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
})!;
Console.WriteLine($"Collection generated at: {items.GeneratedAt}");
foreach (var weapon in items.Weapons)
{
if (weapon.Skins.Length > 0)
{
Console.WriteLine($"- {weapon.Name}: {weapon.Skins[0].Name}");
}
}
}
}
}Write with Validation
Every mutation command defaults to a local dry run. It validates arguments and local ownership against Riot's rules without sending network requests. To execute the write, append --yes.
If validation fails, riotclient exits with code 6 and outputs structured error information to standard error:
csharp
public static async Task EquipCard(string cardUuid, bool dryRun)
{
string args = dryRun
? $"equip --card {cardUuid}"
: $"equip --card {cardUuid} --yes";
var psi = new ProcessStartInfo
{
FileName = "riotclient",
Arguments = args,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
};
using var process = Process.Start(psi)!;
string stdout = await process.StandardOutput.ReadToEndAsync();
string stderr = await process.StandardError.ReadToEndAsync();
await process.WaitForExitAsync();
if (process.ExitCode == 0)
{
Console.WriteLine(dryRun ? "Dry run succeeded:" : "Equipped card successfully:");
Console.WriteLine(stdout.Trim());
}
else if (process.ExitCode == 6)
{
// Validation error
using var errorDoc = JsonDocument.Parse(stderr);
var err = errorDoc.RootElement.GetProperty("error");
string reason = err.GetProperty("reason").GetString()!;
string message = err.GetProperty("message").GetString()!;
Console.Error.WriteLine($"Validation failed [code 6]: {reason} - {message}");
}
else
{
Console.Error.WriteLine($"Command failed with exit code {process.ExitCode}: {stderr}");
}
}Events
Subscribe to real-time events by spawning riotclient watch. Read standard output asynchronously line-by-line with ReadLineAsync():
csharp
public static async Task StreamEvents()
{
var psi = new ProcessStartInfo
{
FileName = "riotclient",
Arguments = "watch --only friend:presence,message",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
};
using var process = Process.Start(psi)!;
Console.WriteLine("Streaming events from Riot Client. Press Ctrl+C to terminate.");
while (await process.StandardOutput.ReadLineAsync() is { } line)
{
if (string.IsNullOrWhiteSpace(line)) continue;
using var doc = JsonDocument.Parse(line);
string eventType = doc.RootElement.GetProperty("event").GetString()!;
var data = doc.RootElement.GetProperty("data");
if (eventType == "friend:presence")
{
var friend = data.GetProperty("friend");
string name = friend.GetProperty("gameName").GetString()!;
string change = data.GetProperty("change").GetString()!;
string state = friend.GetProperty("presence").GetProperty("state").GetString()!;
Console.WriteLine($"Friend {name} is now {change} ({state})");
}
else if (eventType == "message")
{
string from = data.GetProperty("from").GetProperty("gameName").GetString()!;
string body = data.GetProperty("body").GetString()!;
Console.WriteLine($"[{from}]: {body}");
}
}
}Types
Generate strong C# model classes from the JSON schemas using quicktype:
bash
# Generate C# classes for OwnedItems
npx quicktype schema/OwnedItems.json --src-lang schema -l csharp -o OwnedItems.cs --namespace Valoranchi.ModelThis generates System.Text.Json attributed classes matching the library schema:
csharp
namespace Valoranchi.Model
{
public partial class Player
{
public string Puuid { get; set; } = string.Empty;
public string GameName { get; set; } = string.Empty;
public string TagLine { get; set; } = string.Empty;
public string Region { get; set; } = string.Empty;
public string Shard { get; set; } = string.Empty;
public int AccountLevel { get; set; }
}
public partial class OwnedItems
{
public Player? Player { get; set; }
public string Language { get; set; } = string.Empty;
public string GeneratedAt { get; set; } = string.Empty;
public OwnedWeapon[] Weapons { get; set; } = Array.Empty<OwnedWeapon>();
}
public partial class OwnedWeapon
{
public string Name { get; set; } = string.Empty;
public string Category { get; set; } = string.Empty;
public OwnedSkin[] Skins { get; set; } = Array.Empty<OwnedSkin>();
}
public partial class OwnedSkin
{
public string Uuid { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
}
}