Writing a client
Windows · macOS · Android · home
A complete updater, in the order the calls should be made. The shape below is what a host keeping a Roblox client up to date actually needs; adapt the install step to your target.
The loop
- Ask what should be installed.
- Compare against what is installed. Stop if equal.
- Download, following redirects.
- Verify the sha256.
- Install according to
kind. - Record the version you installed.
Compare version, not displayVersion in step 2.
The dotted string is not unique, so two different desktop builds can carry the same one and an
updater keyed on it will skip a real update.
bash
#!/usr/bin/env bash
set -euo pipefail
API="https://www.rbxoffsets.com"
PLATFORM="${1:-windows}" # windows | macos | android
STATE="/var/lib/roblox-tracker/$PLATFORM-version"
mkdir -p "$(dirname "$STATE")"
# 1. what should be installed
meta="$(curl -fsS "$API/api/v1/$PLATFORM/files/latest")"
version="$(printf '%s' "$meta" | jq -r .version)"
kind="$(printf '%s' "$meta" | jq -r .kind)"
name="$(printf '%s' "$meta" | jq -r .fileName)"
want_sha="$(printf '%s' "$meta" | jq -r .sha256)"
# 2. already there? (compare the identity, never displayVersion)
if [ -f "$STATE" ] && [ "$(cat "$STATE")" = "$version" ]; then
echo "up to date: $version"; exit 0
fi
# 3. download - -L is required, the endpoint redirects to Cloudflare
out="/tmp/$name"
curl -fL --retry 3 --retry-delay 5 -o "$out" "$API/download/$PLATFORM/$version"
# 4. verify
if [ -n "$want_sha" ]; then
got="$(sha256sum "$out" | cut -d' ' -f1)"
[ "$got" = "$want_sha" ] || { echo "sha256 mismatch, refusing"; rm -f "$out"; exit 1; }
else
echo "warning: this build is unpinned (no published sha256)"
fi
# 5. install
case "$kind" in
apk) adb install -r "$out" ;;
xapk) tmp="$(mktemp -d)"; unzip -q "$out" -d "$tmp"
adb install-multiple "$tmp"/*.apk ;;
zip) unzip -oq "$out" -d /opt/roblox ;;
esac
# 6. remember
printf '%s' "$version" > "$STATE"
rm -f "$out"
echo "installed $version"
python
import hashlib, requests
API = "https://www.rbxoffsets.com"
PLATFORM = "windows"
meta = requests.get(f"{API}/api/v1/{PLATFORM}/files/latest", timeout=15).json()
with requests.get(f"{API}/download/{PLATFORM}/{meta['version']}", stream=True,
allow_redirects=True, timeout=(15, 600)) as r:
r.raise_for_status()
digest = hashlib.sha256()
with open(meta["fileName"], "wb") as f:
for chunk in r.iter_content(1 << 20):
f.write(chunk)
digest.update(chunk)
if meta["sha256"] and digest.hexdigest() != meta["sha256"]:
raise SystemExit("sha256 mismatch")
Watching every platform at once
curl -s https://www.rbxoffsets.com/api/v1/platforms \
| jq -r '.platforms[] | "\(.label)\t\(.displayVersion)\t\(.releasedAt)"'
Polling politely
The tracker itself checks upstream every 60 seconds, so asking
more often than that learns nothing new. Once every 5 to 15 minutes is plenty
for an updater. The metadata endpoints are database reads and cost this server almost nothing;
/api/v1/{platform}/current may reach upstream, so prefer
/files/latest in a loop and keep /current for the moment you
actually intend to install.
Responses are sent with Cache-Control: no-store. There is no rate limit today.
Set a real User-Agent so an unexpected traffic pattern can be traced to its owner
rather than blocked.
Things that will bite you
- Treating
displayVersionas the identity. It is not unique. Key everything onversion. - Not following redirects. You get a 302 body, not a file, and it looks like a corrupt download.
- Caching the signed URL. It expires in about 60 minutes. Always start from the
/download/path. - Assuming
latestequals the newest Roblox release. It is the newest stored build. When they differ,/api/v1/{platform}/currentexplains why inheldBack. - Falling back to
latestoffsets. Offsets for a neighbouring build are not approximately right. Fail instead. - Ignoring
kind. Anxapkinstalled as a plain APK fails every time. - Treating an empty
sha256as verified. Empty means unknown. Log it. - Sorting Android versions as strings.
2.734.99sorts above2.734.917lexically and is older. Compare the dotted segments as integers, or compareversionCode. Desktop identities do not sort at all — usereleasedAt.