Skip to content

Services

Services are the engine’s built-in systems. Some are objects at the top of the game tree, such as workspace and Players. Others are libraries, such as task and TweenService. You import all of them by name from @rally/sdk:

import { Players, RunService, task, TweenService, workspace } from '@rally/sdk';

This page describes each one and its most useful members. The API reference has every member of every class.

Import What it’s for Works on
game The root of the tree both
workspace The world: parts, gravity, raycasts both
Players Who’s playing: joins, leaves, characters both
Lighting Sky, sun, ambient light and fog both
ReplicatedStorage Objects both sides see: remotes, shared state both
ServerStorage Server-only objects: templates server (empty on clients)
ServerScriptService Server scripts server (empty on clients)
StarterGui UI copied to each player when they join both
StarterPlayer Defaults for new characters both
task Waiting and scheduling on game time both
RunService Per-step events, and which side the code runs on both
TweenService Smoothly animating properties both
UserInputService Keyboard, mouse and touch; the built-in controls player’s device only
DataStoreService Saving data between sessions server only
HttpService Unique ids and JSON both
easings Easing curves both
print, warn Writing to the output both

“Works on both” means the service exists on the server and on every player’s device. What it does there follows the usual rule: changes made on the server replicate to players, and changes made in a LocalScript stay on that player’s device.

The first nine imports are objects in the game tree. game.getService finds the tree services by class name too — game.getService('Players') is the same object as Players — but the libraries below aren’t in the tree, so import them.

The 3D or 2D world. Everything physical lives in it.

  • gravity — studs per second squared, downward. Default 196.2.
  • fallenPartsDestroyHeight — unanchored parts that fall below this height are destroyed, and characters below it die. Default -500.
  • dimension — '3d' or '2d', read-only. Set when the game was created.
  • currentCamera — on a player’s device, the camera. null on the server. See Input and camera.
  • raycast(origin, direction, options?) — the first thing a ray hits, or null. The length of direction is how far the ray goes. Returns the instance it hit (a part, a character or terrain), the position, the surface normal and the distance. Options: ignore (instances the ray passes through) and hitCharacters (default true).
  • getPartsInRadius(center, radius) — the parts that overlap a sphere.
ServerScriptService/FindGround.server.ts
import { print, task, Vector3, workspace } from '@rally/sdk';
task.spawn(async () => {
await task.wait(); // physics queries see the world from the first step on
// Straight down from high above the origin, ignoring characters: where's the ground?
const hit = workspace.raycast(new Vector3(0, 100, 0), new Vector3(0, -200, 0), {
hitCharacters: false,
});
if (hit) print('ground at height', hit.position.y, 'on', hit.instance);
});

Physics covers raycasts, forces and constraints in depth.

Everyone in this server.

  • playerAdded and playerRemoving — fire when someone joins or leaves.
  • getPlayers() — everyone here now.
  • getPlayerByUserId(userId) — the player with that Rally account id, if they’re here.
  • getPlayerFromCharacter(instance) — the player a character belongs to.
  • localPlayer — on a player’s device, that player. null on the server.
  • maxPlayers — how many players fit in this server, read-only. It’s your game’s Max players per server setting.
  • characterAutoLoads — whether characters spawn on their own when players join and after they die. Default true.
  • respawnTime — seconds between a character dying and respawning. Default 3.

Players and characters covers all of it.

How the world is lit. Changes on the server apply for everyone; changes in a LocalScript apply only on that player’s device — a darker sky while they’re in a cave, say.

  • skyColor and ambient — the sky’s color and the light that fills shadows.
  • brightness — the sun’s strength, 0 to 10. Default 1.6.
  • sunDirection — the direction sunlight travels, as a Vector3.
  • fogColor and fogEnd — fog’s color, and the distance at which it’s total. fogEnd 0 turns fog off.
  • shadows — whether things cast shadows.
ServerScriptService/Dusk.server.ts
import { Color3, Lighting, task } from '@rally/sdk';
// Dusk falls over ten seconds.
task.spawn(async () => {
for (let i = 0; i <= 10; i++) {
const t = i / 10;
Lighting.skyColor = Color3.fromHex('#bfdcff').lerp(Color3.fromHex('#ff9b6b'), t);
Lighting.brightness = 1.6 - t;
await task.wait(1);
}
});

For smooth changes, tween the properties instead.

ReplicatedStorage, ServerStorage and ServerScriptService

Section titled “ReplicatedStorage, ServerStorage and ServerScriptService”

Containers, with no members of their own beyond what every instance has.

  • ReplicatedStorage holds what both sides need: RemoteEvents, shared state, templates both sides clone.
  • ServerStorage holds what only the server should have: templates to clone into the world, server-only ModuleScripts. On players’ devices it’s empty.
  • ServerScriptService holds server Scripts. On players’ devices it’s empty.

How games are organized explains what goes in each.

ScreenGuis here are copied into each player’s PlayerGui when they join. Changing StarterGui while the game runs only affects players who join afterwards — to change a player’s UI, change their PlayerGui. See User interface.

Defaults that each new character gets when it spawns:

  • characterWalkSpeed — studs per second. Default 16.
  • characterJumpPower — upward speed when jumping. Default 50.
  • characterMaxHealth — Default 100.

Inside it, StarterPlayerScripts holds the LocalScripts that run on every player’s device; game.getService('StarterPlayerScripts') finds it.

