2.0.0: Rudder.Core 2.0.0 DLL, samples and skill docs on slugs and environments
CI / check (push) Successful in 2s
CI / publish (push) Has been skipped

Claude-Session: https://claude.ai/code/session_01SMCvdwDmuxoaqGgvGBLk1V
This commit is contained in:
edmand46
2026-09-06 22:25:33 +03:00
parent 01e27bde2c
commit b18c59450c
11 changed files with 73 additions and 29 deletions
@@ -26,6 +26,18 @@ Auth service:
Login uses the SDK device id from PlayerPrefs. Do not pass
`SystemInfo.deviceUniqueIdentifier`.
## Environments
A project has two environments, `staging` and `prod`. The SDK key in
`RudderConfiguration` belongs to one of them, so the environment is decided at
login and travels inside the tokens; no API takes an environment argument, and
a player created against a staging key does not exist in prod. Point a QA build
at the staging key and the live build at the prod key.
Tokens stored by SDK 1.x carry no environment claim and are rejected with 401.
The refresh then fails, PlayerPrefs tokens are cleared and `AuthStateChanged`
fires `SignedOut` — call `LoginWithDeviceAsync` again.
## Profile and wallets
```csharp
@@ -4,7 +4,7 @@
`Rudder.Core.dll`. There is no sample scene for this feature.
Progress is tied to a scenario battle-pass node. Prefer `BattlePassEffect`
delivered by `Effects.OnBattlePass` — it binds `scenarioId`/`nodeId`/
delivered by `Effects.OnBattlePass` — it binds `scenarioSlug`/`nodeId`/
`runId` for you. Direct `BattlePassService` calls need those ids manually.
## BattlePassEffect (preferred)
@@ -24,20 +24,20 @@ client.Effects.OnBattlePass += async effect =>
- `Task<PurchaseBattlePassPremiumResponse> PurchasePremiumAsync(CancellationToken = default)` — posts `onPremiumPurchase` on success
- `LevelUp()` / `LevelUpAsync()` — posts `onLevelUp`
- `End()` / `EndAsync()` — posts `onComplete`
- `Get<T>(key, fallback)`, `RunId`, `ScenarioId`, `NodeId`, `Data` like other effects
- `Get<T>(key, fallback)`, `RunId`, `ScenarioSlug`, `NodeId`, `Data` like other effects
`BattlePassLevelEffect` (from `OnBattlePassLevel`) has `Claim()` /
`ClaimAsync()` (posts `onComplete`) and a `Level` property.
## BattlePassService (direct)
- `Task<GetBattlePassProgressResponse> GetProgressAsync(string scenarioId, string nodeId, CancellationToken = default)`
- `Task<GetBattlePassProgressResponse> GetProgressAsync(string scenarioSlug, string nodeId, CancellationToken = default)`
- `Task<AddBattlePassXpResponse> AddXpAsync(AddBattlePassXpRequest request, CancellationToken = default)`
- `Task<ClaimBattlePassRewardResponse> ClaimRewardAsync(ClaimBattlePassRewardRequest request, CancellationToken = default)`
- `Task<PurchaseBattlePassPremiumResponse> PurchasePremiumAsync(PurchaseBattlePassPremiumRequest request, CancellationToken = default)`
- Track constants: `BattlePassService.TrackFree`, `BattlePassService.TrackPremium` (static fields).
Request models carry `ScenarioId`, `NodeId`, and (except progress)
Request models carry `ScenarioSlug`, `NodeId`, and (except progress)
`RunId`; `PurchaseBattlePassPremiumRequest` also takes `IdempotencyKey`.
## Response models
+6 -6
View File
@@ -7,19 +7,19 @@ from scenario `OnQuest` nodes (`QuestEffect`, see
```csharp
var quests = await client.Quests.ListAsync();
var claim = await client.Quests.ClaimAsync(quest.Id);
var completedIds = await client.Quests.ReportProgressAsync("kills", 1);
var claim = await client.Quests.ClaimAsync(quest.Slug);
var completedSlugs = await client.Quests.ReportProgressAsync("kills", 1);
```
API:
- `Task<IReadOnlyList<Quest>> ListAsync(CancellationToken = default)`
- `Task<ClaimQuestResponse> ClaimAsync(string questId, CancellationToken = default)`
- `Task<IReadOnlyList<string>> ReportProgressAsync(string metric, long amount, CancellationToken = default)` — returns the ids of quests completed by this report.
- `Task<ClaimQuestResponse> ClaimAsync(string questSlug, CancellationToken = default)`
- `Task<IReadOnlyList<string>> ReportProgressAsync(string metric, long amount, CancellationToken = default)` — returns the slugs of quests completed by this report.
Models:
- `Quest``Id`, `Name`, `Status`, `Objectives`
- `Quest``Slug`, `Name`, `Status`, `Objectives`
(`List<QuestObjectiveProgress>`), `Rewards` (`List<Reward>`).
- `QuestObjectiveProgress``ObjectiveId`, `Metric`, `Current` (`long?`),
`Target` (`long?`), `Completed` (`bool?`).
@@ -28,6 +28,6 @@ Models:
`Granted` (`List<Reward>`), `Error`.
Store purchases already report metrics automatically; the metric strings are
built by `QuestMetrics.PurchaseOffer(offerId)``purchase.offer:<offerId>`
built by `QuestMetrics.PurchaseOffer(offerSlug)``purchase.offer:<offerSlug>`
and `QuestMetrics.PurchaseItem(itemId)``purchase.item:<itemId>`
(`RudderSdk.Core.QuestMetrics`, static).
@@ -27,7 +27,7 @@ are dispatched the same way.
## Events and effects
All effect types expose `RunId`, `ScenarioId`, `NodeId`, `Data` (`JObject`),
All effect types expose `RunId`, `ScenarioSlug`, `NodeId`, `Data` (`JObject`),
and `T Get<T>(string key, T defaultValue = default)` for node data.
| Event | Effect | Game must |
@@ -39,8 +39,8 @@ and `T Get<T>(string key, T defaultValue = default)` for node data.
| `OnLeaderboard` | `LeaderboardEffect` | `End()` / `EndAsync()`, `Claim()` / `ClaimAsync()`. `IsResolved` marks resolution. |
| `OnBattlePass` | `BattlePassEffect` | Drive the effect — see [battlepass.md](battlepass.md). |
| `OnBattlePassLevel` | `BattlePassLevelEffect` | `Claim()` / `ClaimAsync()` for the level reward; `Level` prop. |
| `OnScenarioCompleted` | `ScenarioCompletedEffect` | `RunId`, `ScenarioId`. Log / surface. |
| `OnScenarioFailed` | `ScenarioFailedEffect` | `RunId`, `ScenarioId`, `NodeId`, `Exception`. Log / surface. |
| `OnScenarioCompleted` | `ScenarioCompletedEffect` | `RunId`, `ScenarioSlug`. Log / surface. |
| `OnScenarioFailed` | `ScenarioFailedEffect` | `RunId`, `ScenarioSlug`, `NodeId`, `Exception`. Log / surface. |
Async and sync variants are equivalent; the sync variants fire-and-forget
the same server callback. The event name must match a scenario trigger
@@ -7,21 +7,21 @@ Verified against the bundled `Rudder.Core.dll` and
```csharp
var stores = await client.Stores.ListAsync();
var response = await client.Stores.PurchaseAsync(storeSlug, offerId);
var response = await client.Stores.PurchaseAsync(storeSlug, offerSlug);
if (response != null && response.Success != true)
throw new InvalidOperationException(response.Error ?? "Purchase rejected");
```
- `Task<IReadOnlyList<Store>> ListAsync(CancellationToken = default)`
- `Task<Store> GetAsync(string slug, CancellationToken = default)`
- `Task<PurchaseOfferResponse> PurchaseAsync(string storeSlug, string offerId, string idempotencyKey = null, CancellationToken = default)` — generates an idempotency key when omitted.
- `Task<PurchaseOfferResponse> PurchaseAsync(string storeSlug, string offerSlug, string idempotencyKey = null, CancellationToken = default)` — generates an idempotency key when omitted.
Models:
- `Store``Id`, `Slug`, `Name`, `Description`, `Status`, `Environment`,
`ProjectId`, `ScenarioId`, `Data` (`JToken`), `Offers` (`List<Offer>`),
`CreatedAt`, `UpdatedAt`.
- `Offer``Id`, `Name`, `Price` (`OfferPrice`), `Contents`
- `Offer``Id`, `Slug`, `Name`, `Price` (`OfferPrice`), `Contents`
(`List<OfferContent>`), `MaxPurchases` (`int?`), `CreatedAt`, `UpdatedAt`.
- `OfferPrice``Currency`, `Amount` (`long?`).
- `OfferContent``ItemId`, `Amount` (`long?`).
@@ -30,7 +30,7 @@ Models:
Check `Success != true` (nullable bool), not `!Success`. After a purchase,
reload wallets (`Player.GetProfileAsync`) and inventory — there is no
`onChange` callback. Purchases auto-report quest metrics
`purchase.offer:<offerId>` / `purchase.item:<itemId>` (see
`purchase.offer:<offerSlug>` / `purchase.item:<itemId>` (see
[quests.md](quests.md)). The store slug must exist in the dashboard, else 404.
## Inventory — `client.Inventory` (`InventoryService`)