Start typing to search packages!
roblox-liveops
By @hurreman
Roblox
Mirrored from Wallyroblox-liveops
The systems that keep a live Roblox game measurable and bring players back, taken mostly from Magic Neighborhood (the only one of the three games that has all of them) and checked against Slime Nebula's reward rules.
# wally.toml
[dependencies]
LiveOps = "hurreman/roblox-liveops@0.1.0"
Modules load on first access. Each one receives the game's own data through callbacks (its save tables, stat readers and grants), so none of them requires game code.
| Module | Side | What it does |
|---|---|---|
Telemetry | server | Onboarding funnel (append-only, new players only), economy flows summed and flushed every 60 s and on leave, progression, custom events with two fields, perf samples (fps, memory, receive rate, server-read ping). Place version in every event. Prints instead of sending in Studio. |
JoinSource | pure | Tagged link launch data (src=tt&c=clip07, JSON, or a bare word) parsed to source + campaign. Only the game's own sources are kept; none / unknown / invalid / other otherwise. |
Sessions | server | A visit log in the save: length, place version, last action, idle and still time, shutdown or leave, join source, device, plus the game's own fields. |
Activity | server + client | AFK detection: active if the client pinged input in the last 120 s. Crowd-sized things count ActiveCount(). |
Badges | server | Save-derived badges and event badges owed until awarded. Holds back during onboarding, counts an award only if it went through, dry run in Studio. |
Milestones | pure | First-time progression stamps with play seconds, and listeners. |
Leaderboards | server | All-time, daily (a store per day) and per-server boards on OrderedDataStores; log encoding for huge values; this server's players when DataStores are off. |
Streak | pure | Daily login streak: consecutive, broken, an optional gift cycle that survives a miss (Slime Nebula), an optional buy-back window (Magic Neighborhood). |
Playtime | pure | A daily ladder of gifts by seconds played; claimed as a bitmask or a set; rolling over to a new day hands back what went unclaimed. |
Settings | server | Client-written settings validated per key (Bool, Choice, Volume, Flags), with a change hook. |
FpsWatch | client | Sustained low frame rate (windowed averages, grace period, hitches and unfocused time ignored), once per session. |
A few of them in use
local LiveOps = require(ReplicatedStorage.Packages.LiveOps)
local telemetry = LiveOps.Telemetry.new({
steps = { "Joined", "BoughtHouse", "FirstMerge" }, -- only ever append
funnel = function(player) local s = data(player) return s and { done = s.stats.funnel, at = s.stats.funnelAt } end,
playSeconds = function(player) return data(player).stats.playSeconds end,
sourceFields = function(player) local s = data(player).stats return s.joinSrc, s.joinCamp end,
balance = function(player, currency) return data(player).cash end,
transactions = { Daily = "TimedReward", Offline = "TimedReward" },
iap = function(sku) return Products.List[sku] ~= nil end,
counters = function(name, value, a, b) adminStats.add(name, value, a, b) end,
})
telemetry:Start()
telemetry:Onboarding(player, "BoughtHouse")
telemetry:Source(player, "Converter", 120)
local clock = Core.ResetClock.Stockholm -- roblox-core
local claimed = LiveOps.Streak.Claim(save.daily, clock.GetDayNumber(os.time()), { cycle = 7 })
local boards = LiveOps.Leaderboards.new({
boards = { { id = "Earned", log = true }, { id = "Today", scope = "daily" }, { id = "Here", scope = "server" } },
value = function(board, player) ... end,
dayOf = clock.GetDayNumber,
})
boards:Start()
Moving the games over
-
Magic Neighborhood. Each service keeps its game-specific parts, and the engine underneath becomes the package's:
Today Keeps Becomes TelemetryServicethe ONBOARDING step list, the tutorial step mapping, place()/housesBucket()TelemetryJoinSourceits SOURCES table JoinSource.new(SOURCES)(its spec passes unchanged through an adapter, and a corpus agrees with the old module)SessionServicegoal, tutorial step and cash, as extraSessionsActivityServiceandclient/ActivityActivityBadgeServiceandshared/Badgesthe rules BadgesMilestonesMilestones, with the TOTD-specific helpers (houses,seedBought) staying beside itLeaderboardServiceleaderstats, rollDay,statValueLeaderboardsRewardsServiceoffline earnings, payouts, welcome card the claim rules: Streakwithsave = { minStreak = 2, maxMissedDays = 3 }, andPlaytimeSettingsServiceSettingswith its keysFpsWatchFpsWatchwithwatching = preset is "auto" -
Slime Nebula:
DailyRewardConfig.EvaluateClaimEligibilitybecomesStreak.Status(..., { cycle = #Days }), with days fromResetClock.Stockholm. They agree on 2000 random states.PlaytimeRewardConfigtiers becomePlaytimewith itsClaimedTiersset.Analytics.luaandOnboardingFunnelcan move ontoTelemetry(keep theRobloxAdminServerstats copy ascounters).
-
Steal an Egg:
TelemetryServicebecomesTelemetrythe same way.
Developing
lune run tests/run # specs on hurreman/luau-spec (DevPackages, else ../luau-spec); fakes in tests/fixtures
When the sibling checkouts are present, three specs check against the games' own code:
JoinSourceruns Magic Neighborhood's spec and a launch-data corpus against its module.Streakdiffs Slime Nebula'sEvaluateClaimEligibilityover 2000 random states. A deliberate change to the cycle rule made it fail.Playtimediffs Magic Neighborhood'sgiftsReadyon its real gift ladder.
In Studio, these were checked: the analytics enum keys and transaction type names the package sends, and that the AnalyticsService, BadgeService, OrderedDataStore, ProximityPrompt and Players APIs it calls exist.
Not run yet:
- real AnalyticsService calls, which need a real Player in Play mode;
- real badge awards;
- OrderedDataStore reads and writes.
Package Details
Install command (Click to copy)
Version
0.1.0
License
MIT
Safe for commercial use
Automated license review — not legal advice.
