code test






How to Make Admin Panel Roblox Studio (Copy & Paste Guide)


Roblox Tutorial

How to Make Admin Panel Roblox Studio (Copy & Paste)

This step-by-step guide shows you exactly how to make admin panel roblox studio with a modern UI, global announcements, fly/invisible/invincible toggles, and a server-wide luck system. All three scripts are included below in tidy, scrollable embeds with a one-click copy button.

Target keyword: how to make admin panel roblox studio

What you’ll build

  • Clean, scrollable Admin UI
  • Global announcement toasts
  • Fly / Invisible / Invincible
  • Server Luck ×2 with timer HUD

Why it matters

Knowing how to make admin panel roblox studio helps you moderate, test, and manage features quickly without manual commands.

Prerequisites

  • Roblox Studio installed
  • RemoteEvents & GUI basics
  • LocalScripts / Server Scripts
  • ModuleScripts, DataStores, MessagingService

Step 1 — RemoteEvents Setup (ReplicatedStorage)

Create these RemoteEvents in ReplicatedStorage:

  1. AdminGlobalMessage
  2. GlobalMessage
  3. AdminAction
  4. LuckUpdate
  5. AdminClient

Tip: Exact names are critical when you learn how to make admin panel roblox studio.

Step 2 — AdminPanelClient (LocalScript)

Place at StarterPlayer ➜ StarterPlayerScripts ➜ AdminPanelClient.

-- AdminPanelClient — clean settings UI with scrolling Admin page
-- Features: Announcement, Server Luck, Fly/Invisible/Invincible, Toast, Luck HUD, drag bar, F2 to open

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TweenService      = game:GetService("TweenService")
local UserInputService  = game:GetService("UserInputService")
local Players           = game:GetService("Players")
local RunService        = game:GetService("RunService")

local player        = Players.LocalPlayer
local EVT_SEND_ANN  = ReplicatedStorage:WaitForChild("AdminGlobalMessage")
local EVT_BROADCAST = ReplicatedStorage:WaitForChild("GlobalMessage")
local EVT_ADMIN_ACT = ReplicatedStorage:WaitForChild("AdminAction")
local EVT_LUCK_PUSH = ReplicatedStorage:WaitForChild("LuckUpdate")
local EVT_ADMIN_CL  = ReplicatedStorage:WaitForChild("AdminClient")

-- ---------- theme ----------
local C = {
	bg=Color3.fromRGB(14,14,18), card=Color3.fromRGB(23,23,29),
	stroke=Color3.fromRGB(38,38,46), text=Color3.fromRGB(235,238,245),
	sub=Color3.fromRGB(160,168,188), blue=Color3.fromRGB(60,110,230),
	green=Color3.fromRGB(90,170,95), gray=Color3.fromRGB(120,120,130)
}
local FT = Enum.Font.Gotham
local FB = Enum.Font.GothamBold

-- ---------- toast ----------
local toastGui = Instance.new("ScreenGui")
toastGui.Name, toastGui.IgnoreGuiInset, toastGui.ResetOnSpawn, toastGui.DisplayOrder = "Toast", true, false, 1500
toastGui.Parent = player:WaitForChild("PlayerGui")

local TOAST_W, TOAST_H, TOAST_Y = 520, 58, 0.32
local toast = Instance.new("Frame")
toast.Size = UDim2.new(0,TOAST_W,0,TOAST_H)
toast.Position = UDim2.new(0.5,-TOAST_W/2,TOAST_Y,0)
toast.Visible = false
toast.BackgroundColor3 = C.card
toast.Parent = toastGui
Instance.new("UICorner", toast).CornerRadius = UDim.new(0,10)
do local s=Instance.new("UIStroke",toast) s.Color=C.stroke s.Thickness=1 end

local toastLabel = Instance.new("TextLabel")
toastLabel.BackgroundTransparency = 1
toastLabel.Size = UDim2.new(1,-24,1,0)
toastLabel.Position = UDim2.new(0,12,0,0)
toastLabel.Font = FB; toastLabel.TextScaled = true
toastLabel.TextColor3 = C.text
toastLabel.Parent = toast