Scheduling on game time. There’s no setTimeout or setInterval in scripts; use these:

  • task.wait(seconds?) — resolves after seconds of game time (or the next step), with the time actually waited. Use it with await.
  • task.delay(seconds, fn) — runs fn later. Returns a function that cancels it.
  • task.spawn(fn) — runs fn now, and reports its errors instead of throwing them. The way to run async code from a script’s top level.
  • task.defer(fn) — runs fn at the start of the next step.
  • task.time() — seconds of game time since the game started.

Tasks and timing covers them in depth.

Events that fire every step of the game, and where the code is running.

  • heartbeat — every step, after physics, with the step’s length in seconds. The usual place for per-step game logic.
  • stepped — every step, before physics.
  • renderStepped — on a player’s device only: every rendered frame, just before drawing. The place for camera and visual updates.
  • isServer() and isClient() — which side this code is running on. In Studio’s Play, where the server and your device’s scripts run together in your browser, both are true.

Animates properties smoothly from their current values to targets.

  • TweenService.create(instance, info, goals) returns a Tween. goals maps property names to target values: numbers, Vector3, Vector2 or Color3.
  • info.time (seconds), info.easing (default 'quadInOut'), info.repeatCount (extra plays; -1 repeats forever), info.reverses (play backwards after each play), info.delay (seconds before starting).
  • A Tween has play(), pause(), cancel(), playing, and a completed event.
ServerScriptService/MovingPlatform.server.ts
import { getChild, Part, TweenService, Vector3, workspace } from '@rally/sdk';
// A platform that slides 20 studs and back, forever.
const platform = getChild(workspace, 'Platform', Part);
TweenService.create(
platform,
{ time: 3, easing: 'sineInOut', reverses: true, repeatCount: -1 },
{ position: platform.position.add(new Vector3(20, 0, 0)) },
).play();

Animation and tweens covers tweens and keyframe animations.

Input on a player’s device. On the server it exists, but its events never fire.

  • inputBegan and inputEnded — a key or mouse button went down or up. Handlers get the input (kind, key, position) and gameProcessed, which is true when the UI used it.
  • isKeyDown(key) — whether a key is held right now, such as 'ShiftLeft'.
  • mouseMoved, mouseLocation and mouseLocked — mouse movement and position, and whether the pointer is locked to the game.
  • touchEnabled — whether the device has a touch screen; lastInputType — 'touch' or 'keyboardMouse', whichever the player used last.
  • addTouchButton(key, label) / removeTouchButton(key) — on-screen buttons for touch players. Keys your scripts listen for get one automatically.
  • viewportSize and safeAreaInsets — the game view’s size and the screen edges covered by a notch or home bar.
  • defaultControls — set it to false to turn off the built-in movement and camera controls.

Input and camera covers input and the camera.

Data that outlives a game server: progress, currencies, high scores. Server only — on players’ devices, every method throws.

  • getDataStore(name) — a store of JSON values by key, with get, set, update, increment and remove. Every method returns a promise.
  • getOrderedDataStore(name) — a store of numbers you can rank with getSorted, for leaderboards.
ServerScriptService/Visits.server.ts
import { DataStoreService, Players, print, warn } from '@rally/sdk';
const visits = DataStoreService.getDataStore('Visits');
Players.playerAdded.connect(async (player) => {
try {
const count = await visits.increment(player.userId);
print(`${player.displayName} has visited ${count} times`);
} catch (e) {
warn('could not count the visit', e);
}
});

Data stores covers saving, loading and leaderboards.

Ids and JSON. Despite its name, it can’t make web requests: games don’t have network access.

  • generateGuid() — a new random unique id string.
  • jsonEncode(value) and jsonDecode(text) — the same as JSON.stringify and JSON.parse.

The easing curves that tweens use: linear, quadIn, quadOut, quadInOut, cubicInOut, sineInOut, backOut, bounceOut and elasticOut. Each is a function that takes progress from 0 to 1 and returns eased progress, which starts at 0 and ends at 1 (backOut and elasticOut overshoot on the way). Use them to shape motion you drive yourself:

ServerScriptService/PopPad.server.ts
import { easings, getChild, Part, RunService, Vector3, workspace } from '@rally/sdk';
// Pop a part up 4 studs over half a second, overshooting a little at the end.
const pad = getChild(workspace, 'Pad', Part);
const start = pad.position;
let t = 0;
const connection = RunService.heartbeat.connect((dt) => {
t = Math.min(1, t + dt / 0.5);
pad.position = start.add(new Vector3(0, 4 * easings.backOut(t), 0));
if (t === 1) connection.disconnect();
});

Write to the game’s output: Studio’s Output panel while you test, and the developer console in the player. print logs normally and warn as a warning. Instances print as their path, like Workspace.Map.Door. See Debugging.

@rally/sdk also has four helpers for everyday jobs:

  • getChild(parent, name, Class) — the child called name, typed as Class. Throws a readable error if it’s missing or another class.
  • waitForChild(parent, name, Class, timeoutSeconds?) — waits until the child exists, then returns it typed as Class. Use it on players’ devices, where objects arrive over time.
  • waitForLocalPlayer() — on a player’s device, resolves with their Player.
  • createLeaderstats(player, { Coins: 0, Wins: 0 }) — on the server, gives a player a leaderstats folder with one NumberValue per entry, shown in the in-game player list. Returns the values by name.
  • script is the Script or LocalScript running the code. In a ModuleScript, it’s the script that imported the module. A Script inside a part uses script.parent to get the part.
  • game is the root of the tree. Besides getService, it has getInstanceById(id), which finds an instance by its id.