From b18c59450c30e71f655d56eb5278702487622a20 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Sun, 6 Sep 2026 22:25:33 +0300 Subject: [PATCH] 2.0.0: Rudder.Core 2.0.0 DLL, samples and skill docs on slugs and environments Claude-Session: https://claude.ai/code/session_01SMCvdwDmuxoaqGgvGBLk1V --- Packages/rudder.sdk/AGENTS.md | 6 ++-- Packages/rudder.sdk/CHANGELOG.md | 32 ++++++++++++++++++ .../Runtime/Plugins/Rudder.Core.dll | Bin 97792 -> 97792 bytes .../Samples~/Scenarios/ScenariosSample.cs | 4 +-- .../StoreInventory/StoreInventorySample.cs | 12 +++---- Packages/rudder.sdk/package.json | 2 +- .../reference/authentication.md | 12 +++++++ .../rudder-unity-sdk/reference/battlepass.md | 8 ++--- skills/rudder-unity-sdk/reference/quests.md | 12 +++---- .../rudder-unity-sdk/reference/scenarios.md | 6 ++-- .../reference/stores-inventory.md | 8 ++--- 11 files changed, 73 insertions(+), 29 deletions(-) diff --git a/Packages/rudder.sdk/AGENTS.md b/Packages/rudder.sdk/AGENTS.md index 310cdf0..56d2e55 100644 --- a/Packages/rudder.sdk/AGENTS.md +++ b/Packages/rudder.sdk/AGENTS.md @@ -113,7 +113,7 @@ save format in a wrapper type. ```csharp var stores = await client.Stores.ListAsync(); -var result = await client.Stores.PurchaseAsync(storeSlug, offerId); +var result = await client.Stores.PurchaseAsync(storeSlug, offerSlug); if (result != null && result.Success != true) { /* result.Error */ } var items = await client.Inventory.GetAsync(); ``` @@ -165,7 +165,7 @@ effects through the `On*` events. ```csharp var quests = await client.Quests.ListAsync(); -await client.Quests.ClaimAsync(quest.Id); +await client.Quests.ClaimAsync(quest.Slug); var completedIds = await client.Quests.ReportProgressAsync("kills", 1); ``` @@ -175,7 +175,7 @@ Store purchases already report `purchase.offer:` / `purchase.item:`. Progress is tied to a scenario battle-pass node. Prefer `BattlePassEffect` from `Effects.OnBattlePass` (it binds scenario/node/run ids). Direct -`BattlePass.GetProgressAsync(scenarioId, nodeId)` needs those ids. Tracks: +`BattlePass.GetProgressAsync(scenarioSlug, nodeId)` needs those ids. Tracks: `BattlePassService.TrackFree` / `TrackPremium`. ## Errors diff --git a/Packages/rudder.sdk/CHANGELOG.md b/Packages/rudder.sdk/CHANGELOG.md index cfc1f0a..0443af1 100644 --- a/Packages/rudder.sdk/CHANGELOG.md +++ b/Packages/rudder.sdk/CHANGELOG.md @@ -1,5 +1,37 @@ # Changelog +## 2.0.0 + +Breaking changes, following Rudder.Core 2.0.0 and the backend environments +release. The bundled `Runtime/Plugins/Rudder.Core.dll` is rebuilt from +Rudder.Core 2.0.0. + +A project now has exactly two environments, `staging` and `prod`. The SDK key +set in `RudderConfiguration` belongs to one of them, so the environment is +resolved at login and carried inside the tokens; nothing in the API takes an +environment argument and players do not cross between environments. Tokens +saved to PlayerPrefs by 1.x have no environment claim and are rejected with +401: the refresh fails, the stored tokens are cleared and +`Auth.AuthStateChanged` fires `SignedOut`, so call `LoginWithDeviceAsync` +again on that event. + +Quests, scenarios and offers are addressed by slug instead of id, because ids +differ between staging and prod. `Quest.Id` became `Quest.Slug`, +`Quests.ClaimAsync` takes a quest slug, `Quests.ReportProgressAsync` returns +quest slugs, `Stores.PurchaseAsync` takes an offer slug and `Offer` carries a +`Slug`, `QuestMetrics.PurchaseOffer` builds its metric from the offer slug, +and every scenario-scoped effect and request exposes `ScenarioSlug` instead of +`ScenarioId` (`PendingEffect`, `ScenarioCompletedEffect`, +`ScenarioFailedEffect`, the battle pass requests, +`BattlePass.GetProgressAsync`). `Store.ScenarioId` is unchanged — it is an +authoring reference, not a player-facing one. The Store/Inventory and +Scenarios samples were updated to `offer.Slug`. + +Also inherited from Core: the effects client reconciles against every pending +poll, so a run that disappeared server-side emits `OnScenarioCompleted` +instead of lingering, and `run_not_active` is a terminal rejection that drops +the run and emits `OnScenarioFailed`. + ## 1.0.0 Breaking changes, following the Rudder.Core 1.0.0 rework: diff --git a/Packages/rudder.sdk/Runtime/Plugins/Rudder.Core.dll b/Packages/rudder.sdk/Runtime/Plugins/Rudder.Core.dll index 6bf0567c5760e17fc12f7f2323cd94c0d389d7ff..ef6147546c33d291a2b140ae2afac3b3bb3d687a 100644 GIT binary patch delta 40045 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%0Ah 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 diff --git a/Packages/rudder.sdk/Samples~/Scenarios/ScenariosSample.cs b/Packages/rudder.sdk/Samples~/Scenarios/ScenariosSample.cs index beb5354..dabc993 100644 --- a/Packages/rudder.sdk/Samples~/Scenarios/ScenariosSample.cs +++ b/Packages/rudder.sdk/Samples~/Scenarios/ScenariosSample.cs @@ -81,8 +81,8 @@ namespace RudderSdk.Unity.Samples if (offer == null) throw new InvalidOperationException("Store " + storeSlug + " has no offers."); - _page.Log("Stores.PurchaseAsync(" + storeSlug + ", " + offer.Id + ")"); - var response = await client.Stores.PurchaseAsync(storeSlug, offer.Id); + _page.Log("Stores.PurchaseAsync(" + storeSlug + ", " + offer.Slug + ")"); + var response = await client.Stores.PurchaseAsync(storeSlug, offer.Slug); if (response != null && response.Success != true) throw new InvalidOperationException(response.Error ?? "Purchase rejected"); diff --git a/Packages/rudder.sdk/Samples~/StoreInventory/StoreInventorySample.cs b/Packages/rudder.sdk/Samples~/StoreInventory/StoreInventorySample.cs index ec460a3..3a3ed76 100644 --- a/Packages/rudder.sdk/Samples~/StoreInventory/StoreInventorySample.cs +++ b/Packages/rudder.sdk/Samples~/StoreInventory/StoreInventorySample.cs @@ -55,11 +55,11 @@ namespace RudderSdk.Unity.Samples foreach (var offer in store.Offers) { var slug = store.Slug; - var offerId = offer.Id; - var name = string.IsNullOrEmpty(offer.Name) ? offer.Id : offer.Name; + var offerSlug = offer.Slug; + var name = string.IsNullOrEmpty(offer.Name) ? offer.Slug : offer.Name; var price = offer.Price == null ? "free" : offer.Price.Amount + " " + offer.Price.Currency; if (_page.Button("Buy " + name + " — " + price)) - _page.Run(this, () => Buy(slug, offerId, name)); + _page.Run(this, () => Buy(slug, offerSlug, name)); } } }); @@ -74,11 +74,11 @@ namespace RudderSdk.Unity.Samples _page.SetStatus(_stores.Count == 0 ? "No stores" : "Loaded " + _stores.Count + " store(s)"); } - async Task Buy(string storeSlug, string offerId, string name) + async Task Buy(string storeSlug, string offerSlug, string name) { var client = Rudder.Initialize(); - _page.Log("Stores.PurchaseAsync(" + storeSlug + ", " + offerId + ")"); - var response = await client.Stores.PurchaseAsync(storeSlug, offerId); + _page.Log("Stores.PurchaseAsync(" + storeSlug + ", " + offerSlug + ")"); + var response = await client.Stores.PurchaseAsync(storeSlug, offerSlug); if (response != null && response.Success != true) throw new InvalidOperationException(response.Error ?? "Purchase rejected"); diff --git a/Packages/rudder.sdk/package.json b/Packages/rudder.sdk/package.json index 4f09e71..eed0da8 100644 --- a/Packages/rudder.sdk/package.json +++ b/Packages/rudder.sdk/package.json @@ -1,6 +1,6 @@ { "name": "rudder.sdk", - "version": "1.0.0", + "version": "2.0.0", "displayName": "Rudder SDK", "description": "LiveOps SDK for Unity — player auth, remote config, stores, inventory, leaderboards, scenarios.", "unity": "6000.0", diff --git a/skills/rudder-unity-sdk/reference/authentication.md b/skills/rudder-unity-sdk/reference/authentication.md index 608706d..b543b0f 100644 --- a/skills/rudder-unity-sdk/reference/authentication.md +++ b/skills/rudder-unity-sdk/reference/authentication.md @@ -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 diff --git a/skills/rudder-unity-sdk/reference/battlepass.md b/skills/rudder-unity-sdk/reference/battlepass.md index 516ee3f..8aa0f63 100644 --- a/skills/rudder-unity-sdk/reference/battlepass.md +++ b/skills/rudder-unity-sdk/reference/battlepass.md @@ -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 PurchasePremiumAsync(CancellationToken = default)` — posts `onPremiumPurchase` on success - `LevelUp()` / `LevelUpAsync()` — posts `onLevelUp` - `End()` / `EndAsync()` — posts `onComplete` -- `Get(key, fallback)`, `RunId`, `ScenarioId`, `NodeId`, `Data` like other effects +- `Get(key, fallback)`, `RunId`, `ScenarioSlug`, `NodeId`, `Data` like other effects `BattlePassLevelEffect` (from `OnBattlePassLevel`) has `Claim()` / `ClaimAsync()` (posts `onComplete`) and a `Level` property. ## BattlePassService (direct) -- `Task GetProgressAsync(string scenarioId, string nodeId, CancellationToken = default)` +- `Task GetProgressAsync(string scenarioSlug, string nodeId, CancellationToken = default)` - `Task AddXpAsync(AddBattlePassXpRequest request, CancellationToken = default)` - `Task ClaimRewardAsync(ClaimBattlePassRewardRequest request, CancellationToken = default)` - `Task 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 diff --git a/skills/rudder-unity-sdk/reference/quests.md b/skills/rudder-unity-sdk/reference/quests.md index 0e4c90f..6af974c 100644 --- a/skills/rudder-unity-sdk/reference/quests.md +++ b/skills/rudder-unity-sdk/reference/quests.md @@ -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> ListAsync(CancellationToken = default)` -- `Task ClaimAsync(string questId, CancellationToken = default)` -- `Task> ReportProgressAsync(string metric, long amount, CancellationToken = default)` — returns the ids of quests completed by this report. +- `Task ClaimAsync(string questSlug, CancellationToken = default)` +- `Task> 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`), `Rewards` (`List`). - `QuestObjectiveProgress` — `ObjectiveId`, `Metric`, `Current` (`long?`), `Target` (`long?`), `Completed` (`bool?`). @@ -28,6 +28,6 @@ Models: `Granted` (`List`), `Error`. Store purchases already report metrics automatically; the metric strings are -built by `QuestMetrics.PurchaseOffer(offerId)` → `purchase.offer:` +built by `QuestMetrics.PurchaseOffer(offerSlug)` → `purchase.offer:` and `QuestMetrics.PurchaseItem(itemId)` → `purchase.item:` (`RudderSdk.Core.QuestMetrics`, static). diff --git a/skills/rudder-unity-sdk/reference/scenarios.md b/skills/rudder-unity-sdk/reference/scenarios.md index 28fa417..1a3cfaa 100644 --- a/skills/rudder-unity-sdk/reference/scenarios.md +++ b/skills/rudder-unity-sdk/reference/scenarios.md @@ -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(string key, T defaultValue = default)` for node data. | Event | Effect | Game must | @@ -39,8 +39,8 @@ and `T Get(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 diff --git a/skills/rudder-unity-sdk/reference/stores-inventory.md b/skills/rudder-unity-sdk/reference/stores-inventory.md index b656043..be81905 100644 --- a/skills/rudder-unity-sdk/reference/stores-inventory.md +++ b/skills/rudder-unity-sdk/reference/stores-inventory.md @@ -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> ListAsync(CancellationToken = default)` - `Task GetAsync(string slug, CancellationToken = default)` -- `Task PurchaseAsync(string storeSlug, string offerId, string idempotencyKey = null, CancellationToken = default)` — generates an idempotency key when omitted. +- `Task 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`), `CreatedAt`, `UpdatedAt`. -- `Offer` — `Id`, `Name`, `Price` (`OfferPrice`), `Contents` +- `Offer` — `Id`, `Slug`, `Name`, `Price` (`OfferPrice`), `Contents` (`List`), `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:` / `purchase.item:` (see +`purchase.offer:` / `purchase.item:` (see [quests.md](quests.md)). The store slug must exist in the dashboard, else 404. ## Inventory — `client.Inventory` (`InventoryService`)