--[[
CustomGroupSystem.lua
=====================
This script kit gives you an *internal* group/role system that lets players join a
team directly inside your game (no external Roblox group, no Robux).
📂 Files included (all in this single textdoc):
1. **GroupServer** – ServerScriptService script that:
• creates Teams automatically
• loads/saves each player’s chosen group via DataStore
• receives RemoteEvent requests to join a group and applies rules
2. **GroupClient** – LocalScript that lives under *StarterGui > ScreenGui* and
builds a basic button-based UI allowing the player to choose a group.
🔧 Setup steps (quick):
------------------------------------------------------------
1. In **Explorer** make sure you have a ***Teams*** container (Model → Service).
2. Paste the *GroupServer* code into a new **Script** called `GroupServer`
inside **ServerScriptService**.
3. Paste the *GroupClient* code into a **LocalScript** inside a **ScreenGui**
(e.g. name it `ChooseGroupGui`) under **StarterGui**.
4. Publish your place (DataStores only work on published places) and
**Game Settings → Security → Enable Studio Access to API Services** so the
DataStore calls work while testing in Studio.
5. Press Play. A small window appears with buttons like "Join Police".
Click to join. Your team changes and the choice is saved for next time.
Add/Remove groups:
• Edit `AvailableGroups` table in *GroupServer* **and** `groups` array in
*GroupClient* to keep them in sync.
• For a locked/VIP group demo, change the `requestedGroup == "VIP"` section.
Ready-made colour selection uses `BrickColor.Random()` – swap for your desired
palette.
-----------------------------------------------------------------]]
-- =============================================================
-- === GroupServer (place this Script in ServerScriptService) ===
-- =============================================================
local Players = game:GetService("Players")
local Teams = game:GetService("Teams")
local DataStoreService = game:GetService("DataStoreService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local GroupStore = DataStoreService:GetDataStore("PlayerGroup")
local AvailableGroups = {
"Police",
"Thieves",
"Builders",
"VIP"
}
-- Utility: ensure Teams exist
local function ensureTeams()
for _, name in ipairs(AvailableGroups) do
if not Teams:FindFirstChild(name) then
local team = Instance.new("Team")
team.Name = name
team.TeamColor = BrickColor.Random() -- pick fixed colours if you
prefer
team.AutoAssignable = false
team.Parent = Teams
end
end
end
ensureTeams()
-- RemoteEvent for group join requests
local joinGroupEvent = ReplicatedStorage:FindFirstChild("JoinGroupEvent")
if not joinGroupEvent then
joinGroupEvent = Instance.new("RemoteEvent")
joinGroupEvent.Name = "JoinGroupEvent"
joinGroupEvent.Parent = ReplicatedStorage
end
-- Load player
Players.PlayerAdded:Connect(function(player)
local success, saved = pcall(function()
return GroupStore:GetAsync(player.UserId)
end)
if success and typeof(saved) == "string" and table.find(AvailableGroups, saved)
then
player:SetAttribute("Group", saved)
local team = Teams:FindFirstChild(saved)
if team then player.Team = team end
else
player:SetAttribute("Group", "None")
end
end)
-- Save on exit
Players.PlayerRemoving:Connect(function(player)
local group = player:GetAttribute("Group") or "None"
pcall(function()
GroupStore:SetAsync(player.UserId, group)
end)
end)
game:BindToClose(function()
-- save everyone on server shutdown
for _, player in ipairs(Players:GetPlayers()) do
local group = player:GetAttribute("Group") or "None"
pcall(function()
GroupStore:SetAsync(player.UserId, group)
end)
end
end)
-- Handle join requests from clients
joinGroupEvent.OnServerEvent:Connect(function(player, requestedGroup)
if typeof(requestedGroup) ~= "string" then return end
if not table.find(AvailableGroups, requestedGroup) then return end
-- Example rule: VIP requires a gamepass (replace pass ID!)
local isAllowed = true
if requestedGroup == "VIP" then
local GamePassService = game:GetService("MarketplaceService")
local VIP_PASS_ID = 12345678 -- swap for your pass id
local owns = false
pcall(function()
owns = GamePassService:UserOwnsGamePassAsync(player.UserId,
VIP_PASS_ID)
end)
isAllowed = owns
end
if isAllowed then
player:SetAttribute("Group", requestedGroup)
local team = Teams:FindFirstChild(requestedGroup)
if team then player.Team = team end
pcall(function()
GroupStore:SetAsync(player.UserId, requestedGroup)
end)
joinGroupEvent:FireClient(player, true, requestedGroup)
else
joinGroupEvent:FireClient(player, false, requestedGroup)
end
end)
-- =============================================================
-- === GroupClient (LocalScript under StarterGui > ScreenGui) ===
-- =============================================================
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local joinGroupEvent = ReplicatedStorage:WaitForChild("JoinGroupEvent")
local PlayerGui = player:WaitForChild("PlayerGui")
--\n-- Build simple UI programmatically -- no need for manual GUI design in Studio
--//
local screenGui = Instance.new("ScreenGui")
screenGui.Name = "ChooseGroupGui"
screenGui.ResetOnSpawn = false
screenGui.Parent = PlayerGui
local frame = Instance.new("Frame")
frame.Size = UDim2.new(0, 400, 0, 260)
frame.Position = UDim2.new(0.5, -200, 0.5, -130)
frame.AnchorPoint = Vector2.new(0.5, 0.5)
frame.BackgroundTransparency = 0.2
frame.BackgroundColor3 = Color3.fromRGB(30, 30, 30)
frame.BorderSizePixel = 0
frame.Parent = screenGui
local UIList = Instance.new("UIListLayout")
UIList.FillDirection = Enum.FillDirection.Vertical
UIList.HorizontalAlignment = Enum.HorizontalAlignment.Center
UIList.VerticalAlignment = Enum.VerticalAlignment.Center
UIList.Padding = UDim.new(0, 6)
UIList.Parent = frame
local title = Instance.new("TextLabel")
title.Size = UDim2.new(1, 0, 0, 30)
title.Text = "Choose a group to join:"
title.TextScaled = true
title.BackgroundTransparency = 1
title.TextColor3 = Color3.new(1,1,1)
title.Font = Enum.Font.GothamBold
title.Parent = frame
local groups = {"Police", "Thieves", "Builders", "VIP"}
local function makeButton(name)
local btn = Instance.new("TextButton")
btn.Size = UDim2.new(1, -20, 0, 40)
btn.Text = "Join " .. name
btn.TextScaled = true
btn.BackgroundColor3 = Color3.fromRGB(50, 50, 50)
btn.TextColor3 = Color3.new(1,1,1)
btn.Font = Enum.Font.Gotham
btn.BorderSizePixel = 0
btn.AutoButtonColor = true
btn.Parent = frame
btn.MouseButton1Click:Connect(function()
joinGroupEvent:FireServer(name)
end)
end
for _, g in ipairs(groups) do
makeButton(g)
end
local info = Instance.new("TextLabel")
info.Size = UDim2.new(1, 0, 0, 24)
info.Text = ""
info.BackgroundTransparency = 1
info.TextColor3 = Color3.new(1,1,1)
info.Font = Enum.Font.GothamSemibold
info.TextScaled = true
info.Parent = frame
joinGroupEvent.OnClientEvent:Connect(function(success, grp)
if success then
info.Text = "You joined " .. grp .. "!"
wait(1.5)
screenGui.Enabled = false
else
info.Text = "You don't have permission for " .. grp
wait(2)
info.Text = ""
end
end)
-- Hide GUI if player already has a group
if player:GetAttribute("Group") and player:GetAttribute("Group") ~= "None" then
screenGui.Enabled = false
end
-- Enable GUI later if you want a change-team button, e.g. via hotkey etc.
-- =============================================================
-- === END OF CustomGroupSystem.lua ===
-- =============================================================