local function showToast(text, color)
	toastLabel.Text = text; toastLabel.TextColor3 = color or C.text
	toast.Visible = true
	toast.Position = UDim2.new(0.5,-TOAST_W/2,TOAST_Y+0.03,0)
	toast.BackgroundTransparency = 0.35
	toastLabel.TextTransparency = 1
	TweenService:Create(toast,TweenInfo.new(0.18,Enum.EasingStyle.Quad,Enum.EasingDirection.Out),
		{Position=UDim2.new(0.5,-TOAST_W/2,TOAST_Y,0), BackgroundTransparency=0.1}):Play()
	TweenService:Create(toastLabel,TweenInfo.new(0.18),{TextTransparency=0}):Play()
	task.wait(2)
	TweenService:Create(toast,TweenInfo.new(0.18,Enum.EasingStyle.Quad,Enum.EasingDirection.In),
		{BackgroundTransparency=0.5}):Play()
	TweenService:Create(toastLabel,TweenInfo.new(0.18),{TextTransparency=1}):Play()
	task.wait(0.2); toast.Visible=false
end
EVT_BROADCAST.OnClientEvent:Connect(showToast)

-- ---------- main panel ----------
local gui = Instance.new("ScreenGui")
gui.Name, gui.IgnoreGuiInset, gui.ResetOnSpawn, gui.DisplayOrder, gui.Enabled =
	"ControlPanel", true, false, 2000, false
gui.Parent = player.PlayerGui

local PANEL_W, PANEL_H = 900, 580
local panel = Instance.new("Frame")
panel.Size = UDim2.new(0,PANEL_W,0,PANEL_H)
panel.Position = UDim2.new(0.5,-PANEL_W/2,0.5,-PANEL_H/2)
panel.BackgroundColor3 = C.bg
panel.Parent = gui
Instance.new("UICorner", panel).CornerRadius = UDim.new(0,12)
do local s=Instance.new("UIStroke",panel) s.Color=C.stroke s.Thickness=1 end
do local p=Instance.new("UIPadding",panel) p.PaddingTop=UDim.new(0,14) p.PaddingLeft=UDim.new(0,14) p.PaddingRight=UDim.new(0,14) end

local title = Instance.new("TextLabel")
title.BackgroundTransparency=1; title.Size=UDim2.new(1,-48,0,42)
title.Text="Settings"; title.Font=FB; title.TextScaled=true; title.TextXAlignment=Enum.TextXAlignment.Left
title.TextColor3=C.text; title.Parent=panel

local closeBtn = Instance.new("TextButton")
closeBtn.Size=UDim2.new(0,36,0,36); closeBtn.Position=UDim2.new(1,-44,0,2)
closeBtn.Text="X"; closeBtn.TextScaled=true; closeBtn.Font=FB
closeBtn.BackgroundColor3=Color3.fromRGB(140,50,50); closeBtn.TextColor3=Color3.new(1,1,1)
Instance.new("UICorner",closeBtn).CornerRadius=UDim.new(0,10)
closeBtn.Parent=panel
closeBtn.MouseButton1Click:Connect(function() gui.Enabled=false end)

-- tabs
local tabs = Instance.new("Frame")
tabs.Position=UDim2.new(0,0,0,52); tabs.Size=UDim2.new(1,0,0,42); tabs.BackgroundTransparency=1; tabs.Parent=panel
local function makeTab(text, x)
	local b=Instance.new("TextButton"); b.Size=UDim2.new(0,180,1,0); b.Position=UDim2.new(0,x,0,0)
	b.Text=text; b.TextScaled=true; b.Font=FB; b.TextColor3=C.text; b.BackgroundColor3=C.card; b.Parent=tabs
	Instance.new("UICorner",b).CornerRadius=UDim.new(0,10)
	do local s=Instance.new("UIStroke",b) s.Color=C.stroke s.Thickness=1 end
	return b
