ROLearn/ Documentation / SDK
Get API key →SDK product →

Release notes

What's new in each version of the ROLearn-Multiplatform SDK packages. Versions follow semver — minor bumps are backwards-compatible, major bumps are not. Direct download links live on the Downloads page.

Roblox (auto-instrument runtime)

Package: RoLearn · Studio plugin

v1.6.0 — 2026-09

  • Add: the live census. Count what your online players currently hold, every few minutes, without ROLearn reading a single save. Map a field under Data Storage, approve it, turn on "Answer live", and the runtime picks it up on its next config poll - no game update and no republish to add a field later.
  • Why it exists: a sampled profile scan cannot report anything held by under roughly 1 to 1.5% of players, because a count that small cannot clear the privacy floor honestly. That is exactly the rare-item question a simulator most wants answered. A live count has no such limit, because it is a census rather than a sample.
  • Zero game code for a stat you already expose as a leaderstats value or a Player attribute. A field inside your ProfileService table needs one line, once: RoLearn.Projection:Provide(function(player) return Profiles[player].Data end). The runtime then reads only the paths you approved.
  • What leaves your server is a COUNT. The profile your game is already holding is read in memory for a player already online, so no DataStore request is made and none of the shared read budget for that experience is spent. No value, no user id and no per-player row is ever transmitted or stored.
  • Note: a player whose save has not loaded yet is reported as unreadable rather than as holding nothing, so a window states its own coverage. Counting them as empty would drag every share down by however many players had just joined.
  • Note: the count describes players ONLINE in the window, who own more than average. It is published against that denominator rather than presented as a figure about everybody.
  • Improve: version stamped as 1.6.0 on every event payload (_rl_version) and in the plugin status line. Press Activate in the plugin and republish to pick this up. Your existing ingest key now keeps working until you publish, instead of being revoked the moment you press the button.

v1.5.0 — 2026-09

  • Add: the Progress collector. Name a stat your game already keeps (a leaderstats value or a Player attribute) and its milestones under Data Storage, "Measure it exactly with the SDK", and the runtime sends a `level` event the first time each player crosses each milestone. No game code. This is what turns "how long does it take to reach Aura 40" from an estimate from saves into an exact time.
  • Note: a returning player's save loads a moment after they join, so readings in the first 20 seconds only set a baseline, sent once with `baseline = true` and never counted as reaching anything. Exact times therefore cover players who join after the update.
  • Note: each milestone is sent at most once per session, and the backend keeps only a player's first crossing, so a watcher costs at most one event per milestone per session against your quota. It does nothing until a watcher is configured for the experience.
  • Improve: version stamped as 1.5.0 on every event payload (`_rl_version`) and in the plugin status line. Press Activate in the plugin and republish the place to pick this up - the plugin always installs the latest runtime, so there is no plugin update to wait for.

v1.4.0 — 2026-08

  • Add: crash capture. The runtime had no crash emitter at all before this, so the Crash Explorer could never populate for any experience no matter how many crashes a game had - the backend half (crash grouping, signature hashing, the hourly rollup) has been live and waiting the whole time. Lua errors carrying a stack trace are now emitted as `crash` events via LogService.MessageOut.
  • Add: `errorEvents` option, default OFF. Single-line errors and warnings are emitted as `error` events only when you pass `RoLearn.start{ errorEvents = true }`. It is opt-in because a chatty game can log thousands of warnings an hour and every event counts against your monthly quota - crashes are rare enough to always be worth one, warnings are not.
  • Fix: the first scene census now arrives about 10 seconds after a server starts instead of 5 minutes. The sampler collects for a full window before it emits, and the window was 300s, so the Scene Complexity and Optimization Recommendations cards stayed on "Collecting" for any play-test shorter than five minutes with nothing to explain why. Later windows keep the normal 300s cadence.
  • Note: crash traces are scrubbed of email-shaped substrings before sending, because ingest rejects payloads that look like they carry PII and a rejected crash lands in your DLQ instead of the Crash Explorer.
  • Note: the runtime's own log lines are never forwarded as crash or error events. A flush warning becoming an event that fails and warns again is a self-amplifying loop, and you would be billed for it.
  • Improve: version stamped as 1.4.0 on every event payload (`_rl_version`) and in the plugin status line. Press Activate in the plugin and republish the place to pick this up - the plugin always installs the latest runtime, so there is nothing to download and no plugin update to wait for.

