Luau Roblox logo

Luau Roblox

Community
kursku
luau-roblox

Luau and Roblox development patterns for strict typing, Roblox service architecture, client/server security, DataStore persistence, RemoteEvent design, and incremental game systems.

Overview

Publisherkursku
Repositoryskills
Skill nameluau-roblox
Stars
112
Forks
34
Bundled files
Instructions only
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by kursku on GitHub. Read the source before you install it.

Installation

Install the Luau Roblox AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/kursku/skills/tree/master/backend/luau-roblox
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/kursku/skills.git /tmp/skills
mkdir -p .claude/skills
cp -r /tmp/skills/backend/luau-roblox .claude/skills/luau-roblox
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Luau Roblox in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Luau Roblox on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Luau Roblox is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Luau / Roblox Development Skill

Applies to any work inside a Roblox game project written in Luau. Covers correct patterns for strict typing, service architecture, security, persistence, and game-loop design.


1. Mandatory File Header

Every .lua / .luau file must start with:

luau
--!strict

No exceptions. This enables Luau's full type-checker and prevents entire classes of runtime errors. Never omit it, never suppress it with --!nonstrict.


2. Service Acquisition

Always use game:GetService() at the top of the file, before any logic. Never access services through game.ServiceName (breaks in some load orders).

luau
--!strict
local Players       = game:GetService("Players")
local RunService    = game:GetService("RunService")
local DataStoreService = game:GetService("DataStoreService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

3. Module Pattern

Every service / controller / module exports a single table named identically to the file. Always export an init() function for one-time setup called from the bootstrap script.

luau
--!strict
local MyService = {}

-- Called once by the bootstrap (init.server.luau / init.client.luau)
function MyService.init()
    -- setup, bind events, start loops
end

-- Public API
function MyService.doThing(player: Player, value: number): boolean
    -- logic
    return true
end

return MyService

Rules:

  • Module table: PascalCase matching the filename (EconomyService, HUDController)
  • Functions: camelCase (awardKnowledge, tryLevelUp)
  • Constants: SCREAMING_SNAKE_CASE (local MAX_ENERGY = 100)
  • Roblox service refs: PascalCase (local Players = ...)
  • Types: PascalCase (type PlayerData = {...})

4. task.* API — Never Use Deprecated Alternatives

DeprecatedCorrect replacement
wait(n)task.wait(n)
spawn(fn)task.spawn(fn)
delay(n, fn)task.delay(n, fn)
coroutine.wrap(fn)()task.spawn(fn)

wait() is deprecated, inaccurate on frame boundaries, and will be removed. task.* is the only correct async primitive in modern Luau.

Server tick loop example:

luau
task.spawn(function()
    while true do
        task.wait(1)
        MyService.tick()
    end
end)

5. Client / Server Architecture

ContainerScript typeRuns on
ServerScriptServiceScriptServer only
StarterPlayerScriptsLocalScriptClient only
ReplicatedStorageModuleScriptBoth (shared)

The server is always authoritative. The client:

  • Displays UI and shows state
  • Fires RemoteEvents to request actions
  • Never computes economic values — the server does that

Never expose a RemoteFunction from client to server (blocks the thread and is exploitable). Use RemoteEvent only. The server fires back data via a separate event if needed.


6. RemoteEvent Security

Roblox exploiters can fire any RemoteEvent with arbitrary arguments at any time.

Never trust client-provided amounts

luau
-- BAD — exploitable
remoteEvent.OnServerEvent:Connect(function(player, upgradeId, cost)
    playerData.knowledge -= cost
end)

-- GOOD — server looks up the real cost
remoteEvent.OnServerEvent:Connect(function(player, upgradeId)
    local config = UpgradeConfig[upgradeId]
    if not config then return end
    local data = DataService.getPlayerData(player)
    if not data or data.knowledge < config.cost then return end
    data.knowledge -= config.cost
    -- apply effect
end)

Validate all arguments

luau
local function onBuyUpgrade(player: Player, upgradeId: unknown)
    -- type check
    if type(upgradeId) ~= "string" then return end
    -- membership check
    if not UpgradeConfig[upgradeId] then return end
    -- data check
    local data = DataService.getPlayerData(player)
    if not data then return end
    -- economy check
    if data.knowledge < UpgradeConfig[upgradeId].cost then return end
    -- safe to proceed
end

Rate-limit all remotes

luau
local lastFire: {[Player]: number} = {}
local DEBOUNCE = 0.5  -- seconds

remoteEvent.OnServerEvent:Connect(function(player: Player, ...)
    local now = os.clock()
    if lastFire[player] and (now - lastFire[player]) < DEBOUNCE then return end
    lastFire[player] = now
    -- proceed
end)

-- Clean up on player leave to avoid memory leaks
game:GetService("Players").PlayerRemoving:Connect(function(player)
    lastFire[player] = nil
end)

7. DataStore Patterns

Always wrap DataStore calls in pcall. Always log failures with a consistent prefix.

luau
local function saveData(player: Player, data: PlayerData)
    local key = "player_" .. player.UserId
    local ok, err = pcall(function()
        dataStore:SetAsync(key, data)
    end)
    if not ok then
        warn("[DataService] SetAsync failed for " .. key .. ": " .. tostring(err))
    end
end

Merge saved data onto a known default

Never trust that a saved record has all fields (new fields added after initial save, corrupted data, etc.). Always merge onto the default template:

luau
local function mergeWithDefault(saved: {[string]: unknown}, default: PlayerData): PlayerData
    local result = table.clone(default)
    for key, value in saved do
        if result[key] ~= nil and type(value) == type(result[key]) then
            result[key] = value
        end
    end
    return result
end

Save cadence

  • Auto-save every 30 seconds via a task.spawn loop
  • Always save on Players.PlayerRemoving — use task.defer to ensure it fires:
luau
Players.PlayerRemoving:Connect(function(player)
    saveData(player, DataService.getPlayerData(player))
end)

8. Type Definitions (Types.lua)

Define all shared data shapes in src/shared/Types.lua. Import them in every file that uses them.

luau
--!strict
export type PlayerData = {
    knowledge: number,
    energy: number,
    diplomas: number,
    mathLevel: number,
    scienceLevel: number,
    historyLevel: number,
    peLevel: number,
    totalKnowledgeEarned: number,
}

export type UpgradeConfig = {
    id: string,
    displayName: string,
    baseCost: number,
    growth: number,
    bonusPerLevel: number,
}

Always annotate function parameters and return types explicitly — never rely on inference when the function is part of a public API.


9. Config Over Magic Numbers

All gameplay numbers live in config files under src/shared/config/. Never hardcode values in service logic.

luau
-- BAD
local cost = 25 * (1.25 ^ level)

-- GOOD
local cfg = SubjectsConfig[subjectId]
local cost = cfg.baseCost * (cfg.growth ^ level)

Upgrade cost formula: cost = baseCost * (growth ^ level)


10. Error Handling

Use pcall around any throwable operation. Log with the format [ServiceName] message.

luau
local ok, result = pcall(function()
    return dataStore:GetAsync(key)
end)
if not ok then
    warn("[DataService] GetAsync failed: " .. tostring(result))
    return nil
end

11. No Globals

Never write to _G or shared. Pass dependencies as arguments or module-level locals set during init(). This keeps modules testable and avoids order-of-require issues.


12. File Length

Keep files under 300 lines. If a service grows past that, extract a sub-module. Example: StudyService grows large → extract StudyCalculator as a pure-function module.


13. Incremental Game Patterns (School_Incremental Specific)

Economy tick (1 second cadence)

luau
-- In EconomyService.init()
task.spawn(function()
    while true do
        task.wait(1)
        for _, player in Players:GetPlayers() do
            EconomyService.tick(player)
        end
    end
end)

Knowledge gain calculation

Knowledge per study = BaseStudyGain * knowledgeMultiplier

where knowledgeMultiplier accumulates from Subject levels: multiplier += mathLevel * MathConfig.bonusPerLevel

Prestige formula

diplomas = math.floor(totalKnowledgeEarned / PrestigeConfig.baseCost)

Boss reward delivery

Boss rewards are rare currencies. Deliver via EconomyService.awardCurrency, not by direct attribute write, so all validation and logging stays in one place.

Subject upgrade cost

cost = subject.baseCost * (subject.growth ^ currentLevel)

Computed in UpgradeCalculator.lua — never inline this formula in UI or service code.


14. Remotes Naming Convention

All RemoteEvents and RemoteFunctions live in src/shared/Remotes.lua. Name them PascalCase verb-noun: RequestStudy, BuyUpgrade, RequestPrestige, BossDefeated.

luau
-- Remotes.lua
--!strict
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Remotes = ReplicatedStorage:WaitForChild("Remotes")

return {
    RequestStudy    = Remotes:WaitForChild("RequestStudy") :: RemoteEvent,
    BuyUpgrade      = Remotes:WaitForChild("BuyUpgrade")  :: RemoteEvent,
    RequestPrestige = Remotes:WaitForChild("RequestPrestige") :: RemoteEvent,
    SyncPlayerData  = Remotes:WaitForChild("SyncPlayerData")  :: RemoteEvent,
}

Quick Reference

Strict mode:           --!strict  (every file, no exceptions)
Async:                 task.wait / task.spawn / task.delay  (never wait/spawn/delay)
Services:              game:GetService()  (never game.ServiceName)
Module table:          PascalCase, one per file
Functions:             camelCase
Constants:             SCREAMING_SNAKE_CASE
Remotes:               PascalCase verb-noun, all in Remotes.lua
Security:              validate type + membership + economy on every OnServerEvent
Rate limit:            0.5s debounce per player per remote
DataStore:             always pcall, always merge onto default template
Save cadence:          30s auto + PlayerRemoving
File length:           max 300 lines
Magic numbers:         forbidden — use config files
Upgrade cost formula:  baseCost * (growth ^ level)
Prestige formula:      floor(totalKnowledgeEarned / baseCost)
Economy tick:          1 second server loop

Frequently asked questions

What does the Luau Roblox AI skill do?

Luau and Roblox development patterns for strict typing, Roblox service architecture, client/server security, DataStore persistence, RemoteEvent design, and incremental game systems.

Why use Luau Roblox on TypingMind?

Because you install it once and use it with any model. Luau Roblox is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Luau Roblox in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/kursku/skills/tree/master/backend/luau-roblox. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Luau Roblox?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Luau Roblox?

As many as you like. As long as a model supports skills, you can use Luau Roblox with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Luau Roblox AI skill free?

It is published on GitHub by kursku. Check the repository for licensing terms. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