end
local tabAnn = makeTab("Announcement", 0)
local tabAdmin = makeTab("Admin", 190)

local content = Instance.new("Frame")
content.Position=UDim2.new(0,0,0,100); content.Size=UDim2.new(1,0,1,-128)
content.BackgroundTransparency=1; content.Parent=panel

local pageAnn = Instance.new("Frame"); pageAnn.Size=UDim2.new(1,0,1,0); pageAnn.BackgroundTransparency=1; pageAnn.Parent=content
local pageAdmin = Instance.new("Frame"); pageAdmin.Size=UDim2.new(1,0,1,0); pageAdmin.BackgroundTransparency=1; pageAdmin.Visible=false; pageAdmin.Parent=content

local function showPage(w)
	pageAnn.Visible = (w=="ann"); pageAdmin.Visible=(w=="admin")
	tabAnn.BackgroundColor3 = (w=="ann") and C.blue or C.card
	tabAdmin.BackgroundColor3 = (w=="admin") and C.blue or C.card
end
tabAnn.MouseButton1Click:Connect(function() showPage("ann") end)
tabAdmin.MouseButton1Click:Connect(function() showPage("admin") end)
showPage("ann")

-- drag bar
local dragBar = Instance.new("Frame")
dragBar.Size=UDim2.new(1,0,0,8); dragBar.Position=UDim2.new(0,0,1,-8); dragBar.BackgroundColor3=Color3.new(1,1,1)
dragBar.Parent=panel
local function makeDraggable(handle,target)
	local dragging=false; local start; local startPos
	local function upd(input)
		local d = input.Position - start
		target.Position = UDim2.fromOffset(startPos.X.Offset + d.X, startPos.Y.Offset + d.Y)
	end
	handle.InputBegan:Connect(function(i)
		if i.UserInputType==Enum.UserInputType.MouseButton1 or i.UserInputType==Enum.UserInputType.Touch then
			dragging=true; start=i.Position; startPos=target.Position
			i.Changed:Connect(function() if i.UserInputState==Enum.UserInputState.End then dragging=false end end)
		end
	end)
	handle.InputChanged:Connect(function(i)
		if dragging and (i.UserInputType==Enum.UserInputType.MouseMovement or i.UserInputType==Enum.UserInputType.Touch) then
			upd(i)
		end
	end)
end
makeDraggable(dragBar, panel)

-- F2 toggle
UserInputService.InputBegan:Connect(function(input,gpe)
	if gpe then return end
	if input.KeyCode==Enum.KeyCode.F2 then gui.Enabled = not gui.Enabled end
end)

-- ---------- helpers (cards/rows) ----------
local function card(parent, titleText, height)
	local f=Instance.new("Frame"); f.Size=UDim2.new(1,0,0,height); f.BackgroundColor3=C.card; f.Parent=parent
	Instance.new("UICorner",f).CornerRadius=UDim.new(0,12)
	do local s=Instance.new("UIStroke",f) s.Color=C.stroke s.Thickness=1 end
	local pad=Instance.new("UIPadding",f); pad.PaddingTop=UDim.new(0,14); pad.PaddingLeft=UDim.new(0,14); pad.PaddingRight=UDim.new(0,14)

	local t=Instance.new("TextLabel"); t.BackgroundTransparency=1; t.Size=UDim2.new(1,0,0,26)
	t.Text=titleText; t.TextScaled=true; t.Font=FB; t.TextXAlignment=Enum.TextXAlignment.Left; t.TextColor3=C.text; t.Parent=f

	local list=Instance.new("UIListLayout", f); list.Padding=UDim.new(0,10); list.SortOrder=Enum.SortOrder.LayoutOrder
	t.LayoutOrder=0
	return f
end

