Code docs

Every method, in one place

The full Luau API the plugin installs into your place: live settings, messages, event tracking, player traits and group ranks. Edit RoSignalHandlers, never RoSignal.

How the pieces fit

The plugin inserts three scripts into ServerScriptService. Only two of them are yours.

ServerScriptService.RoSignal — the runtime. It talks to RoSignal, caches your settings, receives messages, and batches events. The plugin keeps it up to date, so never edit it by hand: the next setup or repair overwrites it.

ServerScriptService.RoSignalHandlers — your file. This is where your game reacts to RoSignal, and the only file you have to touch. It arrives with working examples inside it; delete what you do not need. Plugin updates never overwrite it.

ServerScriptService.RoSignalSettings — your file too. It controls which events send a position to the Analytics map. Written once at pairing time and never overwritten.

Everything below is available after one line: local RoSignal = require(game.ServerScriptService.RoSignal). Nothing in the API errors your game — every call is wrapped, and if RoSignal is unreachable your game keeps running on the last values it had.

local RoSignal = require(game.ServerScriptService.RoSignal)

Settings (live config)

Values you change on the dashboard and read in game. A change reaches every live server in about a second — no publish, no restart.

A setting can have three values at once: the global value everyone gets, a rollout value given to a percentage of players or to a segment, and a per-player override. Get returns the global value. GetFor resolves all three for one player: override first, then rollout, then global. Inside gameplay code, use GetFor — otherwise rollouts and overrides silently do nothing.

RoSignal.Get(key: string, default: any) -> any

Read the global value of a setting, right now.

  • Reads from an in-memory cache — instant, no yield, safe in hot loops.
  • Returns `default` when the key does not exist yet or config has not arrived.
  • Ignores rollouts and player overrides. Use GetFor when a player is involved.
local damage = RoSignal.Get("Damage", 10)
RoSignal.GetFor(player: Player | number, key: string, default: any) -> any

Read the value that applies to one specific player.

  • Resolution order: player override → rollout value (if that player is in the rollout) → global value → `default`.
  • Accepts a Player instance or a raw UserId.
  • Instant, no yield.
Players.PlayerAdded:Connect(function(player)
	local damage = RoSignal.GetFor(player, "Damage", 10)
end)
RoSignal.InRollout(player: Player | number, key: string) -> boolean

True when this player is receiving a setting's rollout value.

  • Bucketing is deterministic per key and UserId, so the same player stays in or out as the percentage grows.
  • For segment-targeted rollouts it checks membership instead of the percentage.
if RoSignal.InRollout(player, "Damage") then
	-- this player is testing the new value
end
RoSignal.HasOverride(player: Player | number, key: string) -> boolean

True when this player has a per-player override on a setting.

  • Useful to skip your own balancing logic for players you are hand-tuning.
if RoSignal.HasOverride(player, "Damage") then
	print(player.Name, "is on a custom value")
end
RoSignal.OnKeyChange(key: string, fn: (newValue, oldValue) -> ())

Run a function only when one setting changes.

  • Does not fire for the value the server started with — only on changes.
  • You can register as many handlers per key as you like.
  • Handlers run in their own thread; an error in one never stops the others.
RoSignal.OnKeyChange("Damage", function(newValue, oldValue)
	print("Damage:", oldValue, "->", newValue)
end)
RoSignal.Bind(key: string, fn: (value) -> ())

Run now with the current value, then again on every change.

  • The same as OnKeyChange plus one immediate call — the usual choice for anything you apply to the world (lighting, spawn rates, multipliers).
  • The first call may receive nil if config has not arrived yet; handle a nil value.
RoSignal.Bind("XpMultiplier", function(value)
	XP_MULTIPLIER = value or 1
end)
RoSignal.OnChange(map: { [key]: fn }) | RoSignal.OnChange(fn)

Watch several settings at once, or every setting.

  • Given a table, it registers one handler per key (same behaviour as OnKeyChange).
  • Given a single function, it runs for every setting change — use it for logging, not for gameplay.
RoSignal.OnChange({
	Damage = function(value) end,
	Speed = function(value) end,
})

RoSignal.OnChange(function(key, newValue)
	print("changed:", key, newValue)
end)

Messages

One-off signals pushed from the dashboard or from an automation into every live server.

Messages are how RoSignal tells your game to do something right now: start an event, hand out a reward, announce something. Every handler receives (data, topic, player). `player` is only set when the send targeted one player — for example an automation acting on the player who triggered it — and only on the server that player is in.

RoSignal.OnMessage(topic: string, fn: (data, topic, player) -> ())

React to one message topic.

  • `data` is whatever the dashboard or automation sent — a table, a string, or nil.
  • `player` is the targeted Player instance, or nil for a broadcast.
  • Unknown topics are ignored, so you can send a topic before you write the handler.
RoSignal.OnMessage("Reward", function(data, topic, player)
	if player then
		giveReward(player, data.item)
	end
end)
RoSignal.OnMessage(fn: (data, topic, player) -> ())

Catch every message, whatever the topic.

  • Handy for logging or for routing topics yourself. Runs in addition to topic handlers.
RoSignal.OnMessage(function(data, topic)
	print("message:", topic, data)
end)

