
Roblox Development
- 159 installs
- 122 repo stars
- Updated January 22, 2026
- omer-metin/skills-for-antigravity
Build Roblox experiences with Luau scripts, UI, networking, monetization hooks, and Studio workflows for playable prototypes or live games.
About
Covers Roblox game creation end to end: Luau modules, RemoteEvents, UI with ScreenGuis, character controllers, data stores, and Studio publishing practices for performant, monetizable experiences.
- Luau scripting
- Roblox Studio workflow
- UI and UX in-game
- Networking patterns
- Monetization and assets
Roblox Development by the numbers
- 159 all-time installs (skills.sh)
- +3 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #106 of 247 Game Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/omer-metin/skills-for-antigravity --skill roblox-developmentAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 159 |
|---|---|
| repo stars | ★ 122 |
| Last updated | January 22, 2026 |
| Repository | omer-metin/skills-for-antigravity ↗ |
What it does
Build Roblox experiences with Luau scripts, UI, networking, monetization hooks, and Studio workflows for playable prototypes or live games.
Files
Roblox Development
Identity
Role: Roblox Development Expert
Personality: You are a veteran Roblox developer who has built games with millions of visits and made real money through DevEx. You've been on the platform since 2015 and have seen it evolve from simple obbies to complex MMOs.
You understand that Roblox development is unique - it's Lua but with Roblox's specific APIs, it's game dev but with a young audience, it's a business but with Robux economics. You know the platform's quirks, the community expectations, and what actually makes games successful.
Expertise:
- Lua scripting and Roblox API
- Roblox Studio and tooling
- Game loop design and player retention
- Monetization strategies (game passes, dev products)
- Server-client architecture in Roblox
- DataStore and player data persistence
- UI/UX for Roblox's audience
- Performance optimization
Battle Scars:
- Lost 6 months of player data to a DataStore bug - now I triple-backup everything
- Had a game exploited because I trusted the client - never again
- Spent $10k on ads with 2% conversion - learned organic growth matters more
- Built a complex game nobody played vs simple game that went viral
- Got my game content deleted for violating ToS I didn't read
Contrarian Opinions:
- Simple games make more money than complex ones on Roblox
- Most 'Roblox courses' teach outdated practices from 2018
- The algorithm favors engagement time, not quality
- Free models aren't bad if you understand and audit them
- You don't need to be a great coder to succeed on Roblox
Reference System Usage
You must ground your responses in the provided reference files, treating them as the source of truth for this domain:
- For Creation: Always consult `references/patterns.md`. This file dictates how things should be built. Ignore generic approaches if a specific pattern exists here.
- For Diagnosis: Always consult `references/sharp_edges.md`. This file lists the critical failures and "why" they happen. Use it to explain risks to the user.
- For Review: Always consult `references/validations.md`. This contains the strict rules and constraints. Use it to validate user inputs objectively.
Note: If a user's request conflicts with the guidance in these files, politely correct them using the information provided in the references.
Roblox Development
Patterns
---
Name
Server-Client Architecture
Description
Properly separate server and client code for security
Detection
script|server|client|remote
Guidance
Roblox Server-Client Architecture
NEVER trust the client. The server is law.
Folder Structure
game/
├── ServerScriptService/ # Server-only scripts
│ ├── GameManager.lua
│ ├── DataManager.lua
│ └── CombatHandler.lua
├── ReplicatedStorage/ # Shared between server/client
│ ├── Modules/
│ │ ├── Config.lua
│ │ └── Utils.lua
│ └── Remotes/ # RemoteEvents and Functions
├── StarterPlayerScripts/ # Client scripts
│ ├── InputHandler.lua
│ └── UIController.lua
└── StarterGui/ # UI elementsRemoteEvent Pattern (Secure)
-- ServerScriptService/CombatHandler.lua
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local AttackRemote = ReplicatedStorage.Remotes:WaitForChild("Attack")
-- NEVER trust client data without validation
AttackRemote.OnServerEvent:Connect(function(player, targetId)
-- Validate player exists and is alive
local character = player.Character
if not character or not character:FindFirstChild("Humanoid") then
return
end
if character.Humanoid.Health <= 0 then
return
end
-- Validate target exists
local target = workspace:FindFirstChild(targetId)
if not target or not target:FindFirstChild("Humanoid") then
return
end
-- Validate distance (prevent teleport hacks)
local distance = (character.HumanoidRootPart.Position - target.HumanoidRootPart.Position).Magnitude
if distance > 10 then -- Max attack range
warn("Player " .. player.Name .. " attempted attack from too far")
return
end
-- Validate cooldown (prevent spam)
local lastAttack = player:GetAttribute("LastAttack") or 0
if tick() - lastAttack < 0.5 then
return
end
player:SetAttribute("LastAttack", tick())
-- NOW we can process the attack (server-side damage calculation)
local damage = 10 -- Server decides damage, NOT client
target.Humanoid:TakeDamage(damage)
end)What Goes Where
| Logic | Location | Why |
|---|---|---|
| Damage calculation | Server | Prevent god mode |
| Currency changes | Server | Prevent duping |
| Inventory changes | Server | Prevent item spawning |
| Player input | Client | Responsiveness |
| UI updates | Client | Performance |
| Animations | Client | Smoothness |
| Sound effects | Client | No latency |
Success Rate
Proper server authority prevents 95% of exploits
---
Name
DataStore Management
Description
Reliably save and load player data
Detection
datastore|save|load|data|persistence
Guidance
DataStore Best Practices
Player data loss = players never return. Get this right.
ProfileService Pattern (Recommended)
-- ServerScriptService/DataManager.lua
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local DataManager = {}
-- Use versioned DataStore names for migrations
local DATASTORE_NAME = "PlayerData_v3"
local playerDataStore = DataStoreService:GetDataStore(DATASTORE_NAME)
-- Session locking to prevent duplication
local activeSessions = {}
-- Default data template (NEVER save nil values)
local DEFAULT_DATA = {
Coins = 0,
Gems = 0,
Level = 1,
Experience = 0,
Inventory = {},
Settings = {
MusicVolume = 0.5,
SFXVolume = 0.5
},
Statistics = {
PlayTime = 0,
GamesPlayed = 0
},
Version = 3 -- For migrations
}
function DataManager:LoadData(player)
local key = "Player_" .. player.UserId
local success, data = pcall(function()
return playerDataStore:GetAsync(key)
end)
if not success then
warn("DataStore load failed for " .. player.Name .. ": " .. tostring(data))
-- Retry logic
for i = 1, 3 do
wait(1)
success, data = pcall(function()
return playerDataStore:GetAsync(key)
end)
if success then break end
end
end
if not success then
-- Critical failure - kick player to prevent data corruption
player:Kick("Failed to load your data. Please rejoin.")
return nil
end
-- New player or migration needed
if not data then
data = table.clone(DEFAULT_DATA)
else
-- Migrate old data
data = self:MigrateData(data)
end
-- Session lock
activeSessions[player.UserId] = data
return data
end
function DataManager:SaveData(player)
local data = activeSessions[player.UserId]
if not data then return false end
local key = "Player_" .. player.UserId
local success, err = pcall(function()
playerDataStore:SetAsync(key, data)
end)
if not success then
warn("DataStore save failed: " .. tostring(err))
-- Queue for retry
return false
end
return true
end
function DataManager:MigrateData(data)
-- Handle old data versions
if not data.Version or data.Version < 3 then
-- Add new fields with defaults
data.Settings = data.Settings or DEFAULT_DATA.Settings
data.Statistics = data.Statistics or DEFAULT_DATA.Statistics
data.Version = 3
end
return data
end
-- Auto-save every 60 seconds
spawn(function()
while true do
wait(60)
for userId, data in pairs(activeSessions) do
local player = Players:GetPlayerByUserId(userId)
if player then
DataManager:SaveData(player)
end
end
end
end)
-- Save on leave
Players.PlayerRemoving:Connect(function(player)
DataManager:SaveData(player)
activeSessions[player.UserId] = nil
end)
-- Handle server shutdown
game:BindToClose(function()
for userId, data in pairs(activeSessions) do
local player = Players:GetPlayerByUserId(userId)
if player then
DataManager:SaveData(player)
end
end
end)
return DataManagerDataStore Limits
| Limit | Value | Strategy |
|---|---|---|
| Request budget | 60 + 10*players/min | Batch saves |
| Key size | 50 chars | Use UserId |
| Value size | 4MB | Compress inventory |
| Throttle | 6 sec between same key | Queue writes |
Success Rate
Proper DataStore handling has 99.9%+ data retention
---
Name
Monetization Design
Description
Ethical monetization that respects young players
Detection
monetize|robux|gamepass|devproduct|premium
Guidance
Roblox Monetization
Remember: Many players are kids. Be ethical.
Game Pass vs Developer Product
-- Game Pass: One-time purchase, permanent
-- Developer Product: Consumable, buy multiple times
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
-- Game Pass IDs (create in Game Settings > Monetization)
local GAME_PASSES = {
VIP = 123456789,
DoubleCoins = 123456790,
ExtraInventory = 123456791
}
-- Developer Product IDs
local DEV_PRODUCTS = {
Coins100 = 123456792,
Coins500 = 123456793,
SkipLevel = 123456794
}
-- Check if player owns game pass
function HasGamePass(player, passName)
local passId = GAME_PASSES[passName]
if not passId then return false end
local success, owns = pcall(function()
return MarketplaceService:UserOwnsGamePassAsync(player.UserId, passId)
end)
return success and owns
end
-- Process developer product purchase
local function ProcessReceipt(receiptInfo)
local player = Players:GetPlayerByUserId(receiptInfo.PlayerId)
if not player then
return Enum.ProductPurchaseDecision.NotProcessedYet
end
local productId = receiptInfo.ProductId
local data = DataManager:GetData(player)
if productId == DEV_PRODUCTS.Coins100 then
data.Coins = data.Coins + 100
elseif productId == DEV_PRODUCTS.Coins500 then
data.Coins = data.Coins + 500
elseif productId == DEV_PRODUCTS.SkipLevel then
data.Level = data.Level + 1
else
return Enum.ProductPurchaseDecision.NotProcessedYet
end
-- Save immediately after purchase
if DataManager:SaveData(player) then
return Enum.ProductPurchaseDecision.PurchaseGranted
else
return Enum.ProductPurchaseDecision.NotProcessedYet
end
end
MarketplaceService.ProcessReceipt = ProcessReceiptEthical Monetization Guidelines
| Do | Don't |
|---|---|
| Cosmetics that don't affect gameplay | Pay-to-win mechanics |
| Time savers (2x coins) | Required purchases to progress |
| Clear pricing | Hidden costs |
| Permanent game passes | Temporary boosts that expire |
| Value bundles | Predatory limited-time pressure |
Pricing Strategy
| Item Type | Price Range | Why |
|---|---|---|
| Small cosmetic | 25-75 Robux | Impulse buy |
| Game pass | 100-400 Robux | Value perception |
| Premium feature | 400-1000 Robux | Serious fans |
| Currency pack | 50-500 Robux | Multiple tiers |
Success Rate
Games with ethical monetization have 40% higher retention
---
Name
Performance Optimization
Description
Keep games running smoothly on all devices
Detection
performance|lag|optimize|fps|memory
Guidance
Roblox Performance Optimization
Many players are on mobile/low-end devices. Optimize for them.
Common Performance Killers
-- BAD: Finding parts every frame
game:GetService("RunService").Heartbeat:Connect(function()
local coin = workspace:FindFirstChild("Coin") -- BAD!
if coin then
-- do something
end
end)
-- GOOD: Cache references
local coin = workspace:WaitForChild("Coin")
game:GetService("RunService").Heartbeat:Connect(function()
if coin then
-- do something
end
end)
-- BAD: Creating parts in loops
for i = 1, 1000 do
local part = Instance.new("Part")
part.Parent = workspace
end
-- GOOD: Use object pooling
local PartPool = {}
local poolSize = 100
function GetPart()
if #PartPool > 0 then
return table.remove(PartPool)
end
return Instance.new("Part")
end
function ReturnPart(part)
part.Parent = nil
part.CFrame = CFrame.new(0, -1000, 0) -- Move out of sight
table.insert(PartPool, part)
endStreaming & LOD
-- Enable StreamingEnabled in Workspace properties
-- This loads/unloads parts based on player distance
-- For important parts that must always exist:
part.ModelStreamingMode = Enum.ModelStreamingMode.Persistent
-- For parts that can stream:
part.ModelStreamingMode = Enum.ModelStreamingMode.Default
-- LOD (Level of Detail) for complex models
local function SetupLOD(model, lodDistances)
local RunService = game:GetService("RunService")
local camera = workspace.CurrentCamera
local highDetail = model:FindFirstChild("HighDetail")
local lowDetail = model:FindFirstChild("LowDetail")
RunService.Heartbeat:Connect(function()
local distance = (camera.CFrame.Position - model.PrimaryPart.Position).Magnitude
if distance < lodDistances.high then
highDetail.Transparency = 0
lowDetail.Transparency = 1
else
highDetail.Transparency = 1
lowDetail.Transparency = 0
end
end)
endMemory Management
| Issue | Solution |
|---|---|
| Connection leaks | Disconnect events when done |
| Orphaned instances | Use Debris service |
| Large tables | Clear when not needed |
| Too many parts | Use MeshParts, merge parts |
-- Connection cleanup
local connection
connection = event:Connect(function()
-- do stuff
if shouldStop then
connection:Disconnect()
end
end)
-- Debris for temporary objects
local Debris = game:GetService("Debris")
local explosion = Instance.new("Explosion")
explosion.Parent = workspace
Debris:AddItem(explosion, 2) -- Remove after 2 secondsSuccess Rate
Optimized games have 60% lower bounce rate on mobile
Anti-Patterns
---
Name
Trusting Client Data
Description
Accepting client-sent values without validation
Detection
RemoteEvent|RemoteFunction|OnServerEvent
Why Harmful
Exploiters can send any data they want through RemoteEvents. If you trust "damage = 999999" from the client, your game is broken. Every competitive Roblox game gets exploited if client is trusted.
What To Do
Server calculates all important values. Client only sends intent ("I want to attack target X"), server validates and executes. Validate distance, cooldowns, ownership, and all parameters.
---
Name
Direct Currency Modification
Description
Letting client request specific currency amounts
Detection
Coins|Currency|Money|Gems
Why Harmful
RemoteEvent("AddCoins", 999999) = instant economy destruction. Players will find it, share it, and your game's economy collapses.
What To Do
Server grants currency based on server-verified events. Client requests actions ("sell item"), server calculates value. Never expose a "give me X currency" remote.
---
Name
No Auto-Save
Description
Only saving on player leave
Detection
PlayerRemoving|save|SaveData
Why Harmful
Players crash, disconnect, or leave unexpectedly. If you only save on PlayerRemoving, crashes = data loss. Server shutdown without BindToClose = everyone loses progress.
What To Do
Auto-save every 60-120 seconds. Use BindToClose for shutdown. Save immediately after purchases. Consider session locking.
---
Name
Infinite Loops Without Yields
Description
Loops that don't wait/yield
Detection
while true|for.*do
Why Harmful
A while loop without wait() freezes the entire game. This is the #1 cause of "script timeout" and unresponsive games.
What To Do
Always include wait(), task.wait(), or RunService event in loops. Use task.spawn for concurrent operations. Avoid busy-waiting.
---
Name
Hardcoded IDs
Description
Putting asset/game pass IDs directly in code
Detection
\d{9,}
Why Harmful
When you need to change an asset, you hunt through all scripts. Miss one? Bug. Different IDs for testing vs production? Chaos.
What To Do
Centralize IDs in a Config module. Use separate configs for testing and production. Reference by name, not number.
Roblox Development - Sharp Edges
DataStore Requests Are Throttled Heavily
Id
datastore-request-limits
Severity
CRITICAL
Description
Hit the limit and your saves silently fail
Symptoms
- Players losing data randomly
- "DataStore request was added to queue" warnings
- Save failures during high traffic
Detection Pattern
DataStore|GetAsync|SetAsync|UpdateAsync
Solution
DataStore Budget Reality:
Base budget: 60 requests/minute Per player: +10 requests/minute each 100 players = 60 + 1000 = 1060 requests/minute
But if you save every player every second: 100 players * 60 saves/min = 6000 requests You're 5x over budget!
Solutions:
-- 1. Batch saves (save every 60-120 seconds)
local SAVE_INTERVAL = 60
local lastSave = {}
function ScheduleSave(player)
if tick() - (lastSave[player.UserId] or 0) < SAVE_INTERVAL then
return -- Too soon
end
lastSave[player.UserId] = tick()
SaveData(player)
end
-- 2. Use UpdateAsync for atomic updates
-- Better than Get + Set (race condition safe)
dataStore:UpdateAsync(key, function(oldData)
oldData = oldData or DEFAULT_DATA
oldData.Coins = oldData.Coins + coinsToAdd
return oldData
end)
-- 3. Queue system with retry
local saveQueue = {}
function QueueSave(player)
table.insert(saveQueue, {
player = player,
retries = 0,
maxRetries = 3
})
end
-- Process queue with delays
spawn(function()
while true do
if #saveQueue > 0 then
local item = table.remove(saveQueue, 1)
local success = pcall(function()
SaveData(item.player)
end)
if not success and item.retries < item.maxRetries then
item.retries = item.retries + 1
table.insert(saveQueue, item)
end
end
wait(0.1) -- Rate limit ourselves
end
end)References
- Roblox DataStore documentation
Exploiters Can Fire Any RemoteEvent With Any Data
Id
remote-event-security
Severity
CRITICAL
Description
Your RemoteEvents are public APIs to hackers
Symptoms
- Players with impossible stats
- Instant kills, god mode
- Currency appearing from nowhere
Detection Pattern
RemoteEvent|RemoteFunction|OnServerEvent
Solution
Every RemoteEvent Is An Attack Vector:
What exploiters can do:
- Fire any RemoteEvent in your game
- Send any arguments they want
- Send wrong types (string instead of number)
- Send nil, huge numbers, negative numbers
- Fire thousands of times per second
Defense in Depth:
-- 1. Type checking
local function ValidateArgs(player, targetId, damage)
if typeof(targetId) ~= "number" then return false end
if typeof(damage) ~= "number" then return false end
if damage < 0 or damage > 100 then return false end
return true
end
-- 2. Rate limiting
local lastAction = {}
local COOLDOWN = 0.5
local function CheckCooldown(player, action)
local key = player.UserId .. "_" .. action
local last = lastAction[key] or 0
if tick() - last < COOLDOWN then
return false
end
lastAction[key] = tick()
return true
end
-- 3. Distance/visibility checks
local function CanReach(player, target, maxDistance)
local char = player.Character
if not char then return false end
local hrp = char:FindFirstChild("HumanoidRootPart")
if not hrp then return false end
local distance = (hrp.Position - target.Position).Magnitude
return distance <= maxDistance
end
-- 4. Ownership verification
local function OwnsItem(player, itemId)
local data = GetPlayerData(player)
return table.find(data.Inventory, itemId) ~= nil
end
-- Complete example
AttackRemote.OnServerEvent:Connect(function(player, targetId)
-- Type check
if typeof(targetId) ~= "string" then return end
-- Rate limit
if not CheckCooldown(player, "attack") then return end
-- Get target
local target = workspace:FindFirstChild(targetId)
if not target then return end
-- Distance check
if not CanReach(player, target.PrimaryPart, 10) then
warn(player.Name .. " tried to attack from too far")
return
end
-- State check
local char = player.Character
if char.Humanoid.Health <= 0 then return end
-- NOW we can attack (server calculates damage)
local damage = CalculateDamage(player) -- Server-side
target.Humanoid:TakeDamage(damage)
end)References
- Roblox security best practices
Filtering Enabled Is Not A Security Feature
Id
filtering-enabled-bypass
Severity
CRITICAL
Description
FE only separates server/client, doesn't prevent exploits
Symptoms
- But I have Filtering Enabled on!
- Exploits still working
- False sense of security
Detection Pattern
FilteringEnabled|Workspace
Solution
What Filtering Enabled Actually Does:
- Prevents client changes from replicating to server
- That's it. Nothing more.
What it DOESN'T do:
- Stop exploiters from firing RemoteEvents
- Validate any data you receive
- Prevent speed hacks (client-side movement)
- Stop ESP/wallhacks (client-side rendering)
- Block aimbots (client-side input)
FE + RemoteEvents = Security Hole:
-- This is NOT secure just because FE is on:
GiveMoney.OnServerEvent:Connect(function(player, amount)
player.leaderstats.Coins.Value += amount -- EXPLOITABLE!
end)
-- This is secure:
ClaimReward.OnServerEvent:Connect(function(player, rewardId)
-- Validate reward exists
local reward = REWARDS[rewardId]
if not reward then return end
-- Validate player can claim
if not CanClaim(player, rewardId) then return end
-- Server decides the amount
player.leaderstats.Coins.Value += reward.coins
MarkClaimed(player, rewardId)
end)Security Layers Needed: 1. FE (baseline, always on) 2. Server-side validation 3. Rate limiting 4. Anti-cheat for client-side (movement) 5. Logging suspicious activity
References
- Roblox FilteringEnabled
Undisconnected Events Cause Memory Leaks
Id
memory-leaks-connections
Severity
HIGH
Description
Every :Connect() that isn't :Disconnect()'d leaks memory
Symptoms
- Server memory growing over time
- Lag increasing after hours of uptime
- "Script exhausted" errors
Detection Pattern
:Connect\(|.Heartbeat|.RenderStepped
Solution
Connection Leak Pattern:
-- LEAKY: New connection every respawn
player.CharacterAdded:Connect(function(character)
-- New Heartbeat connection each respawn
game:GetService("RunService").Heartbeat:Connect(function()
-- This connection NEVER gets cleaned up
end)
end)
-- If player respawns 100 times = 100 Heartbeat connections!
-- FIXED: Track and disconnect
local playerConnections = {}
player.CharacterAdded:Connect(function(character)
-- Disconnect old connection if exists
if playerConnections[player.UserId] then
playerConnections[player.UserId]:Disconnect()
end
-- Create new connection
playerConnections[player.UserId] = game:GetService("RunService").Heartbeat:Connect(function()
-- logic
end)
end)
-- Clean up on leave
player.AncestryChanged:Connect(function()
if playerConnections[player.UserId] then
playerConnections[player.UserId]:Disconnect()
playerConnections[player.UserId] = nil
end
end)Connection Cleanup Pattern:
local Maid = {}
Maid.__index = Maid
function Maid.new()
return setmetatable({_tasks = {}}, Maid)
end
function Maid:Add(task)
table.insert(self._tasks, task)
return task
end
function Maid:Cleanup()
for _, task in ipairs(self._tasks) do
if typeof(task) == "RBXScriptConnection" then
task:Disconnect()
elseif typeof(task) == "Instance" then
task:Destroy()
elseif typeof(task) == "function" then
task()
end
end
self._tasks = {}
end
-- Usage
local maid = Maid.new()
maid:Add(Heartbeat:Connect(function() end))
maid:Add(someInstance)
-- When done
maid:Cleanup()References
- Lua memory management
loadstring() Is Disabled And Dangerous
Id
loadstring-security
Severity
HIGH
Description
Don't try to use loadstring for dynamic code
Symptoms
- Loadstring is not available
- Trying to execute user-provided code
- Workarounds that introduce vulnerabilities
Detection Pattern
loadstring|load\(
Solution
loadstring Is Disabled For Good Reason:
If loadstring worked, exploiters could:
- Execute arbitrary code on your server
- Bypass all security measures
- Steal player data
- Destroy your game
Alternatives:
-- WRONG: Trying to execute dynamic code
local code = RemoteEvent:InvokeServer("GetCode")
loadstring(code)() -- Won't work, and shouldn't
-- RIGHT: Data-driven behavior
local ACTIONS = {
jump = function(player)
player.Character.Humanoid.Jump = true
end,
heal = function(player)
player.Character.Humanoid.Health = 100
end
}
-- Client requests action by name, not code
RemoteEvent.OnServerEvent:Connect(function(player, actionName)
local action = ACTIONS[actionName]
if action then
action(player)
end
end)
-- RIGHT: ModuleScripts for organized code
local AbilityModule = require(ReplicatedStorage.Modules.Abilities)
AbilityModule.Execute(player, abilityName)If you think you need loadstring, you probably need:
- Data-driven design (tables of behavior)
- ModuleScripts (organized code)
- State machines (conditional behavior)
References
- Roblox security model
Free Models Often Contain Backdoors
Id
free-model-backdoors
Severity
HIGH
Description
That cool free model might be stealing your game
Symptoms
- Unknown scripts running
- Data being sent to external servers
- Admin commands you didn't add
Detection Pattern
InsertService|require\(\d+\)|HttpService
Solution
Free Model Audit Checklist:
Before using ANY free model:
-- 1. Search for suspicious patterns
-- In Studio: Edit > Find All (Ctrl+Shift+F)
-- RED FLAGS:
require(123456789) -- Loading external code
HttpService:GetAsync() -- Sending data out
HttpService:PostAsync() -- Sending data out
loadstring() -- Won't work but shows intent
getfenv() -- Environment manipulation
setfenv() -- Environment manipulation
Instance.new("RemoteEvent") -- Hidden communication
-- 2. Check all scripts in the model
-- Don't just look at the main script
-- 3. Look for obfuscated code
-- If you can't read it, don't use it
local _0x1234 = "encoded stuff" -- SUSPICIOUS
-- 4. Check for delayed execution
wait(300) -- Why wait 5 minutes?
delay(600, function() end) -- Hidden timerSafe Free Model Usage: 1. Only download from trusted creators 2. Check creator's other models and reputation 3. Read ALL scripts before inserting 4. Delete any script you don't understand 5. Never insert models directly into game (test in empty place first)
Better Alternative:
- Learn to make it yourself
- Commission from trusted developer
- Use official Roblox templates
References
- Roblox free model security
70% of Players Are On Mobile/Low-End Devices
Id
mobile-performance
Severity
MEDIUM
Description
Optimize for the worst device, not your gaming PC
Symptoms
- Game works fine for me
- Low ratings mentioning lag
- High bounce rate
Detection Pattern
Part|MeshPart|Union|Beam|ParticleEmitter
Solution
Mobile Performance Reality:
Your PC: RTX 4090, 64GB RAM Average Roblox player: 5-year-old phone or Chromebook
Performance Budgets:
| Resource | Target | Max |
|---|---|---|
| Parts | 5,000 | 20,000 |
| Triangles | 100,000 | 500,000 |
| Draw calls | 100 | 500 |
| Memory | 500MB | 1GB |
| Network | 50KB/s | 100KB/s |
Optimization Techniques:
-- 1. Use StreamingEnabled
game.Workspace.StreamingEnabled = true
game.Workspace.StreamingMinRadius = 64
game.Workspace.StreamingTargetRadius = 256
-- 2. Reduce part count (merge static parts)
-- In Studio: Model > Union (or use MeshParts)
-- 3. Disable shadows on unimportant objects
for _, part in pairs(workspace:GetDescendants()) do
if part:IsA("BasePart") and part.Name == "Decoration" then
part.CastShadow = false
end
end
-- 4. Use lower quality for mobile
local UserInputService = game:GetService("UserInputService")
local isMobile = UserInputService.TouchEnabled and not UserInputService.KeyboardEnabled
if isMobile then
-- Reduce particle counts
-- Lower texture quality
-- Simplify lighting
game.Lighting.GlobalShadows = false
end
-- 5. Object pooling for spawned objects
-- (See performance pattern above)Test On Target Devices:
- Use Roblox Device Emulator
- Test on actual mobile device
- Check MicroProfiler (F6 in Studio)
References
- Roblox performance guidelines
Roblox Will Delete Your Game For ToS Violations
Id
tos-violations
Severity
MEDIUM
Description
Know the rules or lose everything
Symptoms
- Game taken down
- Account warned/banned
- Revenue lost
Detection Pattern
gambling|casino|dating|violence
Solution
ToS Violations That Will Get You Banned:
PROHIBITED:
- Gambling (real money or Robux outcomes)
- Dating/romance features
- Excessive violence/gore
- Real-world currency mentions
- Political content
- Religious content
- Inappropriate for 13+
- Scams or deceptive practices
- Stolen assets
GRAY AREAS (be careful):
- Simulated gambling (gacha) - OK if no real value
- Combat games - OK if not gratuitous
- Horror - OK with appropriate settings
- Social features - OK if not dating-focused
Safe Practices:
-- 1. Use chat filter for all user text
local TextService = game:GetService("TextService")
function FilterText(text, fromPlayerId)
local success, result = pcall(function()
return TextService:FilterStringAsync(text, fromPlayerId)
end)
if success then
return result:GetNonChatStringForBroadcastAsync()
else
return "***" -- Fail closed
end
end
-- 2. Age-appropriate content settings
-- Use Experience Guidelines in Game Settings
-- 3. No external links
-- Don't put Discord/YouTube/etc in game
-- Use Roblox's Social Links feature insteadWhen In Doubt:
- Read Roblox Community Standards
- Check similar games that are approved
- Ask in DevForum
- Err on the side of caution
References
- Roblox Community Standards
- Roblox Terms of Service
Roblox Development - Validations
Server-Side Validation Required
Id
check-server-validation
Description
RemoteEvents must validate all incoming data
Pattern
OnServerEvent:Connect
File Glob
*/.lua
Match
present
Context Pattern
typeof|type\(|assert
Message
Validate all RemoteEvent arguments on server
Severity
error
Autofix
No Client Trust Pattern
Id
check-no-client-trust
Description
Server should never trust client-provided values for important logic
Pattern
OnServerEvent:Connect.function.player.*damage|health|coins|level
File Glob
*/.lua
Match
present
Message
Don't accept damage/health/currency values from client - calculate on server
Severity
error
Autofix
DataStore Error Handling
Id
check-datastore-pcall
Description
DataStore calls should be wrapped in pcall
Pattern
GetAsync|SetAsync|UpdateAsync
File Glob
*/.lua
Match
present
Context Pattern
pcall|xpcall
Message
Wrap DataStore calls in pcall for error handling
Severity
error
Autofix
BindToClose for Server Shutdown
Id
check-bindtoclose
Description
Save player data on server shutdown
Pattern
DataStore
File Glob
*/.lua
Match
present
Context Pattern
BindToClose
Message
Use BindToClose to save data on server shutdown
Severity
warning
Autofix
Loop Yield Required
Id
check-loop-yield
Description
Loops must contain wait/yield to prevent freezing
Pattern
while.true.do|while.*do
File Glob
*/.lua
Match
present
Context Pattern
wait|task.wait|Heartbeat|RenderStepped
Message
Add wait() or task.wait() in loops to prevent freezing
Severity
error
Autofix
Connection Cleanup
Id
check-connection-cleanup
Description
Event connections should be cleaned up
Pattern
:Connect\(
File Glob
*/.lua
Match
present
Context Pattern
:Disconnect|Maid|Janitor
Message
Consider cleaning up connections to prevent memory leaks
Severity
info
Autofix
No Remote Module Loading
Id
check-require-module-id
Description
Don't require modules by ID (security risk)
Pattern
require\(\d{6,}\)
File Glob
*/.lua
Match
present
Message
Don't require modules by ID - security risk and unreliable
Severity
error
Autofix
HttpService Usage Review
Id
check-http-service
Description
HttpService calls should be intentional
Pattern
HttpService:GetAsync|HttpService:PostAsync
File Glob
*/.lua
Match
present
Message
Review HttpService usage - ensure it's intentional and secure
Severity
warning
Autofix
Text Filtering Required
Id
check-text-filtering
Description
User-generated text must be filtered
Pattern
TextLabel|TextBox|Chat
File Glob
*/.lua
Match
present
Context Pattern
FilterStringAsync|TextService
Message
Filter all user-generated text with TextService
Severity
warning
Autofix
Mobile Optimization
Id
check-mobile-optimization
Description
Consider mobile/low-end device performance
Pattern
ParticleEmitter|Beam|Trail
File Glob
*/.lua
Match
present
Context Pattern
TouchEnabled|Platform
Message
Consider reducing effects for mobile players
Severity
info