local function row(parent, main, sub)
	local f=Instance.new("Frame"); f.Name="Row"; f.Size=UDim2.new(1,0,0,64); f.BackgroundColor3=C.card
	f.Parent=parent; f.LayoutOrder=1
	Instance.new("UICorner",f).CornerRadius=UDim.new(0,10)
	do local s=Instance.new("UIStroke",f) s.Color=C.stroke s.Thickness=1 end
	local pad=Instance.new("UIPadding",f); pad.PaddingTop=UDim.new(0,12); pad.PaddingLeft=UDim.new(0,12); pad.PaddingRight=UDim.new(0,12)

	local left=Instance.new("Frame"); left.BackgroundTransparency=1; left.Size=UDim2.new(1,-260,1,0); left.Parent=f
	local title=Instance.new("TextLabel"); title.BackgroundTransparency=1; title.Size=UDim2.new(1,0,0,26)
	title.Text=main; title.TextScaled=true; title.Font=FB; title.TextXAlignment=Enum.TextXAlignment.Left; title.TextColor3=C.text; title.Parent=left
	local desc=Instance.new("TextLabel"); desc.BackgroundTransparency=1; desc.Position=UDim2.new(0,0,0,26); desc.Size=UDim2.new(1,0,0,20)
	desc.Text=sub or ""; desc.TextScaled=true; desc.Font=FT; desc.TextXAlignment=Enum.TextXAlignment.Left; desc.TextColor3=C.sub; desc.Parent=left

	local right=Instance.new("Frame"); right.BackgroundTransparency=1; right.Size=UDim2.new(0,240,1,0); right.Position=UDim2.new(1,-240,0,0); right.Parent=f
	return f, right
end

local function pill(parent, text, color, cb)
	local b=Instance.new("TextButton"); b.Size=UDim2.new(0,120,0,36); b.Position=UDim2.new(1,-120,0.5,-18)
	b.BackgroundColor3=color; b.TextColor3=Color3.new(1,1,1); b.TextScaled=true; b.Font=FB; b.Text=text; b.Parent=parent
	Instance.new("UICorner",b).CornerRadius=UDim.new(0,18)
	b.MouseButton1Click:Connect(function() if cb then cb() end end)
	return b
end

-- ---------- Announcement page ----------
do
	local a = card(pageAnn, "Announcement", 190)

	local r, right = row(a, "Global message", "Broadcast a small popup to all players")

	local input = Instance.new("TextBox")
	input.Size = UDim2.new(1,-130,0,36)          -- BIGGER input
	input.Position = UDim2.new(0,0,0.5,-18)
	input.BackgroundColor3 = Color3.fromRGB(32,32,40)
	input.TextColor3 = C.text; input.PlaceholderText = "type an announcement…"
	input.TextScaled = true; input.ClearTextOnFocus = false; input.Font = FT
	input.Parent = right
	Instance.new("UICorner",input).CornerRadius=UDim.new(0,10)
	do local s=Instance.new("UIStroke",input) s.Color=C.stroke s.Thickness=1 end

	pill(right, "Send", C.blue, function()
		local txt = input.Text
		if txt and #txt > 0 then EVT_SEND_ANN:FireServer(txt, nil); input.Text = "" end
	end)
end

-- ---------- Admin page (scrollable) ----------
local adminCard = card(pageAdmin, "Admin", 430)

-- make a ScrollingFrame inside the card for many rows
local scroll = Instance.new("ScrollingFrame")
scroll.Size = UDim2.new(1,-0,1,-46)
scroll.Position = UDim2.new(0,0,0,46)
scroll.BackgroundTransparency = 1
scroll.ScrollBarThickness = 6
scroll.AutomaticCanvasSize = Enum.AutomaticSize.Y
scroll.CanvasSize = UDim2.new()
scroll.Parent = adminCard

local list = Instance.new("UIListLayout", scroll)
list.Padding = UDim.new(0,10)
list.SortOrder = Enum.SortOrder.LayoutOrder

-- row: Server Luck
do
	local r, right = row(scroll, "Server Luck ×2", "Doubles global luck; stacks and resets the 5:00 timer")
	pill(right, "Activate", C.green, function() EVT_ADMIN_ACT:FireServer("DoubleLuck") end)
end

