Scope and getting started
Reference for the public runtime of SDK 0.3.0-preview.2 with HUD API 3: SOU.Hud.Sdk, SOU.Hud.Sdk.Blazor and JavaScript sdk/sou-hud.js. C# uses .NET 10. Published desktop 0.3.34 includes the matching unified v3 session. A host implementing that session is required; the SDK does not compare a numeric minimum desktop version.
The development guide covers installation, CLI, templates, preview and packaging. The data reference explains sources and meaning, and the type reference describes contract fields. See the panel reference and package format for additional rules.
A new HUD uses one session per output. Blazor uses SouHudHost. Unless noted otherwise, C# types belong to SOU.Hud.Sdk, the graph and server clock to SOU.Live.Contracts, and components to SOU.Hud.Sdk.Blazor. JavaScript imports exports from sdk/sou-hud.js; load the bundled SignalR client first. — in tables means there is no direct equivalent.
Create, start and stop a session
| C# / Blazor | JavaScript | Contract |
|---|---|---|
new HudSession<TOptions>(HudClient client, JsonTypeInfo<TOptions> optionsType) | new HudSession(client, options = {}) | Creates without starting network activity. C# requires a client and generated option metadata. JavaScript requires a client implementing watchSessionGraphV3. |
— | createHudSession(client = new HudClient(), options = {}) | Factory for the same session. The default client uses the current package address. |
Task StartAsync(CancellationToken cancellationToken = default) | start(): Promise<void> | Starts once without waiting for a reachable source. Repeated calls open no additional connection. The token or option signal applies to the whole lifetime. |
Task Completion | completion: Promise<void> | Observe lifetime separately from startup. C# is already complete before starting; afterwards this is the run task. JavaScript remains pending before startup until termination or disposal. Normal cancellation and handled option faults are reported in state. |
ValueTask DisposeAsync() | dispose(): Promise<void> | Cancels the owned stream, waits for termination and removes subscriptions. Repeated disposal is supported. C# starting after disposal throws. JavaScript rejects a first start after prior disposal; if already started, start() only returns its existing startup promise and does not restart. |
HudIdentity Identity | HudClient.identity | Package ID and version. JavaScript reads them on the originating client. |
The session has no public manual reset. Transport/protocol loss first invalidates every domain; reconnection then supplies a new complete baseline. A local option fault permanently stops that instance; after correcting it, create a new instance or reload the document. Do not start a panel stream or options polling loop alongside it.
Read current state and readiness
| C# / Blazor | JavaScript | Contract |
|---|---|---|
HudSessionSnapshot<TOptions> Current | current: HudSessionSnapshot<TOptions> | Atomically published state. Do not mutate it; treat custom C# option objects as immutable too. |
bool Ready; HudConnectionStatus Connection | ready: boolean; connection: HudConnectionStatus | Ready only after a validated baseline of all four domains. Socket status Connected/connected is insufficient. C#: Connecting, Connected, Reconnecting, Stopped; JS: connecting, connected, reconnecting, stopped. |
HudV3Graph? Game; HudPanelView? Panel; HudViewStateV3? View; TOptions? Options | game; panel; view; options | Unavailable before baseline/after reset. C# Options is default(TOptions); JS uses null. Use a reference/record option type and check Ready. A ready graph can still have Info=null when game data is absent. |
HudSessionFault? Error; long Sequence | error: HudSessionError | null; sequence: number | Last fault and local publication sequence, not a server/game revision. A successfully accepted update clears the fault. Status changes can retain it until then. |
LiveSnapshot? Snapshot | snapshot: DeepReadonly<LiveSnapshot> | null | Lazy aggregate projection into the older snapshot shape. Select graph parts directly for new displays. Retained graphs continue to represent their historical state. |
A session reset does not itself reload the browser document. Session readiness, server clock readiness and source freshness are separate. 0 is a real value, null means unknown, and an empty list is explicitly empty.
Subscriptions and events
| C# / Blazor | JavaScript | Contract |
|---|---|---|
IDisposable Observe<T>(Func<HudSessionSnapshot<TOptions>,T> selector, Action<T> callback, IEqualityComparer<T>? comparer = null, bool emitCurrent = true) | observe(selector, listener, { equals = Object.is, emitCurrent = true } = {}): Unsubscribe | Evaluates current selection immediately, emits immediately by default, then only on change. C# compares with EqualityComparer<T>.Default. The JS listener receives (value,state). Disabling the initial notification still establishes a comparison value. |
event Action<HudSessionSnapshot<TOptions>> StateChanged | onState(listener, { emitCurrent = false } = {}): Unsubscribe | Every new publication. The C# event has no initial replay. JS optionally emits current state. |
event Action<HudPanelAction> PanelAction | onAction(listener): Unsubscribe | Accepted live action after state commit. No replay on registration, baseline or reconnection. Use Id, Section, Name and Value from the contract object. |
event Action<HudSessionFault> ErrorOccurred | onError(listener): Unsubscribe | Session faults; JavaScript also reports isolated subscriber failures here. No initial replay. |
event Action<Exception> SubscriberFailed | onError(listener): Unsubscribe | Selector/comparison/callback failures do not stop other subscriptions or the session. Error-listener failures are isolated. JavaScript uses kind=subscriber. |
Remove C# handles with Dispose() and your own event handlers with -=. JavaScript returns a deregistration function Unsubscribe = () => void. It removes only that subscription. End the entire owned session during teardown; do not register subscriptions after disposal. Callbacks are synchronous and should not block the feed. Custom Blazor components dispatch through InvokeAsync; SouHudRegion handles this.
Select scalars, immutable records or retained graph parts. A new JavaScript object on every selector call is always changed under Object.is. A custom comparison must include all displayed fields. Subscriptions open no additional connection or clock.
Validate options before commit
| C# / Blazor | JavaScript | Contract |
|---|---|---|
JsonTypeInfo<TOptions> optionsType | parseOptions(value) => TOptions | C# deserializes using metadata; option JSON must be an object and the result non-null. Identical raw JSON is not deserialized again within a connection. JS uses the object unchanged without a parser. A parser is synchronous, not an array or promise. |
— | validateOptions(value): boolean | void | Optional; false or an exception signals failure. No promise. |
— | optionsEqual(previous,next): boolean | Optional; true retains the previous immutable reference. No asynchronous comparison. |
StartAsync(cancellationToken) | options.signal?: AbortSignal | Lifetime cancellation. An already-aborted signal starts no running stream. |
SouHudSettings.Diagnostics: bool? = null | options.diagnostics?: boolean | Explicitly on/off. Unset follows ?diagnostics=1 in the document or JS client address. |
Option preparation runs before atomic commit; failure publishes no partial baseline. JS copies custom option objects as immutable JSON: finite numbers, no cycles/functions/undefined/arbitrary class instances; maximum nesting depth 64. C# uses your metadata/converters: use immutable application types and your own domain validation.
C# example with complete lifetime
Complete file for a .NET 10 project referencing the SDK. The caller keeps HttpClient alive, supplies a host package address and cancels through the token. In Blazor use the shared DI session instead.
using System.Text.Json.Serialization;
using SOU.Hud.Sdk;
public sealed record ReferenceOptions(string Caption = "My HUD");
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
[JsonSerializable(typeof(ReferenceOptions))]
internal partial class ReferenceJson : JsonSerializerContext;
public static class SessionReference
{
public static async Task RunAsync(HttpClient http, Uri packageAddress, CancellationToken stop)
{
await using var session = new HudSession<ReferenceOptions>(
new HudClient(http, packageAddress), ReferenceJson.Default.ReferenceOptions);
using var map = session.Observe(
state => state.Game?.Info?.Map,
value => Console.WriteLine(value ?? "Waiting for game data"));
await session.StartAsync(stop);
await session.Completion;
}
}
Complete JavaScript module with disposal
Place this module beside the template-generated sdk directory. The caller uses runHud(signal) and invokes the returned dispose() method during teardown. For visible output replace console output with textContent on your own element.
import { HudClient, createHudSession } from './sdk/sou-hud.js';
export function runHud(signal) {
const session = createHudSession(new HudClient(), {
signal,
parseOptions(raw) {
const caption = raw.caption ?? 'My HUD';
if (typeof caption !== 'string') throw new TypeError('caption must be a string');
return { caption };
},
validateOptions: value => value.caption.length <= 100,
optionsEqual: (previous, next) => previous.caption === next.caption
});
const unsubscribe = session.observe(
state => state.game?.info?.map ?? 'Waiting for game data',
value => console.log(value)
);
const removeError = session.onError(error => console.error(error.kind, error.message));
void session.start();
return {
session,
async dispose() {
unsubscribe();
removeError();
await session.dispose();
}
};
}
Read panel values safely
| C# / Blazor | JavaScript | Contract |
|---|---|---|
HudPanelView.Revision; Values; References | HudPanelView.revision; values; references | Immutable grouped dictionaries. Values contains C# JsonElement values or JavaScript JSON values. |
JsonElement? Get(string section, string field) | get(section,name): unknown | Missing fields return null. JS also returns null for a stored JSON null. |
string? Text(string section, string field, string? fallback = null) | text(section,name,fallback = null): string | null | String values only. Missing/wrong type returns fallback; an intentionally empty string remains empty. |
bool Flag(string section, string field, bool fallback = false) | flag(section,name,fallback = false): boolean | Boolean values only; no string/number coercion. |
HudPanelReference? Reference(string section, string field) | reference(section,name): HudPanelReference | null | Resolved reference or null; do not guess identity from labels. |
HudPanelState ToState() | toState(): HudPanelState | Defensive copy into mutable DTO form; modifications do not affect the session/panel. |
— | new HudPanelView(state) | For custom adapters; copies and freezes JSON. C# views come from the session. |
Select from the immutable game graph
| C# / Blazor | JavaScript | Contract |
|---|---|---|
HudV3Graph.SessionId; SchemaVersion; Revision; ReceivedAt; Freshness | sessionId; revision; receivedAt; freshness | Graph metadata. C# SchemaVersion is 3. Preserve source timestamps and do not infer freshness from socket status. |
Info; Clock; Roster; Radar; GrenadeOrder; KillOrder | info; clock; roster; radar; grenadeOrder; killOrder | Typed singleton objects or null. Roster.Players/roster.players and order keys define order. Graph Clock is domain state, not a running server clock. |
ManagedMatch; GameMatch | managedMatch; gameManagedMatch | Prepared match at snapshot level or match in the game object. Do not equate their sources. |
ImmutableDictionary<string,HudPlayerPartsV3> Players | players: ReadonlyMap<string,HudPlayerPartsV3> | Opaque keys. Parts: Key, Identity, Vitals, Equipment, Weapons, Statistics, Pose; JS uses camelCase. Unchanged parts retain references. |
HudPlayerPartsV3? Player(string key) | player(key): HudPlayerPartsV3 | null | Unknown keys return null. |
ObservedPlayer; CurrentPlayer | roster.observed; roster.current; player(key) | C# resolves confirmed roles. JS resolves the same roster key using player. Do not assign roles from Steam ID alone. |
IEnumerable<HudPlayerPartsV3> FindPlayersBySteamId(string steamId) | findPlayerBySteamId(steamId): HudPlayerPartsV3 | null | C# returns all matches: the same Steam ID may appear in multiple source blocks. JS returns the first match/null; filter players.values() yourself for all matches. |
RadarPlayer? GetRadarPlayer(string key); GrenadeState? GetGrenade(string key); KillFeedEntry? GetKill(string key) | getRadarPlayer(key); getGrenade(key); getKill(key) | Typed lookup values/null; use the radar/order object for order. |
IReadOnlyDictionary<(HudObjectKindV3 Kind,string Key),HudObjectV3> Objects; GetObject(kind,key = "main") | objects: ReadonlyArray<HudObjectV3>; getObject(kind,key) | Raw objects without aggregate projection. Unknown: C# null, JS undefined. JS requires an explicit key. |
IsCompatibilitySnapshotCreated; LiveSnapshot ToSnapshot() | snapshotMaterialized; toSnapshot() | Detects/creates the lazy aggregate projection. A retained graph preserves historical state even after later updates. |
Blazor: services, host and regions
Call SouHudServices.AddSouHud<TOptions>(IServiceCollection services, JsonTypeInfo<TOptions> optionsType, Action<SouHudSettings>? configure = null) once as an extension method; it returns the same IServiceCollection. Example: builder.Services.AddSouHud<ReferenceOptions>(ReferenceJson.Default.ReferenceOptions) with the types above. Scoped registrations: HudClient, HudSession<TOptions>, HudClock, HudRenderClock, HudPerformanceMetrics, HudBrowserRuntime<TOptions>. SouHudSettings is singleton. An existing HttpClient is preserved; otherwise a scoped one uses the browser base address.
| C# / Blazor | JavaScript | Contract |
|---|---|---|
SouHudSettings.Diagnostics: bool? = null | — | For example settings => settings.Diagnostics = false. Null follows diagnostics=1 in the document URL. |
SouHudHost<TOptions>: ChildContent; DisposeAsync() | — | One root host. Cascading values: Session, Clock, Metrics. Starts after first render. ChildContent appears only with a ready server clock; still handle session readiness separately. Option faults appear as an alert. Dispose ends the owned session/browser runtime. |
SouHudRegion<TOptions,TValue>: Select; Comparer = null; ChildContent; Session; Dispose() | — | Select is required; Comparer optional. ChildContent receives the selection. Session comes from the host. Parameter changes replace the subscription; Dispose removes it. Only changed selections request rendering. |
HudRenderBoundary: RenderKey = null; Now; ClockMilliseconds = 0; ChildContent; Clock; Metrics; Dispose() | — | An immutable RenderKey controls data rendering. Now is an explicit anchor, initially default. 0 disables clock subscription; positive intervals are at least 16 ms. ChildContent receives DateTimeOffset. Clock/Metrics are optional cascading values. A time change alone without a clock subscription/key does not request rendering. |
Complete Razor component using ReferenceOptions from the C# example. The registration above belongs in WebAssembly startup. No global parent timer is required.
@using SOU.Hud.Sdk.Blazor
<SouHudHost TOptions="ReferenceOptions">
<SouHudRegion TOptions="ReferenceOptions" TValue="string"
Select='@(state => state.Game?.Info?.Map ?? "Waiting for game data")'
Context="map">
<p>@map</p>
</SouHudRegion>
</SouHudHost>
Server clock and countdown
| C# / Blazor | JavaScript | Contract |
|---|---|---|
HudRenderClock.IsReady; DateTimeOffset Now | hudClock.isReady(): boolean; hudClock.now(): number | Check readiness before reading time, otherwise it throws. C# returns server UTC; JS Unix milliseconds. |
IDisposable HudRenderClock.Subscribe(int milliseconds, Action<DateTimeOffset> callback, DateTimeOffset? anchor = null) | hudClock.subscribe(listener): Unsubscribe | C#: at least 16 ms, no immediate tick. One shared timer only while subscribed; anchor is the last tick, otherwise ready time/no anchor. JS emits state events {ready,reason} only, not periodic ticks or an initial replay. |
event Action<DateTimeOffset> HudRenderClock.Tick; event Action<Exception> SubscriberFailed | — | Tick follows subscribers during Advance; the event alone starts no timer. Callback failures are isolated. |
void HudRenderClock.Advance(DateTimeOffset now); ValueTask DisposeAsync() | — | Advance uses the supplied instant for custom deterministic integrations/tests. Normally let the host supply time. Dispose removes timer/subscriptions. |
double? HudCountdown.Remaining(double? seconds, DateTimeOffset? receivedAt, DateTimeOffset now, bool paused = false) | countdownRemaining(seconds,receivedAt,now,paused = false): number | null | Seconds; result at least 0. Invalid/missing duration returns null. Pause preserves duration even without a source anchor. Otherwise an anchor is required; negative elapsed time is clamped to 0. JS receivedAt: ISO string, Unix milliseconds or Date; now: Unix milliseconds or Date. |
A clock state change is not an animation interval. JavaScript chooses rendering cadence for the affected elements and stops its timer during teardown. Do not replace Date or restamp source timestamps. hudClock uses the synchronized host clock without requests or its own timer.
C# clock for custom host integrations
Normal Blazor HUDs use the registered clock. These public methods on SOU.Live.Contracts.HudClock support custom host adapters; do not start additional clock requests alongside the host.
| C# / Blazor | JavaScript | Contract |
|---|---|---|
new HudClock(TimeProvider? timeProvider = null); IsSynchronized; UtcNow | — | Defaults to TimeProvider.System. The server anchor advances with monotonic elapsed time; UtcNow throws without a valid anchor. |
MaximumRoundTrip = 2 seconds; MaximumSampleAge = 90 seconds; TryGetUtcNow(out DateTimeOffset now) | — | Rejects slow samples and expires old anchors. The Try variant returns false instead of inventing time. |
long GetTimestamp(); TimeSpan GetElapsedTime(long startingTimestamp); HudClockRequest BeginSynchronization() | — | Monotonic timestamps and an ordered request with Timestamp, Generation and Sequence. |
bool TrySynchronize(HudClockSample sample, long requestTimestamp, long responseTimestamp); bool TrySynchronize(HudClockSample sample, HudClockRequest request, long responseTimestamp) | — | HudClockSample contains ServerTime. Half the round trip estimates the return path. Rejected samples do not replace a valid anchor. The request variant also guards obsolete generations/sequences. |
void Invalidate(); Task<bool> HudClockSynchronizer.SynchronizeAsync(HudClock clock, HttpClient http, Uri clockAddress, CancellationToken token = default) | — | Invalidate expires the anchor. The HTTP helper serializes per clock, uses cookie context/no-cache/no-store and a 3s timeout. Transport failures throw; rejected samples return false. |
Fault categories and reconnection
| C# / Blazor | JavaScript | Contract |
|---|---|---|
HudSessionFault(HudSessionFaultKind Kind, Exception Exception); Message | HudSessionError(kind,message,cause?); kind; cause; message | C#: Transport, Protocol, Options. JS: transport, protocol, options, subscriber. C# Message is Exception.Message; JS retains the cause. |
Transport / Protocol | transport / protocol | Domain reset and reconnect with a new baseline. No silent v2 fallback. The unified session uses WebSockets directly without negotiation/polling fallback. Retry delays 1, 2, 5, at most 10 seconds. |
Options | options | Terminal local type/parser/validation fault. Correct it and create a new instance. |
SubscriberFailed | subscriber | Selector/callback/optional diagnostics failed; other receivers remain active. JS onError does not automatically imply current.error or a stopped session. |
Optional local diagnostics
Diagnostics are off by default. Enable through ?diagnostics=1 or explicit settings; false disables them even with the URL opt-in. No extra live connection or telemetry. Measurements cover visible time; hidden intervals and intervals crossing visibility/reset boundaries are excluded. rAF intervals are not GPU frames; DOM queue time is not isolated CPU time. Long Tasks require browser support.
| C# / Blazor | JavaScript | Contract |
|---|---|---|
HudClient.ProcessingMeasured; HudSession<TOptions>.ProcessingMeasured: Action<HudProcessingMeasurement>? | — | Null by default. Synchronous v3 measurement callback; failures do not interrupt processing. HudProcessingStage: Deserialize/Reducer. Record fields Stage,Milliseconds,Kind,SessionId,RoundNumber,Phase. Excludes transport waiting/preceding SignalR binding/rendering. |
HudPerformanceMetrics.Enabled | session.diagnostics.enabled; ready: Promise<void> | Blazor additionally measures WASM/DOM stages. JS ready waits for the optional module, not game readiness. Already resolved before startup; activation occurs after session.start(). |
HudPerformanceSnapshot Snapshot(); void Reset() | session.diagnostics.read(): HudBrowserDiagnosticReport | null; reset(): void | JS read returns null while disabled/not ready. Reset clears bounded current measurements. The Blazor host provides local display and JSON export. |
new HudPerformanceMetrics(bool enabled, Func<double>? elapsedMilliseconds = null) | — | Custom aggregator in SOU.Hud.Sdk.Diagnostics; defaults to Stopwatch. MaxSegments=32, MaxSeconds=120. Disabled mode collects nothing. |
long Start(); void Record(string stage,long started); void RecordElapsed(string stage,double milliseconds) | — | Start=0 while disabled/hidden. Pair Record with Start. RecordElapsed requires finite/nonnegative values and a known stage. Stages: deserialize,reducer,presentation,app-render-queue,boundary-render-queue,clock-render-queue. |
SetVisibility(bool isVisible); SetContext(Guid sessionId,int? roundNumber,string? countdownPhase); ContextSnapshot(); event Action ContextChanged | — | Custom adapters set visibility and confirmed context. The standard host owns this. ContextChanged fires on context change/reset. |
HudPerformanceDiagnostics: Metrics; Task ResetMeasurements(); Task SetMeasurementVisibility(bool visible); ValueTask DisposeAsync() | — | Metrics parameter required. The host adds this display only in diagnostics mode. Public tasks are browser callbacks, not another data API. Disposal removes the module and publications. |
C# diagnostics types: HudPerformanceSnapshot with Schema,Enabled,Visible,VisibleMs,ResetOrdinal,ElapsedMs,SessionOrdinal,Segment,Round,Phase,TimingSemantics,QuantileSemantics,HistogramBoundsMs,Stages,Segments,Seconds. HudPerformanceStage: Stage,Count,MeanMs,P50Ms,P95Ms,P99Ms,MaxMs,Over33Ms,Over50Ms,Over100Ms,Buckets. HudPerformanceSegment: Segment,SessionOrdinal,Round,Phase,StartMs,Stages. HudPerformanceSecond: StartMs,SessionOrdinal,Segment,Round,Phase,Stages. HudPerformanceSecondStage: Stage,Count,TotalMs,MaxMs. HudPerformanceContext: ResetOrdinal,SessionOrdinal,Segment,Round,Phase,Visible. Quantiles are histogram upper bounds. HudPerformanceJson supplies generated Snapshot/Context metadata.
JavaScript HudBrowserDiagnosticReport: schema,runtime,sampledAtPerformanceMs,semantics,browser. Browser part: elapsedMs,visibleMs,visible,visibilityChanges,rafSupported,longTaskSupported,context,frames,longTasks,histogramBoundsMs,segments,seconds. HudTimingSummary: count,meanMs,maxMs,p50Ms,p95Ms,p99Ms,over33Ms,over50Ms,over100Ms,optional buckets. HudBrowserDiagnosticContext: sessionOrdinal,segment,round,phase. JS measures neither SignalR JSON parsing nor WASM/DOM completion. Disabled/unsupported does not prove zero load.
Optional adapters for existing presentation models
New displays select v3 graph parts directly. Existing GameState components can use the public C# adapters in SOU.Hud.Sdk.Presentation and Blazor. They never change the authoritative graph.
| C# / Blazor | JavaScript | Contract |
|---|---|---|
HudProjectionOptions: IncludeRadar, IncludeGrenades, IncludePlayerPose, IncludeBombPosition, IncludeProviderTimestamp, IncludeSourceBlocks | — | Every init option defaults to true. Default retains all fields; WithoutMotion sets all six to false and omits their presentation parts. |
new HudGameProjection(HudProjectionOptions? options = null); GameState? ForDisplay(GameState? incoming); ManagedMatch? Match(ManagedMatch? incoming); void Reset() | — | A projection per consumer with structural cache. Null remains null; equivalent fields can retain previous references. Immutable inputs required. Reset clears only the cache. |
new HudUpdateState(HudGameProjection projection); bool Accept(LiveSnapshot next); Snapshot; DisplayGame; Match | — | Updates aggregate state/projection and reports a material presentation/metadata change. No timer or networking. |
SouHudSnapshotView<TOptions>: Projection = new(); CoalesceMilliseconds = 0; ChildContent; Session; Clock; Metrics; DisposeAsync() | — | Under the host. Window 0..100 ms; 0 means no delay. Only equivalent presentation requests are combined; material data/panel/options/reset transitions remain immediate. Disposal removes event subscription/scheduled rendering. |
HudSnapshotPresentation<TOptions>(LiveSnapshot? Snapshot,GameState? DisplayGame,ManagedMatch? Match,TOptions? Options,HudPanelState? Panel,DateTimeOffset Now) | — | ChildContent value; Panel is a defensive HudPanelState copy rather than HudPanelView. |
Public browser runtime for custom Blazor hosts
HudBrowserRuntime<TOptions>(session, js, clock, renderClock, metrics) coordinates the host bridge. bool ClockReady, string? Error and event Action? Changed report state. Task StartAsync() initializes idempotently; ValueTask DisposeAsync() ends the session, render clock, clock refresh and browser references. Task ClockChanged(BrowserSample sample) is a browser callback; BrowserSample(int Handle, bool Ready, double UnixMilliseconds) is its data type. Normal HUDs do not invoke this additionally: SouHudHost owns the runtime.
The manager view arrives as View through the same session. The Blazor runtime and JS watchSessionGraphV3 apply it through the host bridge. No second connection. souHudManager.applyView(view) is available to custom adapters. HudViewStateV3: InstanceId,Revision,Route; revision 0 means no explicit selection. Views must be declared in the package.
Low-level client for custom integrations
These methods remain public but are handled by the high-level session. Use only for custom integrations owning state/teardown logic, never alongside the corresponding HudSession. Each enumeration/watch call owns a separate stream. Use a package address from the host or local SDK preview.
| C# / Blazor | JavaScript | Contract |
|---|---|---|
new HudClient(HttpClient http, Uri packageAddress); Identity | new HudClient(address = location.href); identity | Address contains /hud-packages/{id}/{version}/. C# HudIdentity(string Id,string Version), FromUri(Uri address) read identity. JS identity is frozen; address is the client URL. Keep HttpClient alive. |
Uri Media(string relative) | media(path): string | Local /cast-media/ paths with one file segment only. No .., backslashes, query, fragment or percent encoding. Returns the package origin. |
IAsyncEnumerable<HudSessionUpdateV3> WatchSessionV3Async(CancellationToken token = default) | watchSessionV3(receive,options = {}): HudWatchCancellation | Unified v3 stream for game, panel, view and options. C# has no custom option parser in this overload and materializes the aggregate projection. JS snapshot stays lazy. |
— | watchSessionGraphV3(receive,{ signal,status,error,prepareOptions } = {}): HudWatchCancellation | Same JS stream with optional synchronous option preparation before commit. Used by the high-level session. C# graph watch is not public. |
IAsyncEnumerable<HudV3Update> WatchV3Async(CancellationToken token = default) | watchV3(receive,options = {}): HudWatchCancellation | Game-only v3 data with its own baseline/deltas. No unified panel/options/manager runtime. |
event Action<string,HudConnectionStatus> ConnectionChanged; event Action<string,Exception> ConnectionFailed | WatchOptions.status(channel,status); WatchOptions.error(error) | Channels live,panel,session. JS defaults to a no-op status callback and console.error for errors. Custom C# client event handlers must not throw. |
CancellationToken / await foreach | HudWatchCancellation(): void; completion: Promise<void> | JS returns a cancellation function with completion. Cancel then await completion. WatchOptions.signal ends the same stream. C# uses await-foreach with a token. |
WatchAsync(token = default); WatchPanelAsync(token = default) | watch(receive,options = {}); watchPanel(receive,options = {}) | Existing separate older live/panel methods remain compatible. Not for new v3 HUDs or alongside the unified session. C# returns IAsyncEnumerable<LiveSnapshot> or IAsyncEnumerable<HudPanelMessage>; JS a watch cancellation handle. |
HudClient.stream(channel,path,args,receive,options,decode,reset,method) is a JavaScript implementation helper, not an entry point offered by the public TypeScript declaration. Use typed watch methods for their protocol/validation selection. HudSdkJson provides generated C# metadata for LiveSnapshot, HudMessageV3, HudSessionMessageV3, JsonElement, HudPanelMessage and String. Custom options still need your generated context.
Low-level updates and reducers
| C# / Blazor | JavaScript | Contract |
|---|---|---|
HudV3Update(Message,Snapshot); IsSnapshot; Objects; Changed(HudObjectKindV3 kind,string? key = null) | HudV3Update: message; snapshot; isSnapshot; objects; changed(kind,key = null) | Accepted game batch with aggregate projection. Changed tests this message object list; without a key the kind is sufficient. |
HudSessionUpdateV3(Message,Snapshot,Panel,View,Options); IsSnapshot; GameUpdated; PanelUpdated; ViewUpdated; OptionsUpdated; GameUpdate; PanelMessage | HudSessionUpdateV3: message; game; snapshot; panel; view; options; preparedOptions; isSnapshot; gameUpdated; panelUpdated; viewUpdated; optionsUpdated; gameUpdate; panelMessage | Baseline marks all domains updated; afterwards only the message domain. PanelUpdated can mean an action without replacement panel state. GameUpdate/PanelMessage may be absent. JS preparedOptions is the preparation result; options is frozen raw JSON. |
new HudV3State(); Objects; Reset(); LiveSnapshot ToSnapshot(); HudV3Graph ToGraph(); LiveSnapshot Apply(HudMessageV3 message); HudV3Graph ApplyGraph(HudMessageV3 message) | new HudV3State(); objects; message; graph; getObject(kind,key); reset(); toSnapshot(); apply(message); applyGraph(message) | One reducer per stream. Complete batch/graph validation before commit. Aggregate projection throws without baseline; JS graph/message are initially null. Reset discards baseline. C# lookup uses Objects. |
new HudSessionStateV3(string id,string version); Message; Graph; Snapshot; Panel; View; Options; Reset(); Apply(message); Apply(message,Action<JsonElement>? prepareOptions) | new HudSessionStateV3(id,version); current; reset(); apply(message); applyGraph(message,prepareOptions = value => value) | Package-bound atomic reducer. Domain getters throw without baseline; C# Message is initially null. Initial revision 1 with all four domains, then exact base/revision+1 with one domain. Option preparation before commit. |
HudV3SyncException(string message) | HudV3SyncError extends Error | Invalid headers, revisions, replacements/references are rejected. Do not skip arbitrary deltas; require a new baseline. |
HudV3Protocol contains SchemaVersion=3, HubPath=live/v3, BrowserHubPath=/hud-live/v3, SnapshotPath=state/v3, WatchMethod=Watch, SessionWatchMethod=WatchHud, MaximumSafeInteger=9007199254740991. Constants describe the contract; normal HUDs do not build extra connections from them. HudSessionSnapshot is high-level state, HudSessionUpdateV3 a low-level item, and HudSessionMessageV3 a wire message.
Public C# helpers for protocol adapters
For custom adapters, SOU.Live.Contracts also provides HudV3Projection.Project(LiveSnapshot snapshot) for a complete object projection, HudV3Projection.KillKey(KillFeedEntry kill) for the canonical kill key, HudV3Diff.Create(LiveSnapshot? previous,LiveSnapshot next) for batch comparison, and HudV3Diff.Equal(HudObjectV3 a,HudObjectV3 b) for content comparison. HudV3Encoder.Encode(LiveSnapshot snapshot) produces a snapshot/following batch on one serially used instance per receiver; Reset() discards its baseline. Failed encoding preserves the previous baseline. A normal HUD display does not additionally need these helpers.
JavaScript declarations: DeepReadonly<T> for transitive read-only access, Unsubscribe for deregistration, WatchOptions for signal/status/error, HudObserveOptions<T> for equals/emitCurrent, HudSessionOptions<TOptions> for option preparation/cancellation/diagnostics, HudClockState for ready/reason and HudSessionDiagnostics for enabled/ready/read/reset. Wire fields and enums are in the type reference; the development guide explains your first working output.