# ISkeletonClient

<Badge tone="type">interface</Badge> <Badge>thread-safe</Badge>

```csharp
namespace AR51.Net;

public interface ISkeletonClient
```

Live skeletons of the people the AR 51 system tracks. Use it through [`IAR51Client.Skeletons`](/docs/sdk-api/csharp-api/classes/iar51client/), or on its own as a [`SkeletonClient`](/docs/sdk-api/csharp-api/classes/skeletonclient/) with the skeleton service's address.

Two ways to receive skeletons: call [`StartAsync`](#startasync) and handle `SkeletonReceived`, or read [`StreamAsync`](#streamasync) with `await foreach`. All members are safe to use from any thread.

:::note[Units]
Positions are in **meters**, in the AR 51 world: **left-handed, X right, Y up, Z forward** (the same axes as Unity). See [Data conventions](/docs/csharp-api/conventions/).
:::

:::tip[How-to]
This page is **reference** (facts only). For a walkthrough with a complete sample, see the [Skeletons guide](/docs/csharp-api/skeletons/).
:::

## Properties

<div className="apiPropTable apiTable">

| Property | Type | Access | Description |
|---|---|---|---|
| `IsAvailable` | `bool` | `get` | `true` when the skeleton service's address is known (always, for a standalone [`SkeletonClient`](/docs/sdk-api/csharp-api/classes/skeletonclient/)). |

</div>

## Method summary

<div className="apiTable">

| Method | Returns | |
|---|---|---|
| [`StartAsync`](#startasync) | [`Task`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task) |  |
| [`StopAsync`](#stopasync) | [`Task`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task) |  |
| [`StreamAsync`](#streamasync) | [`IAsyncEnumerable<Skeleton>`](/docs/sdk-api/csharp-api/classes/skeleton/) |  |

</div>

→ Full descriptions in [Method details](#method-details) below.

## Events

Standard .NET events (`EventHandler<T>`): subscribe with `+=`, unsubscribe with `-=`. They are raised on a background thread.

<div className="apiTable">

| Event | Args | Raised when |
|---|---|---|
| `SkeletonReceived` | [`SkeletonEventArgs`](#skeletoneventargs) | Raised for every skeleton received while started, on a background thread. Keep handlers short: they run on the receiving thread. An exception thrown by a handler is logged and does not stop the others or the stream. |

</div>

<Example>

```csharp
ar51.Skeletons.SkeletonReceived += (_, e) =>
{
    // Background thread: copy what you need and return quickly.
    Vector3 nose = e.Skeleton.GetPosition(Joint.Nose);
    Console.WriteLine($"{e.Skeleton.Id}: nose at {nose} m");
};
```

</Example>

## Method details

### StartAsync

<ApiBody kind="method">

```csharp
Task StartAsync(CancellationToken cancellationToken = default);
```

Starts receiving skeletons into `SkeletonReceived`. Returns at once; calling it again does nothing.

<Params>
<Param name="cancellationToken" type="CancellationToken" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken">Ignored. Optional.</Param>
</Params>

<Returns type="Task" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task">A completed task.</Returns>

<Throws>
<Throw type="ObjectDisposedException" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception">The client was disposed.</Throw>
</Throws>

:::note
Receiving begins as soon as the skeleton service is reachable, and resumes by itself after the service restarts or the network drops, until [`StopAsync`](#stopasync) or dispose.
:::

</ApiBody>

### StopAsync

<ApiBody kind="method">

```csharp
Task StopAsync(CancellationToken cancellationToken = default);
```

Stops receiving skeletons: stops `SkeletonReceived`, ends every [`StreamAsync`](#streamasync) loop running now, and waits until the event stream is closed. Calling it again does nothing.

<Params>
<Param name="cancellationToken" type="CancellationToken" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken">Stops waiting for the stream to close; the stream still closes. Optional.</Param>
</Params>

<Returns type="Task" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task">A task that completes when the event stream is closed.</Returns>

:::note
[`StreamAsync`](#streamasync) loops end normally, without an exception, after the skeleton they are handling. Loops started after this call are not affected.
:::

</ApiBody>

### StreamAsync

<ApiBody kind="method">

```csharp
IAsyncEnumerable<Skeleton> StreamAsync(CancellationToken cancellationToken = default);
```

Streams skeletons until [`StopAsync`](#stopasync) is called (the loop ends normally), `cancellationToken` is cancelled, or the connection drops. Works without [`StartAsync`](#startasync).

<Params>
<Param name="cancellationToken" type="CancellationToken" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken">Stops the stream. Optional.</Param>
</Params>

<Returns type="IAsyncEnumerable<Skeleton>" typeHref="/docs/sdk-api/csharp-api/classes/skeleton/">Skeletons in the order they arrive.</Returns>

<Throws>
<Throw type="AR51Exception" typeHref="/docs/sdk-api/csharp-api/classes/ar51exception/">The skeleton service is not available, or the stream failed.</Throw>
<Throw type="OperationCanceledException" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.operationcanceledexception">`cancellationToken` was cancelled.</Throw>
<Throw type="ObjectDisposedException" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception">The client was disposed.</Throw>
</Throws>

:::note
Each call opens its own stream, which closes when the loop ends. Unlike `SkeletonReceived`, it does not reconnect: start a new loop after an [`AR51Exception`](/docs/sdk-api/csharp-api/classes/ar51exception/).
:::

<Example>

```csharp
using var stop = new CancellationTokenSource(TimeSpan.FromSeconds(10));
try
{
    await foreach (Skeleton skeleton in ar51.Skeletons.StreamAsync(stop.Token))
        Console.WriteLine($"{skeleton.Id} at {skeleton.GetPosition(Joint.MidHip)} m");
}
catch (OperationCanceledException)
{
    // ten seconds are up
}
```

</Example>

</ApiBody>

## Related types

Small types used by this interface, documented here.

### SkeletonEventArgs

<Badge tone="type">class · sealed</Badge>

```csharp
public sealed class SkeletonEventArgs : EventArgs
```

Data for `ISkeletonClient.SkeletonReceived`.

<div className="apiPropTable apiTable">

| Property | Type | Access | Description |
|---|---|---|---|
| `Skeleton` | [`Skeleton`](/docs/sdk-api/csharp-api/classes/skeleton/) | `get` | The received skeleton. |

</div>

<details>
<summary>Constructor</summary>

```csharp
public SkeletonEventArgs(Skeleton skeleton);
```

Creates the event data.

</details>

## See also

- [Skeletons guide](/docs/csharp-api/skeletons/) — walkthrough with a complete, compiled sample
- [Class index](/docs/sdk-api/csharp-api/classes/) — every AR51.Net type
- [Data conventions, threading and errors](/docs/csharp-api/conventions/)