-- row: Target player
local targetBox
do
	local r, right = row(scroll, "Target player", "Leave blank to target yourself")
	targetBox = Instance.new("TextBox")
	targetBox.Size = UDim2.new(1,0,0,36)
	targetBox.Position = UDim2.new(0,0,0.5,-18)
	targetBox.BackgroundColor3 = Color3.fromRGB(32,32,40)
	targetBox.TextColor3 = C.text; targetBox.PlaceholderText="name (optional)"
	targetBox.TextScaled = true; targetBox.ClearTextOnFocus=false; targetBox.Font=FT
	targetBox.Parent = right
	Instance.new("UICorner",targetBox).CornerRadius=UDim.new(0,10)
	do local s=Instance.new("UIStroke",targetBox) s.Color=C.stroke s.Thickness=1 end
end

-- row: Fly
do
	local r, right = row(scroll, "Fly (toggle)", "Grants flight to the target player")
	pill(right, "Toggle", C.blue, function()
		EVT_ADMIN_ACT:FireServer("FlyToggle", {target = targetBox.Text})
	end)
end

-- row: Invisible
do
	local r, right = row(scroll, "Invisible (toggle)", "Hide character parts/decals for everyone")
	pill(right, "Toggle", C.gray, function()
		EVT_ADMIN_ACT:FireServer("InvisibleToggle", {target = targetBox.Text})
	end)
end

-- row: Invincible
do
	local r, right = row(scroll, "Invincible (toggle)", "Locks Health to MaxHealth")
	pill(right, "Toggle", C.green, function()
		EVT_ADMIN_ACT:FireServer("InvincibleToggle", {target = targetBox.Text})
	end)
end

-- ---------- Luck HUD ----------
local hud = Instance.new("ScreenGui")
hud.Name="LuckHUD"; hud.IgnoreGuiInset=true; hud.ResetOnSpawn=false; hud.DisplayOrder=1100; hud.Parent=player.PlayerGui
local hudFrame = Instance.new("Frame")
hudFrame.Size=UDim2.new(0,210,0,60); hudFrame.Position=UDim2.new(1,-220,1,-70)
hudFrame.BackgroundColor3=C.card; hudFrame.Visible=false; hudFrame.Parent=hud
Instance.new("UICorner",hudFrame).CornerRadius=UDim.new(0,10)
do local s=Instance.new("UIStroke",hudFrame) s.Color=C.stroke s.Thickness=1 end
local hudLabel = Instance.new("TextLabel")
hudLabel.BackgroundTransparency=1; hudLabel.Size=UDim2.new(1,-16,0,24); hudLabel.Position=UDim2.new(0,8,0,6)
hudLabel.Font=FB; hudLabel.TextScaled=true; hudLabel.TextColor3=Color3.fromRGB(120,220,120); hudLabel.Text="luck"; hudLabel.Parent=hudFrame
local hudTimer = Instance.new("TextLabel")
hudTimer.BackgroundTransparency=1; hudTimer.Size=UDim2.new(1,-16,0,22); hudTimer.Position=UDim2.new(0,8,0,32)
hudTimer.Font=FT; hudTimer.TextScaled=true; hudTimer.TextColor3=C.text; hudTimer.Text="00:00"; hudTimer.Parent=hudFrame

local currentMult, secondsLeft, lastTick = 1, 0, 0
local function fmtTime(s) s = math.max(0, math.floor(s)); return string.format("%02d:%02d", math.floor(s/60), s%60) end
local function refreshHUD()
	if secondsLeft > 0 and currentMult > 1 then
		hudFrame.Visible = true
		hudLabel.Text = ("luck  x%d"):format(currentMult)
		hudTimer.Text = fmtTime(secondsLeft)
	else
		hudFrame.Visible = false
	end
end
EVT_LUCK_PUSH.OnClientEvent:Connect(function(mult, secs) currentMult, secondsLeft = mult, secs; refreshHUD() end)
RunService.RenderStepped:Connect(function(dt)
	if secondsLeft > 0 then
		secondsLeft = math.max(0, secondsLeft - dt)
		if math.floor(secondsLeft) ~= lastTick then lastTick = math.floor(secondsLeft); refreshHUD() end
	end
end)

