Forest Logo
search
package_2

roblox-liveops

By @hurreman

Roblox

Mirrored from Wally

roblox-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.

ModuleSideWhat it does
TelemetryserverOnboarding 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.
JoinSourcepureTagged 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.
SessionsserverA 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.
Activityserver + clientAFK detection: active if the client pinged input in the last 120 s. Crowd-sized things count ActiveCount().
BadgesserverSave-derived badges and event badges owed until awarded. Holds back during onboarding, counts an award only if it went through, dry run in Studio.
MilestonespureFirst-time progression stamps with play seconds, and listeners.
LeaderboardsserverAll-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.
StreakpureDaily login streak: consecutive, broken, an optional gift cycle that survives a miss (Slime Nebula), an optional buy-back window (Magic Neighborhood).
PlaytimepureA 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.
SettingsserverClient-written settings validated per key (Bool, Choice, Volume, Flags), with a change hook.
FpsWatchclientSustained 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:

    TodayKeepsBecomes
    TelemetryServicethe ONBOARDING step list, the tutorial step mapping, place() / housesBucket()Telemetry
    JoinSourceits SOURCES tableJoinSource.new(SOURCES) (its spec passes unchanged through an adapter, and a corpus agrees with the old module)
    SessionServicegoal, tutorial step and cash, as extraSessions
    ActivityService and client/ActivityActivity
    BadgeService and shared/Badgesthe rulesBadges
    MilestonesMilestones, with the TOTD-specific helpers (houses, seedBought) staying beside it
    LeaderboardServiceleaderstats, rollDay, statValueLeaderboards
    RewardsServiceoffline earnings, payouts, welcome cardthe claim rules: Streak with save = { minStreak = 2, maxMissedDays = 3 }, and Playtime
    SettingsServiceSettings with its keys
    FpsWatchFpsWatch with watching = preset is "auto"
  • Slime Nebula:

    • DailyRewardConfig.EvaluateClaimEligibility becomes Streak.Status(..., { cycle = #Days }), with days from ResetClock.Stockholm. They agree on 2000 random states.
    • PlaytimeRewardConfig tiers become Playtime with its ClaimedTiers set.
    • Analytics.lua and OnboardingFunnel can move onto Telemetry (keep the RobloxAdminServer stats copy as counters).
  • Steal an Egg: TelemetryService becomes Telemetry the 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:

  • JoinSource runs Magic Neighborhood's spec and a launch-data corpus against its module.
  • Streak diffs Slime Nebula's EvaluateClaimEligibility over 2000 random states. A deliberate change to the cycle rule made it fail.
  • Playtime diffs Magic Neighborhood's giftsReady on 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

check_circle

Safe for commercial use

Automated license review — not legal advice.