Events

Everything RoSignal shows you in Analytics, and everything automations can trigger on, comes from Track.

Track never sends an HTTP request on the spot. The runtime counts events in memory, merges the counts across all your servers with Roblox's MemoryStore, and uploads one small summary a minute. Your counts stay exact — you just stop paying for one request per event, and your game stops waiting on the network. A thin slice of raw events is kept so you can still read real examples in the dashboard, and pending counts are flushed when a server shuts down.

Keep event names stable and low in number: plans limit distinct event names per game, and a name generated per player or per item burns that limit instantly. Put the varying part in the props table instead.

RoSignal.Track(name: string, props?: table, player?: Player, options?: { map: boolean })

Report that something happened.

  • `name` is the event name shown in Analytics — a stable label like "ShopPurchase", not a per-player string.
  • `props` is a flat table of extra detail. Numbers get summed and averaged for you; strings are grouped into breakdowns.
  • Passing `player` attributes the event to them, which is what makes per-player analytics, segments, and player-targeted automations work.
  • When a player is passed, their position is also counted into a map cell so the event shows on the Analytics map. Pass `{ map = false }` to skip that for one event, or configure it globally in RoSignalSettings.
  • Never errors and never yields: if anything is wrong the event is dropped and your game runs on.
RoSignal.Track("ShopPurchase", { item = "Sword", robux = 199 }, player)

-- a high-frequency event that would flood the map
RoSignal.Track("Heartbeat", {}, player, { map = false })

Player traits

Facts about a player that segments can filter on.

Traits describe who a player is — VIP, level, region, whatever matters to you — as opposed to events, which describe what they did. Segments built on traits then drive rollouts, player overrides, and automation actions.

RoSignal.SetTraits(player: Player | number, traits: table)

Attach or update facts about a player.

  • Traits are merged, not replaced — sending { level = 12 } leaves other traits alone.
  • Calls are batched and sent a few seconds later, so calling it repeatedly is cheap.
  • Keep the key set small and stable, the same way you would with event names.
RoSignal.SetTraits(player, { vip = true, level = 12, region = "EU" })

Group ranks

Promote a player in your linked Roblox group from inside the game.

Requires a group linked to this game in RoSignal and the Roblox group permission granted to your account. RoSignal performs the rank change with your credentials, so the group role must sit below your own.

RoSignal.SetRank(player, role: string | number | { roleId, roleName }, options?: { onlyPromote: boolean }) -> boolean, string?

Move a player into a group role.

  • Pass a role name ("Member"), a numeric role id, or a table with `roleId` / `roleName`.
  • Promote-only by default. Pass `{ onlyPromote = false }` to allow demotions.
  • Yields — call it inside task.spawn if you are on a hot path.
  • Returns `true`, or `false` plus a readable reason. It never errors.
task.spawn(function()
	local ok, err = RoSignal.SetRank(player, "Member")
	if not ok then
		warn("rank failed:", err)
	end
end)

Moderation

Bans and kicks issued in RoSignal need no code at all.

Bans go through Roblox's own restriction API, so Roblox enforces them at join time — there is nothing to write in your handlers, and a banned player cannot get in even if your game is offline from RoSignal. Kicks are delivered to the live servers by the runtime and applied for you.

For your own in-game ban rules, use Roblox's Players:BanAsync directly.

RoSignalSettings reference

Your file. Controls which events send a position to the Analytics map.

Positions are only ever read when you pass a player to Track. Turning them off saves bandwidth on events where a location means nothing.

return {
	-- Events that include a player also send that player's position.
	MapByDefault = true,

	-- Optional allow-list. When set, ONLY these events send a position,
	-- whatever MapByDefault says.
	MapEvents = nil, -- e.g. { "Death", "ShopPurchase" }

	-- Events that never send a position.
	MapExclude = {}, -- e.g. { "Heartbeat" }
}

Behaviour and guarantees

What happens when things go wrong.

No RoSignal call errors your game. Every network call is wrapped; failures are dropped quietly and retried with backoff where it makes sense.

If RoSignal is unreachable, your game keeps running on the last settings it received. When the connection returns, new values arrive and your change handlers fire as usual.

Only Get, GetFor, InRollout, and HasOverride read local state and never yield. Track and SetTraits are fire-and-forget. SetRank is the one call that yields.

Pending event counts are flushed on server shutdown, so a shutting-down server does not lose its last minute.

Changes to settings and messages take effect in live servers immediately — publishing is only needed when the scripts themselves change (first setup, or after a Repair setup).

Troubleshooting

The four things that actually go wrong.

Nothing reaches RoSignal — turn on Allow HTTP Requests in Game Settings → Security, then publish the place.

The plugin says it cannot reach rosignal.app — press Retry connection in the plugin and choose Allow, or enable it in Plugins → Manage Plugins → RoSignal → Permissions.

RoSignal or RoSignalHandlers is missing or out of date — open the plugin and press Repair setup. It reinstalls the runtime and leaves your own files alone.

You lost the pairing code, or want to rotate the key — generate a new code on the Setup page. Pairing again issues a fresh key and retires the old one, so re-run setup in Studio and publish.