-- ---------- Local Fly controller ----------
local flying=false; local lv,att,ao; local move=Vector3.zero; local up,down=0,0
local function stopFly()
	flying=false; if lv then lv:Destroy(); lv=nil end; if ao then ao:Destroy(); ao=nil end; if att then att:Destroy(); att=nil end
	local ch=player.Character; if ch then local h=ch:FindFirstChildOfClass("Humanoid"); if h then h.PlatformStand=false end end
end
local function startFly()
	local ch=player.Character or player.CharacterAdded:Wait()
	local hrp=ch:WaitForChild("HumanoidRootPart"); local hum=ch:WaitForChild("Humanoid")
	att=Instance.new("Attachment",hrp)
	lv=Instance.new("LinearVelocity",hrp); lv.Attachment0=att; lv.MaxForce=1e6; lv.VelocityConstraintMode=Enum.VelocityConstraintMode.Vector
	ao=Instance.new("AlignOrientation",hrp); ao.Mode=Enum.OrientationAlignmentMode.OneAttachment; ao.Attachment0=att; ao.MaxTorque=math.huge; ao.ReactionTorqueEnabled=true
	hum.PlatformStand=true; flying=true
end
UserInputService.InputBegan:Connect(function(i,gpe)
	if gpe then return end
	if i.KeyCode==Enum.KeyCode.W then move=Vector3.new(move.X,move.Y,-1)
	elseif i.KeyCode==Enum.KeyCode.S then move=Vector3.new(move.X,move.Y,1)
	elseif i.KeyCode==Enum.KeyCode.A then move=Vector3.new(-1,move.Y,move.Z)
	elseif i.KeyCode==Enum.KeyCode.D then move=Vector3.new(1,move.Y,move.Z)
	elseif i.KeyCode==Enum.KeyCode.Space then up=1
	elseif i.KeyCode==Enum.KeyCode.LeftControl then down=1 end
end)
UserInputService.InputEnded:Connect(function(i,gpe)
	if i.KeyCode==Enum.KeyCode.W or i.KeyCode==Enum.KeyCode.S then move=Vector3.new(move.X,move.Y,0)
	elseif i.KeyCode==Enum.KeyCode.A or i.KeyCode==Enum.KeyCode.D then move=Vector3.new(0,move.Y,move.Z)
	elseif i.KeyCode==Enum.KeyCode.Space then up=0
	elseif i.KeyCode==Enum.KeyCode.LeftControl then down=0 end
end)
RunService.RenderStepped:Connect(function(dt)
	if not flying then return end
	local ch=player.Character; if not ch then return end
	local hrp=ch:FindFirstChild("HumanoidRootPart"); if not hrp then return end
	local cam=workspace.CurrentCamera; local cf=CFrame.new(Vector3.zero, cam.CFrame.LookVector)
	local dir=(cf.RightVector*move.X + cf.LookVector*(-move.Z)); local vert=up-down
	local speed=60; lv.VectorVelocity=dir*speed + Vector3.new(0,vert*speed,0)
	ao.CFrame=CFrame.new(Vector3.zero, (dir.Magnitude>0.001 and dir.Unit or cam.CFrame.LookVector))
end)
EVT_ADMIN_CL.OnClientEvent:Connect(function(action)
	if action=="FlyToggle" then
		if flying then stopFly() else startFly() end
		showToast(flying and "fly: enabled" or "fly: disabled", flying and Color3.fromRGB(120,220,120) or Color3.fromRGB(220,120,120))
	end
end)

Step 3 — AdminPanelServer (ServerScriptService)

Create a Script named AdminPanelServer in ServerScriptService.

AdminPanelServer Script
The copy functionality for this script isn’t working properly in the embed.

Get Script from Pastebin

Step 4 — LuckManager (ModuleScript)

Add a ModuleScript named LuckManager in ServerScriptService.

-- ServerScriptService/LuckManager (ModuleScript)
-- Global luck that doubles on demand and resets to 5:00. Cross-server in live games,
-- safe no-op for Studio (no DataStore/Messaging errors).

