Files
rudder-csharp-sdk/CHANGELOG.md
T
2026-09-06 22:25:32 +03:00

10 KiB

Changelog

2.0.0

Breaking change — major bump, required by the backend environments release. Every project now has exactly two environments, staging and prod, and the SDK key you configure decides which one the player belongs to. The environment never appears in the client API: it is resolved at login and carried inside the access and refresh tokens. Tokens issued before this release have no environment claim and are rejected with 401, so the first call after the backend upgrade refreshes, fails, clears the stored tokens and raises AuthService.AuthStateChanged with RudderAuthState.SignedOut. Log the player in again with Auth.LoginWithDeviceAsync / LoginWithCustomAsync.

Quests, scenarios and offers are addressed by their slug instead of their id, because ids differ between staging and prod while slugs are stable. Renamed accordingly: Quest.Id is now Quest.Slug, QuestsService.ClaimAsync takes a quest slug, ReportProgressAsync returns the slugs of the completed quests (ReportQuestProgressResponse.CompletedQuestSlugs), StoresService.PurchaseAsync takes an offer slug and Offer carries a Slug, QuestMetrics.PurchaseOffer builds its metric from the offer slug, and every scenario-scoped type exposes ScenarioSlug instead of ScenarioIdPendingEffect, the effect objects, ScenarioCompletedEffect, ScenarioFailedEffect, the battle pass requests and BattlePassService.GetProgressAsync. Leaderboards, items and stores already used slugs and are unchanged.

Also shipped here, previously committed but never published: the effects client reconciles against every GET /sdk/v1/scenarios/pending response, so a run that disappears server-side (finished elsewhere, expired after a promote) now emits OnScenarioCompleted instead of lingering; and run_not_active joins unknown_run and run_expired as a terminal rejection that drops the run and emits OnScenarioFailed.

1.0.0

Breaking change — major bump. The local scenario engine is replaced by a server-driven effects client; the realtime websocket client is removed.

Removed

  • Local scenario runtime: PlanRun, ExecutionPlan walking, node sessions (NotificationSession, StoreOfferSession, LeaderboardSession, WaitSession, QuestSession, BattlePassSession, BattlePassLevelSession, ConfigChangedSession), RespondAsync / Respond, RestoreAsync, Clear, ActiveRuns, IsRunning, CurrentNodeId, OnConfigChanged.
  • RudderClientOptions.PlanStateStore / IPlanStateStore and IPlanScheduler — no local plan persistence or delayed plan work.
  • RealtimeService (client.Realtime), RudderClientOptions.RealtimeUrl / RealtimeTransportFactory, IRealtimeTransport / IRealtimeTransportFactory.

Changed (breaking)

  • client.Scenario is a trigger only: TriggerAsync(eventName) returns Task (not started plans). Returned pending effects are ingested into client.Effects.
  • Subscribe to client.Effects.On* instead of client.Scenario.On*. Sessions are now effect objects (NotificationEffect, StoreOfferEffect, …). Complete them with the methods on the effect (DoneAsync, PurchaseAsync/DeclineAsync, EndAsync/ClaimAsync, …).
  • client.Update(deltaTime) is still required every frame — it now pumps the effects client (30s GET /sdk/v1/scenarios/pending heartbeat + wait-deadline checks), not a local DAG or a websocket.
  • After login (or when constructed with a stored access token), the next Update fetches pending effects. There is no loginEvent option; trigger login-gated scenarios yourself.

Migration

Before After
client.Scenario.OnNotification (and other On*) client.Effects.OnNotification (same event names)
NotificationSession / StoreOfferSession / … NotificationEffect / StoreOfferEffect / …
session.CompleteAsync() / RespondAsync("output") effect.DoneAsync() (and the matching method on each effect)
RudderClientOptions.PlanStateStore + RestoreAsync gone — server owns run state; pending fetch after login + heartbeat
client.Realtime gone
client.Update(deltaTime) still required (effects pump)

0.4.0

  • Generated DTOs live under Models/ (RudderSdk.Core.Models). Wire fields are nullable. ListQuestsRequest is gone; Quests.ListAsync POSTs with no body. BoundaryNode.WaitDeadline is DateTimeOffset?.
  • LeaderboardSession.ClaimAsync / Claim complete the leaderboard node with handle onClaim. The server matches live rank to an authored place.
  • RewardClaimedAsync / RewardClaimed are obsolete aliases and will be removed in the next SDK version.
  • rank_not_eligible on Claim leaves the session open (retry Claim or End); it no longer fails the run.

0.3.0

Removed

  • UGC surface: UgcService (client.Ugc), the generated RudderSdk.Core.Models.Ugc DTOs, IUploadTransport and RudderClientOptions.UploadTransport. The server no longer exposes the UGC endpoints.

