Utility Modules
Global utility ala Roblox yang nempel di runtime — task, crypto, datetime, uuid, random, websocket, dan regex. Surface tipis, function-based (kecuali yang emang butuh state), jadi snippet Roblox bisa di-paste tanpa rewrite.
task — Roblox-compat scheduler
Map ke primitive yang udah ada (runThread / removeThread / sleep) jadi script copy-paste dari Roblox jalan tanpa rewrite. Catatan: task.wait pake detik (float), sleep pake milidetik — pilih vocabulary mana pun yang cocok.
task.wait(seconds?)
| Field | Type |
|---|---|
| Signature | (seconds: number?) → number |
| Returns | detik yang di-sleep (default 0 buat single-tick yield) |
| Async | yes |
task.wait(0.5) -- yield setengah detik
task.wait() -- single-tick yield
task.spawn(fn, ...)
| Field | Type |
|---|---|
| Signature | (fn: (...any) → (), ...any) → number |
| Returns | thread handle id, dipake buat task.cancel |
local id = task.spawn(function(x, y) print(x + y) end, 2, 3)
task.delay(seconds, fn, ...)
Run fn(args...) setelah seconds lewat. Return handle id buat task.cancel.
task.defer(fn, ...)
Sama kayak spawn tapi single-tick yield dulu — jalan di resumption point berikutnya.
task.cancel(id)
Abort task by handle id. Return true kalau handle match task yang masih hidup.
crypto — hash + encoding
Input/output byte string (Lua string itu binary-safe, bukan UTF-8). Decoder raise kalau input invalid — typo silent gak ke-hide.
| Function | Returns |
|---|---|
crypto.sha256(input) | hex digest, 64 char |
crypto.sha512(input) | hex digest, 128 char |
crypto.hexEncode(bytes) | lower-case hex |
crypto.hexDecode(hex) | raw bytes |
crypto.base64Encode(input) | standard base64 (padded =) |
crypto.base64Decode(b64) | raw bytes |
crypto.base64UrlEncode(input) | URL-safe base64 (no padding) |
crypto.base64UrlDecode(b64) | raw bytes |
print(crypto.sha256("hello"))
--> "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
local b = crypto.base64Encode("hello world")
print(b) --> "aGVsbG8gd29ybGQ="
print(crypto.base64Decode(b)) --> "hello world"
Belum ada HMAC / signing primitive. Tambah pas script beneran butuh.
datetime — wall-clock helpers
Isi gap yang os.time() / os.date(...) Lua stdlib gak cover: presisi millisecond, ISO-8601 round-trip, konversi UTC vs local eksplisit.
Quick reference
| Function | Returns |
|---|---|
datetime.unixSeconds() | UTC unix detik saat ini |
datetime.unixMillis() | UTC unix milidetik saat ini |
datetime.iso() | "2026-05-05T08:55:07Z" |
datetime.format(fmt?) | strftime UTC, default ISO-8601 |
datetime.formatLocal(fmt?) | strftime LOCAL TZ |
datetime.formatUnix(ts, fmt?) | format unix-second timestamp tertentu |
datetime.parseIso(s) | unix detik, atau nil kalau gagal |
datetime.monotonicMillis() | wall-clock millis since epoch |
Round-trip
local original = "2026-05-05T08:55:07Z"
local secs = datetime.parseIso(original)
local back = datetime.formatUnix(secs) -- default ISO-8601
assert(back == original)
parseIso return nil kalau gagal — script bisa guard tanpa pcall.
uuid — UUID v4
print(uuid.new()) --> "f47ac10b-58cc-4372-a567-0e02b2c3d479"
print(uuid.new("simple")) --> "f47ac10b58cc4372a5670e02b2c3d479"
print(uuid.new("urn")) --> "urn:uuid:f47ac10b-..."
Cryptographically random (UUID v4). Pas buat: per-script run id, idempotency key, filename ephemeral, correlation id di HTTP request.
random — RNG yang lebih ok dari math.random
Dua surface:
- Stateless — pake default RNG process-wide non-deterministic.
random.new(seed)userdata — deterministic stream buat run reproducible.
Stateless
print(random.integer(1, 6)) -- inclusive [1, 6]
print(random.number()) -- [0, 1) float
print(random.number(10)) -- [0, 10)
print(random.number(5, 10)) -- [5, 10)
print(random.choice({"a","b"}))
local salt = random.bytes(16) -- 16-byte random string
local t = {1, 2, 3, 4, 5}
random.shuffle(t) -- in place
Seeded (deterministic)
local rng = random.new(42)
for i = 1, 5 do
print(rng:integer(1, 100)) -- selalu sequence yang sama dengan seed=42
end
RandomState punya method yang sama: :integer, :number, :bytes, :choice, :shuffle.
websocket — async client
Surface minimal Roblox-style. connect return (ws, err) jadi script bisa if err then return end tanpa pcall.
websocket.connect(url) → (ws, err)
| Field | Type |
|---|---|
| Signature | (url: string) → (WebSocket?, string?) |
| Returns | (ws, nil) kalau sukses, (nil, err) kalau gagal |
| Accepts | ws:// dan wss:// |
local ws, err = websocket.connect("wss://echo.websocket.org")
if err then return print("connect gagal:", err) end
ws:on("message", function(text) print("recv:", text) end)
ws:on("close", function(code, reason) print("closed:", code, reason) end)
ws:send("hello")
sleep(1000)
ws:close()
Method di ws
| Method | Arg | Effect |
|---|---|---|
ws:send(text) | string | send Text frame |
ws:sendBinary(bytes) | string | send Binary frame (raw bytes) |
ws:close() | — | tutup koneksi (idempotent) |
ws:isClosed() | — | true setelah close (lokal atau peer) |
ws:on(event, fn) | lihat tabel | subscribe event inbound |
Event
| Event | Callback signature |
|---|---|
"message" | (text: string) — Text frame |
"binary" | (bytes: string) — Binary frame |
"close" | (code: number, reason: string) — peer close |
"error" | (message: string) — read-side error |
Catatan
- Gak ada auto-reconnect / backoff / heartbeat. Script yang butuh wrap
connectdi loop sendiri. tungsteniteudah handle ping/pong internal — code lo gak ngeliat itu.- Tiap koneksi spawn 1 tokio task buat read + 1 buat write. Tear-down pas
close()atau peer close.
regex — PCRE-style pattern
string.match / string.gmatch Lua bawaan pake Lua pattern yang minimal (gak ada | alternation, character class cuma %w/%d dll, gak ada quantifier group). Module ini expose Rust regex crate jadi script bisa pake engine yang lo udah tau dari bahasa lain.
regex.new(pattern) → (re, err)
Compile sekali, reuse murah. Return (nil, err) kalau syntax error.
local re, err = regex.new([[(\d{4})-(\d{2})-(\d{2})]])
if err then return print(err) end
local m = re:find("today is 2026-05-05 thanks")
-- m = { match = "2026-05-05", start = 10, ["end"] = 19,
-- groups = { "2026", "05", "05" } }
Method di Regex compiled
| Method | Returns |
|---|---|
re:isMatch(haystack) | boolean |
re:find(haystack) | match-table pertama, atau nil |
re:findAll(haystack) | {RegexMatch} — semua non-overlapping match |
re:gmatch(haystack) | iterator — for m in re:gmatch(s) do ... end |
re:replace(haystack, replacement) | string ke-replace (dollar-form $1/$2) |
re:split(haystack) | {string} |
re:pattern() | source pattern asli |
Shape match-table
{
match = "2026-05-05", -- substring full yang match
start = 10, -- 1-based byte offset char pertama
["end"] = 19, -- 1-based byte offset char terakhir (inclusive)
groups = { "2026", "05", "05" }, -- 1-indexed capture group
}
Index matching string.find Lua (1-based, end-inclusive). Group optional yang gak match dateng sebagai nil.
One-shot helper
Buat compare cepet tanpa nyimpen handle compiled:
print(regex.isMatch([[(?i)hello]], "Hello World")) --> true
print(regex.find([[\d+]], "abc 42 def").match) --> "42"
print(regex.replace([[\s+]], " a b c", " ")) --> " a b c"
for _, word in ipairs(regex.split([[\W+]], "hello, world!")) do
print(word)
end
regex.escape(s)
Neutralize semua metachar jadi s match literal. Pake pas lo splice input user ke pattern:
local needle = "1.2.3+x"
local pat = regex.escape(needle)
print(regex.isMatch(pat, "find 1.2.3+x here")) --> true
print(regex.isMatch(pat, "find 1X2X3+x here")) --> false
Replace dengan capture group
replace pake dollar-form group references (regex crate convention), BUKAN Lua %1:
local re = regex.new([[(\w+)@(\w+)]])
print(re:replace("alice@example, bob@test", "$2/$1"))
--> "example/alice, test/bob"
Kapan pake regex.new vs one-shot
- Hot loop / pattern repeat:
regex.new(p)compile sekali, reuse. - One-off scrub:
regex.replace(p, h, r)dll compile per call — fine buat cold path.
Inline flag
Crate regex support inline flag group: (?i) case-insensitive, (?s) . match newline, (?m) multi-line, (?x) verbose. Combine: (?im)hello dst.
Recipe gabungan — signed-payload echo bot
Sign tiap outbound pake HMAC-style crypto.sha256(secret + payload), attach UUID correlation id, forward response balik ke script:
local SECRET = "shhh"
local ws, err = websocket.connect("wss://echo.websocket.org")
if err then return print(err) end
ws:on("message", function(reply)
print(datetime.iso(), "<", reply)
end)
for i = 1, 5 do
local cid = uuid.new("simple")
local body = string.format('{"id":"%s","i":%d,"ts":%d}', cid, i, datetime.unixSeconds())
local sig = crypto.sha256(SECRET .. body)
ws:send(body .. " sig=" .. sig)
task.wait(0.5)
end
task.wait(1)
ws:close()