local RunService        = game:GetService("RunService")
local MessagingService  = game:GetService("MessagingService")
local DataStoreService  = game:GetService("DataStoreService")

local LUCK_TOPIC    = "GLOBAL_LUCK_V1"
local LUCK_DS       = DataStoreService:GetDataStore("LuckStateV1")
local DEFAULT_MULT  = 1
local DURATION_SECS = 5 * 60 -- 5 minutes
local IS_STUDIO     = RunService:IsStudio()

local State = { multiplier = DEFAULT_MULT, expiresAt = 0 } -- os.time()
local Subscribers = {}

local function now() return os.time() end
local function secondsRemaining() return math.max(0, State.expiresAt - now()) end

local function pushLocal()
	for _, cb in ipairs(Subscribers) do
		task.spawn(cb, State.multiplier, secondsRemaining())
	end
end

local function applyState(mult, exp)
	State.multiplier = mult
	State.expiresAt  = exp
	pushLocal()
end

local function persist()
	if IS_STUDIO then return end
	local ok, err = pcall(function()
		LUCK_DS:SetAsync("state", { multiplier = State.multiplier, expiresAt = State.expiresAt })
	end)
	if not ok then warn("[Luck] Persist failed:", err) end
end

local function publish()
	if IS_STUDIO then return end
	local ok, err = pcall(function()
		MessagingService:PublishAsync(LUCK_TOPIC, {
			multiplier = State.multiplier,
			expiresAt  = State.expiresAt,
			t          = now(),
		})
	end)
	if not ok then warn("[Luck] Publish failed:", err) end
end

local function load()
	if IS_STUDIO then
		applyState(DEFAULT_MULT, 0)
		return
	end
	local ok, data = pcall(function() return LUCK_DS:GetAsync("state") end)
	if ok and typeof(data) == "table" then
		applyState(tonumber(data.multiplier) or DEFAULT_MULT, tonumber(data.expiresAt) or 0)
	else
		applyState(DEFAULT_MULT, 0)
	end
end

local function subscribe()
	if IS_STUDIO then return end
	local ok, sub = pcall(function()
		return MessagingService:SubscribeAsync(LUCK_TOPIC, function(msg)
			local d = msg.Data
			if typeof(d) ~= "table" then return end
			if typeof(d.multiplier) ~= "number" or typeof(d.expiresAt) ~= "number" then return end
			applyState(d.multiplier, d.expiresAt)
		end)
	end)
	if not ok then warn("[Luck] Subscribe failed:", sub) end
end

local M = {}

function M.Init()
	load()
	subscribe()
end

function M.OnChanged(cb)
	table.insert(Subscribers, cb)
	task.defer(cb, State.multiplier, secondsRemaining())
end

function M.Get()
	return State.multiplier, secondsRemaining()
end

function M.DoubleAndReset()
	local newMult = math.clamp(State.multiplier * 2, 1, 2^30)
	local newExp  = now() + DURATION_SECS
	applyState(newMult, newExp)
	persist()
	publish()
end

function M.Tick()
	if secondsRemaining() <= 0 and State.multiplier ~= DEFAULT_MULT then
		applyState(DEFAULT_MULT, 0)
		persist()
		publish()
	end
end

return M

Testing Checklist

  • Press F2 to open/close the panel
  • Send a global message (toast shows on screen)
  • Toggle Fly, Invisible, Invincible
  • Activate Server Luck ×2 and watch the HUD timer
  • Target by partial player name
  • Confirm the Admin page scrolls fully

Important: Live servers handle DataStores differently. Always test publish/subscribe when you’re serious about how to make admin panel roblox studio.

Restricting Access

Add a simple whitelist in the server script:

local ADMIN_USERIDS = { [123456789]=true, [987654321]=true }
-- before executing actions:
-- if not ADMIN_USERIDS[player.UserId] then return end

© 2025 Roblox Tutorial — Learn how to make admin panel roblox studio and more.