(Note: Player shave to chose what age group they are in as you can’t know someone’s age group)
what this system actually does
this setup lets players pick an age group manually, then it:
- puts them into a queue for that group
- finds or creates a private server just for that group
- keeps servers from overfilling
- rotates servers when they get old or full
- avoids race conditions when multiple players click at once
- retries teleports if roblox does a roblox moment
everything runs from one Script. no extra assets needed.
step 1: where to put the script
- open Roblox Studio
- go to ServerScriptService
- create a Script (not LocalScript)
- paste the entire code in there
- make sure the game is published (teleports wont work in unpublished games)
thats it. dont split it. dont rename anything inside it.
step 2: what happens when a player joins
when a player joins:
- the script auto creates a ScreenGui
- adds 3 buttons (Under13, Teen, Adult)
- injects a LocalScript directly into the gui
- the buttons lock after clicking so players cant spam
you do not need to make a gui yourself unless you want custom visuals.
step 3: how age groups actually work (important)
roblox does not let you read real age. this system does NOT try to.
these are just self selected groups, think of them like:
- play style
- maturity preference
- community choice
do NOT say things like:
- “verified age”
- “real age servers”
- “minors/adults only”
use neutral wording in your game description or UI if you change text later.
step 4: server logic (how it decides where to send players)
for each age group:
- it stores server data in a DataStore
- also keeps a live queue using MemoryStore
- each server has:
- a max player limit
- a creation timestamp
- if a server is full or too old → new one is created
- players are teleported in small batches
this avoids:
- 20 players being sent to a dead server
- everyone racing to create servers at once
- datastore corruption (mostly)
step 5: testing it (do this properly)
you cant test this with 1 player and expect magic.
best way:
- publish the game
- start Start Server + Start Player in studio
- open multiple test clients
- click different buttons
- watch servers split correctly
if teleports fail in studio, thats normal sometimes. live game is more reliable.
step 6: common mistakes people make
these will break it:
- putting the script anywhere other than ServerScriptService
- renaming the RemoteEvent
- trying to move the LocalScript out of the gui
- testing in an unpublished place
- assuming roblox age is involved (its not)
also dont spam click in studio, teleport throttling is real.
step 7: customizing it (safe changes)
things you can change safely:
- button text
- age group names (just keep them consistent)
- MAX_SERVER_SIZE
- SERVER_TTL
- teleport retry count
- gui colors / sizes
if you add new groups, you MUST:
- add a button
- add it to the processing loop
- use the same string everywhere
step 8: moderation & tos reality check
be smart about presentation.
this system is allowed only if:
- players choose themselves
- no claims about real age verification
- no exclusionary language in rules
if you market it wrong, roblox moderation wont care how clean your code is.
step 9: when this system is actually worth using
use this if:
- your game has social interaction
- different maturity levels cause issues
- you want softer separation without hard rules
dont use it if:
- the game is solo
- age doesnt matter at all
- you expect it to enforce safety (it doesnt)
final dev advice (real talk)
this is already way more advanced than 95% of roblox games.
most devs stop at TeleportService:Teleport() and call it a day.
code:
local Players = game:GetService("Players")
local TeleportService = game:GetService("TeleportService")
local DataStoreService = game:GetService("DataStoreService")
local MemoryStoreService = game:GetService("MemoryStoreService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local PLACE_ID = game.PlaceId
local MAX_SERVER_SIZE = 20
local SERVER_TTL = 3600
local TELEPORT_RETRIES = 3
local ServerStore = DataStoreService:GetDataStore("AgeGroupServersV3")
local QueueMap = MemoryStoreService:GetSortedMap("AgeGroupQueuesV3")
local LockMap = MemoryStoreService:GetSortedMap("AgeGroupLocksV3")
local event = Instance.new("RemoteEvent")
event.Name = "AgeGroupSelected"
event.Parent = ReplicatedStorage
local function acquireLock(key)
return pcall(function()
LockMap:SetAsync(key, os.time(), 5)
end)
end
local function releaseLock(key)
pcall(function()
LockMap:RemoveAsync(key)
end)
end
local function getServerData(group)
local data
pcall(function()
data = ServerStore:GetAsync(group)
end)
if type(data) ~= "table" then
return nil
end
if os.time() - data.created > SERVER_TTL then
return nil
end
return data
end
local function saveServerData(group, data)
pcall(function()
ServerStore:SetAsync(group, data)
end)
end
local function createServer(group)
local code = TeleportService:ReserveServer(PLACE_ID)
local data = {
code = code,
count = 0,
created = os.time()
}
saveServerData(group, data)
return data
end
local function claimSlot(group)
local data = getServerData(group)
if not data or data.count >= MAX_SERVER_SIZE then
data = createServer(group)
end
data.count += 1
saveServerData(group, data)
return data.code
end
local function teleport(player, code)
for i = 1, TELEPORT_RETRIES do
local ok = pcall(function()
TeleportService:TeleportToPrivateServer(
PLACE_ID,
code,
{ player }
)
end)
if ok then
return true
end
task.wait(i)
end
return false
end
local function processGroup(group)
if not acquireLock(group) then
return
end
local entries
pcall(function()
entries = QueueMap:GetRangeAsync(group, 1, 10)
end)
if not entries then
releaseLock(group)
return
end
for _, item in ipairs(entries) do
local userId = tonumber(item.value)
local player = Players:GetPlayerByUserId(userId)
if player then
local code = claimSlot(group)
teleport(player, code)
end
pcall(function()
QueueMap:RemoveAsync(group, item.id)
end)
end
releaseLock(group)
end
event.OnServerEvent:Connect(function(player, group)
pcall(function()
QueueMap:SetAsync(group, player.UserId, os.time(), 60)
end)
end)
task.spawn(function()
while true do
task.wait(2)
processGroup("Under13")
processGroup("Teen")
processGroup("Adult")
end
end)
Players.PlayerAdded:Connect(function(player)
local gui = Instance.new("ScreenGui")
gui.Name = "AgeSelectGui"
gui.ResetOnSpawn = false
gui.Parent = player:WaitForChild("PlayerGui")
local function makeButton(text, y, name)
local b = Instance.new("TextButton")
b.Size = UDim2.fromScale(0.45, 0.12)
b.Position = UDim2.fromScale(0.275, y)
b.Text = text
b.Name = name
b.BackgroundColor3 = Color3.fromRGB(35, 35, 35)
b.TextColor3 = Color3.new(1, 1, 1)
b.Parent = gui
end
makeButton("Under 13", 0.25, "Under13")
makeButton("Teen", 0.4, "Teen")
makeButton("Adult", 0.55, "Adult")
local ls = Instance.new("LocalScript")
ls.Parent = gui
ls.Source = [[
local rs = game:GetService("ReplicatedStorage")
local event = rs:WaitForChild("AgeGroupSelected")
local gui = script.Parent
local locked = false
local function lock()
if locked then return false end
locked = true
for _, v in ipairs(gui:GetChildren()) do
if v:IsA("TextButton") then
v.AutoButtonColor = false
v.TextTransparency = 0.4
end
end
return true
end
gui.Under13.MouseButton1Click:Connect(function()
if lock() then
event:FireServer("Under13")
end
end)
gui.Teen.MouseButton1Click:Connect(function()
if lock() then
event:FireServer("Teen")
end
end)
gui.Adult.MouseButton1Click:Connect(function()
if lock() then
event:FireServer("Adult")
end
end)
]]
end)
RunService.Heartbeat:Connect(function()
for _, group in ipairs({ "Under13", "Teen", "Adult" }) do
local data = getServerData(group)
if data and data.count <= 0 then
pcall(function()
ServerStore:RemoveAsync(group)
end)
end
end
end)
hope yall have a good day ![]()