Scripting and Luau¶
Nova runs a Luau VM. That means modern Luau syntax is accepted at runtime, not just by the editor:
local total = 0
for _, value in { 1, 2, 3 } do
total += value
end
local function classify(n: number): string
if n > 10 then
return "large"
elseif n > 3 then
return "medium"
end
return "small"
end
for i = 1, 5 do
if i == 3 then
continue
end
print(classify(i))
end
Type annotations are erased at runtime. They are useful for tooling and for catching mistakes in Studio, but they do not change behavior.
Standard libraries¶
| Library | Status | Notes |
|---|---|---|
task |
Supported | spawn, defer, delay, wait, cancel, and the rest. |
bit32 |
Supported | |
utf8 |
Supported | |
math |
Supported | Includes math.round, math.clamp, and math.log(x, base). |
string |
Supported | Includes string.split. |
table |
Supported | Includes table.find, table.clear, table.clone. |
os |
Supported | os.clock is a high-resolution wall clock; os.date follows modern Luau. |
coroutine |
Supported | |
debug |
Partial | A restricted library is registered; some stock Lua debug APIs are unavailable. |
buffer |
Not yet | Use strings/tables, or wait for the backport. |
vector |
Not yet | Use Vector3/Vector2. |
package |
Not loaded | Use ModuleScript and require. |
The task library is the preferred scheduler API. The legacy wait, spawn,
and delay globals still exist for old code.
task.spawn(function()
print("runs immediately")
end)
task.delay(2, function()
print("runs after two seconds")
end)
local elapsed = task.wait(1)
Globals and services¶
The usual Roblox globals are present:
local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local camera = Workspace.CurrentCamera
print(workspace, game, script, typeof(player), player:IsA("Player"))
Instance.new, Enum, Vector3, CFrame, Color3, UDim2, TweenInfo,
BrickColor, Ray, Region3, and the other common constructors are available.
See the datatype index.
Instance helpers¶
Attributes, tags, and property-change signals are supported:
local part = workspace:WaitForChild("Door")
part:SetAttribute("Locked", true)
print(part:GetAttribute("Locked"))
part:AddTag("Interactable")
print(part:HasTag("Interactable"))
part:GetPropertyChangedSignal("Anchored"):Connect(function()
print("Anchored is now", part.Anchored)
end)
CollectionService is the supported way to work with tags across scripts:
local CollectionService = game:GetService("CollectionService")
for _, instance in CollectionService:GetTagged("Interactable") do
print(instance:GetFullName())
end
CollectionService:GetInstanceAddedSignal("Interactable"):Connect(function(instance)
print("added", instance)
end)
Security and sandboxing¶
Nova keeps the classic security model. A normal game script cannot call APIs marked:
PluginSecurity— Studio plugins only.LocalUserSecurity— local user / CoreScript contexts.RobloxScriptSecurity— Roblox/CoreScript internals.RobloxSecurity— engine internals.
Trying to call one produces a message such as Insufficient permissions. The
API reference marks restricted members with their security level; treat them as
unavailable unless you are writing a plugin or CoreScript.
HttpService.HttpEnabled is LocalUserSecurity: the deployment host enables
HTTP for game servers. If HttpService:GetAsync says HTTP is not enabled, that
is a host configuration issue, not a script bug.
loadstring exists but is sandboxed. getfenv/setfenv are legacy and
restricted. Prefer ModuleScript and explicit parameters.
Modules¶
require works with ModuleScript instances:
-- ReplicatedStorage/Modules/MathUtil
local MathUtil = {}
function MathUtil.clamp01(value: number): number
return math.clamp(value, 0, 1)
end
return MathUtil
local MathUtil = require(game:GetService("ReplicatedStorage"):WaitForChild("Modules"):WaitForChild("MathUtil"))
print(MathUtil.clamp01(1.5))
Keep shared modules in ReplicatedStorage, server-only modules in
ServerScriptService or ServerStorage, and client-only modules in
StarterPlayerScripts.
Error handling¶
pcall and xpcall work as expected. Use them around network calls, data-store
calls, and anything that can fail because an asset or instance is missing:
local ok, result = pcall(function()
return game:GetService("DataStoreService"):GetDataStore("Example"):GetAsync("key")
end)
if not ok then
warn("data store read failed:", result)
end
Common language differences from old Roblox code¶
| Old pattern | Modern / Nova pattern |
|---|---|
wait(1) |
task.wait(1) |
spawn(f) |
task.spawn(f) |
delay(1, f) |
task.delay(1, f) |
findFirstChild(name) |
FindFirstChild(name) |
IsPlaying |
Playing |
Pitch |
PlaybackSpeed |
MinDistance / MaxDistance |
RollOffMinDistance / RollOffMaxDistance |
game.Workspace |
workspace or game:GetService("Workspace") |
for _, v in next, t |
for _, v in t (Luau) or for _, v in pairs(t) |
Legacy lowercase aliases usually still exist, but new code should use the modern names.
Editor support¶
NovaStudio uses the Luau parser for analysis and autocomplete. If a script shows a warning that looks wrong, check whether the construct is actually valid in the Luau VM first; the analyzer and the VM share the same grammar.