Changed (breaking)

  • BattlePassService simple overloads sent empty scenario/node ids and always failed server-side. Battle pass state is tied to a scenario battle pass node, so the public API now mirrors the web SDK: GetProgressAsync(scenarioId, nodeId) and the request-DTO overloads AddXpAsync, ClaimRewardAsync, PurchasePremiumAsync (carrying ScenarioId/NodeId/RunId) are public; the internal request overloads are gone.
  • BattlePassSession now exposes the bound battle pass operations (GetProgressAsync, AddXpAsync(source, amount), ClaimRewardAsync(level, track), PurchasePremiumAsync() — crosses onPremiumPurchase on success), mirroring the web SDK session.
  • BattlePassLevelSession.CompleteAsync/Complete renamed to ClaimAsync/Claim; new Level property reads the levelNumber node data key.
  • Unsupported scenario node types now fail the run (OnScenarioFailed) instead of stalling silently.
  • Scenario counter updates no longer read a continuation plan from the response (the server stopped returning one); when the server reports the objective completed, the node crosses onComplete.

Fixed

  • A failed scenario counter update no longer fails the run; the error is logged and the node stays active.

0.2.0

Added

  • QuestsService (client.Quests) with ListAsync / ClaimAsync.
  • AuthService.LoginWithDeviceAsync(region, language, nickname?) and AuthService.RefreshAsync(); AuthStateChanged event (RudderAuthState.SignedIn / SignedOut).
  • Session pipeline: single-flight token refresh plus one transparent retry-on-401 for every API call; failed refresh clears tokens and reports SignedOut.
  • HttpClientTransport — default System.Net.Http.HttpClient-based transport. RudderClientOptions.Transport is now optional, as are TokenStore (InMemoryTokenStore) and DeviceIdProvider (GuidDeviceIdProvider).
  • Typed exceptions: RudderAuthException (401), RudderNotFoundException (404), RudderRateLimitException (429), RudderNetworkException (network/timeout). All carry RequestId from the server error payload.
  • StorageService.ListAllAsync and UgcService.ListAllAsync (IAsyncEnumerable<T> cursor pagination helpers).
  • RudderLogLevel enum; IRudderLogger.Log now takes a level.
  • Package metadata (Rudder.Core, MIT) and XML documentation.

Changed (breaking)

  • Generated models moved to domain folders/namespaces (RudderSdk.Core.Models.<Domain>); the flat RudderSdk.Core.DTO namespace is gone. Battlepass*BattlePass*, SdkQuestQuest.
  • BattlepassServiceBattlePassService, client.Battlepassclient.BattlePass.
  • client.Scenariosclient.Scenario.
  • ScenarioService.SendAsyncTriggerAsync, now returning the started PlanRuns.
  • Scenario events reduced to the canonical set: OnNotification, OnStoreOffer, OnLeaderboard, OnConfigChanged, OnWait, OnQuest, OnBattlePass, OnBattlePassLevel, OnScenarioCompleted, OnScenarioFailed. Legacy events (OnShowNotification, OnShowStore, OnQuestEvent, OnBattlepassEvent, OnLeaderboardEvent, OnNodeEvent, OnNode, OnCompleted, OnStore, OnRemoteConfigOverride, OnRunCompleted, OnRunFailed) are removed, along with the Services/Scenarios/Legacy folder.
  • Session types renamed: StoreSessionStoreOfferSession (BuyAsync/BuyPurchaseAsync/Purchase), RemoteConfigOverrideSessionConfigChangedSession, BattlepassSessionBattlePassSession, BattlepassLevelSessionBattlePassLevelSession, ScenarioRunFailedEventScenarioFailedEvent.
  • StoresService.BuyAsyncPurchaseAsync(storeSlug, offerId, idempotencyKey = null); a null key is replaced with a generated GUID.
  • InventoryService.GetAsync returns IReadOnlyList<PlayerInventoryItem>.
  • StoresService.ListAsync returns IReadOnlyList<Store>.
  • QuestsService.ListAsync returns IReadOnlyList<Quest>.
  • UgcService.GetDownloadUrlAsync returns non-null string and throws InvalidOperationException when the server returns no URL.
  • BattlePassService simple overloads (AddXpAsync(long amount), ClaimRewardAsync(level, track), GetProgressAsync(), PurchasePremiumAsync(idempotencyKey?)); the request-DTO overloads are internal for the scenario runtime.
  • LeaderboardsService.GetRankingAsync/SubmitScoreAsync are internal; use the FindBySlug(slug) handle (SubmitAsync, ListAsync).
  • AuthService.LoginViaDeviceAsync removed; use LoginWithDeviceAsync.
  • ScenarioService.Restore() (sync-over-async) removed; use RestoreAsync.
  • RealtimeService.ConnectAsync and IRealtimeTransport.ConnectAsync take TimeSpan timeouts instead of int timeoutSeconds.
  • IRudderTransport.SendAsync takes a string? accessToken instead of RequestOptions; RequestOptions is removed and EmptyResponse is internal.
  • RudderClient getters Options, Transport, TokenStore, DeviceIdProvider are internal.
  • RudderApiException.StatusCode is int (was long).
  • IRudderLogger.Log(string)Log(RudderLogLevel level, string message).
  • Nullable reference types enabled (<Nullable>enable</Nullable>).
  • Remote config cache now includes every config except an explicit "active": false (a missing flag means active).