Roblox (Lua)

Package: RoLearnSDK ·

v0.6.1 — 2026-05

  • Critical fix: the LocalizationService method name shipped in v0.3.0–v0.6.0 was misspelled as GetCountryRegionCodeForPlayerAsync (extra "Code") — a method that does not exist on Roblox's API. pcall silently swallowed the error and every player resolved to "XX", which is why the country pulse dashboard has only "XX" data on record for games running an older SDK. The correct method is GetCountryRegionForPlayerAsync. After republishing your place with v0.6.1 the country column on the SDK Dashboard should populate with real ISO codes within one nightly aggregation cycle.
  • Add: country lookup now retries up to 3× with exponential backoff (2s, 4s, 8s) to ride out Roblox's per-IP rate-limit on the country endpoint during mass-join waves — the most common cause of remaining "XX" rows on real adult accounts after the typo fix.
  • Add: Player.LocaleId fallback. When the account-region API returns empty or fails after all retries (under-13 accounts, restricted regions), the SDK now parses the locale tag (e.g. "vi-vn" → "VN") so the player still attributes to a country, just at lower confidence.
  • Add: session_start and session_end payloads now carry a `country_source` field ∈ {"account_region","locale_id","unknown"} so the dashboard can distinguish definitive country from inferred country at the event level.
  • Add: delayed re-resolve. If the join-time lookup returns "XX" we retry once 5 minutes later (after Roblox's per-IP bucket refills), patching the cached session entry so subsequent events for the same player carry the real ISO code.
  • Add: warn() lines are now emitted when LocalizationService raises an error, so a future API rename surfaces loudly in your Studio output instead of silently degrading to "XX" like this typo did for three weeks.
  • Improve: SDK_VERSION stamped to 0.6.1 in every event payload (_sdk_version field).

v0.6.0 — 2026-05

  • Fix: flush now uses HttpService:RequestAsync and classifies failures — a 4xx (except 429) is treated as a permanent validation/auth rejection (the batch is dropped and the server's error body is logged), while 5xx / 429 / network errors stay retryable with exponential backoff. Resolves the infinite HTTP 422 retry loop where a single malformed event blocked every event behind it and re-persisted across server restarts.
  • Fix: an out-of-enum purchase.source (e.g. a product NAME passed where iap|gamepass|devproduct|subscription is expected) is now repaired to "iap" at enqueue time — before the event can enter the in-memory or persistent buffer. This was the root cause of the 422 loop.
  • Fix: crash/error auto-capture now ignores the SDK's own [RoLearnSDK] log lines (which previously formed a self-amplifying feedback loop) and Roblox's "DataStore request was added to queue" throughput notices. Extend the ignore list via the new config.crash_ignore_patterns option.
  • Improve: the persistent DataStore buffer is now schema-tagged; a buffer written by an incompatible older version is discarded on load instead of being replayed.
  • Improve: SDK_VERSION stamped to 0.6.0 in every event payload (_sdk_version field).
  • Server-side companion: the ingest endpoint now partial-accepts batches — valid events are written and only malformed events are dead-lettered (visible in /studio/dlq), so one bad event no longer fails the whole batch even for older clients.

v0.5.0 — 2026-05

  • Add: SDK.getVariant(player, experimentId) — sticky deterministic A/B test assignment via /experiments/{id}/assign. Returns the variant name, or nil if the player is not in the experiment / fetch fails. Per-server 1h in-memory cache keyed by (experimentId, playerId) so customers can call it per-event without spamming the assign endpoint. (Phase 8.1, Scale+ plans.)
  • Phase 4+5 parity: trackAdImpression, trackPerformance, trackCrash, trackError now match Unity + Steamworks event shapes 1:1.
  • Improve: SDK_VERSION stamped to 0.5.0 in every event payload (_sdk_version field).

v0.4.0 — 2026-05

  • Add: country propagated into purchase, session_end, and ad_impression payloads — cached once per join from LocalizationService:GetCountryRegionForPlayerAsync(), then attached to every subsequent event for the player. Powers per-country playtime + revenue rollups on the new Demographics panel without re-resolving country on each event.
  • Fix: SDK.purchase() now defaults currency to "ROBUX" instead of "USD". The MarketplaceService gamepass auto-hook passes no currency field, so the prior "USD" default silently excluded every auto-tracked gamepass purchase from sdk_revenue_daily rollups. Gamepass revenue should populate correctly going forward.
  • Improve: SDK_VERSION stamped to 0.4.0 in every event payload (_sdk_version field).

v0.3.0 — 2026-05

  • Add: FPS sampling via RunService.Heartbeat — one performance snapshot emitted per session_end.
  • Add: LogService crash/error capture — multi-line errors grouped as crash events, single-line as error events.
  • Add: trackAdImpression() for ILRD (Impression Level Revenue Data) — format/network/revenue_usd fields.
  • Add: trackPerformance() and trackCrash() manual helpers for custom instrumentation.
  • Improve: SDK_VERSION stamped to 0.3.0 in every event payload.

v0.2.1 — 2026-04

  • Fix: DataStore offline buffer no longer drops events when a server crashes mid-flush.
  • Add: optional before_send(event) hook for per-event PII redaction.
  • Improve: exponential backoff jitter cap raised to 60s to reduce thundering-herd on outages.

v0.2.0 — 2026-03

  • Add: persistent offline buffer using DataStore — survives server restart.
  • Add: auto-attached MarketplaceService.PromptPurchaseFinished purchase tracking.
  • Breaking: SDK.init now requires endpoint (was hardcoded). Update your boot script.

v0.1.0 — 2026-02

  • Initial release — server-side telemetry for Roblox games.

Unity (C#)

Package: com.rolearn.sdk · UPM (Git URL)

v0.5.0 — 2026-05

  • Add: RoLearnSDK.GetVariantAsync(experimentId, playerId) — sticky deterministic A/B test assignment via /experiments/{id}/assign with 1h in-memory cache. Returns the variant name (Task<string>), or null if the player is not in the experiment / fetch fails. (Phase 8.1, Scale+ plans.)
  • Phase 4+5 parity: TrackAdImpression, TrackPerformance, TrackCrash, TrackError implementations brought to full event-shape parity with Roblox + Steamworks.
  • Improve: _sdk_version stamped as "0.5.0" on every event payload.

v0.1.0 — 2026-04

  • Initial release — UPM package for Unity 2021.3 LTS+.
  • IL2CPP-safe via shipped link.xml.
  • Optional Unity IAP listener auto-tracks purchases when #if UNITY_PURCHASING.

Steamworks (C++)

Package: rolearn-sdk · vcpkg / Conan

v0.5.0 — 2026-05

  • Add: rolearn::Client::get_variant(experiment_id, player_id) — sticky deterministic A/B test assignment via /experiments/{id}/assign over libcurl, mutex-guarded in-process 1h cache. Returns the variant string, or empty string if the player is not in the experiment / fetch fails. C ABI wrapper rolearn_get_variant() also exposed. (Phase 8.1, Scale+ plans.)
  • Phase 4+5 parity: track_ad_impression, track_performance, track_crash, track_error now match Roblox + Unity event shapes 1:1.
  • Improve: SDK_VERSION constant stamped to "0.5.0" in every event payload (_sdk_version field).

v0.2.1 — 2026-04

  • Fix: ISteamMicroTxn query batching no longer leaks file descriptors on Linux.
  • Add: optional Conan recipe alongside vcpkg manifest.

v0.2.0 — 2026-03

  • Add: ISteamUserStats hook — periodic capture of live concurrent users.
  • Add: thread-safe instance singleton.

v0.1.0 — 2026-02

  • Initial release — header-only C++ embed client with libcurl + nlohmann/json.