Im trying to make an admin panel where i can select a player and give them a tier in chat tag (for example tier 5, tier 4 etc…)
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- DataStore for saving player tags
local TagDataStore = DataStoreService:GetDataStore("PlayerTags_v2")
-- List of admin usernames (replace with your actual admin names)
local ADMINS = {
"davy_tornato", -- Replace with your username
"AltroAdmin", -- Add other admin usernames here
"ahmedrocks5yt"
}
-- Tag configuration table: tagName → { Color = Color3, Priority = number }
local TAG_CONFIGS = {
["TIER 1"] = { Color = Color3.new(1, 0.84, 0 ), Priority = 5 }, -- Gold
["TIER 2"] = { Color = Color3.new(0.75, 0.75, 0.75), Priority = 4 }, -- Silver
["TIER 3"] = { Color = Color3.new(0.8, 0.5, 0.2 ), Priority = 3 }, -- Bronze
["TIER 4"] = { Color = Color3.new(0.2, 0.8, 0.2 ), Priority = 2 }, -- Green
["TIER 5"] = { Color = Color3.new(0.6, 0.6, 0.6 ), Priority = 1 } -- Gray
}
-- ActiveTags: [username] = tagName
local ActiveTags = {}
-- Create (or find) a folder in ReplicatedStorage for our remote events/functions
local remoteFolder = ReplicatedStorage:FindFirstChild("AdminRemotes")
if not remoteFolder then
remoteFolder = Instance.new("Folder")
remoteFolder.Name = "AdminRemotes"
remoteFolder.Parent = ReplicatedStorage
end
-- Define or reuse each RemoteEvent / RemoteFunction
local openAdminPanelRemote = remoteFolder:FindFirstChild("OpenAdminPanel")
if not openAdminPanelRemote then
openAdminPanelRemote = Instance.new("RemoteEvent")
openAdminPanelRemote.Name = "OpenAdminPanel"
openAdminPanelRemote.Parent = remoteFolder
end
local setTagRemote = remoteFolder:FindFirstChild("SetTag")
if not setTagRemote then
setTagRemote = Instance.new("RemoteEvent")
setTagRemote.Name = "SetTag"
setTagRemote.Parent = remoteFolder
end
local removeTagRemote = remoteFolder:FindFirstChild("RemoveTag")
if not removeTagRemote then
removeTagRemote = Instance.new("RemoteEvent")
removeTagRemote.Name = "RemoveTag"
removeTagRemote.Parent = remoteFolder
end
local getTagDataRemote = remoteFolder:FindFirstChild("GetTagData")
if not getTagDataRemote then
getTagDataRemote = Instance.new("RemoteFunction")
getTagDataRemote.Name = "GetTagData"
getTagDataRemote.Parent = remoteFolder
end
local getTagConfigsRemote = remoteFolder:FindFirstChild("GetTagConfigs")
if not getTagConfigsRemote then
getTagConfigsRemote = Instance.new("RemoteFunction")
getTagConfigsRemote.Name = "GetTagConfigs"
getTagConfigsRemote.Parent = remoteFolder
end
local syncTagsRemote = remoteFolder:FindFirstChild("SyncTags")
if not syncTagsRemote then
syncTagsRemote = Instance.new("RemoteEvent")
syncTagsRemote.Name = "SyncTags"
syncTagsRemote.Parent = remoteFolder
end
-- Utility: check if a given player is in the ADMINS list
local function isAdmin(player)
for _, adminName in ipairs(ADMINS) do
if player.Name == adminName then
return true
end
end
return false
end
-- Load a saved tag for this username from DataStore
local function loadPlayerTag(username)
local success, result = pcall(function()
return TagDataStore:GetAsync(username)
end)
if success and result then
return result
end
return nil
end
-- Save (or remove) a tag for this username in DataStore
local function savePlayerTag(username, tagName)
local success, err = pcall(function()
if tagName and tagName ~= "" then
TagDataStore:SetAsync(username, tagName)
else
TagDataStore:RemoveAsync(username)
end
end)
if not success then
warn("Error saving tag for " .. username .. ": " .. tostring(err))
end
end
-- Create an overhead BillboardGui tag above the player's head
local function createOverheadTag(player, tagName)
if not player.Character or not player.Character:FindFirstChild("Head") then
return
end
-- Remove any pre‐existing “AdminTag”
local existingTag = player.Character.Head:FindFirstChild("AdminTag")
if existingTag then
existingTag:Destroy()
end
-- Build a new BillboardGui under the Head
local billboardGui = Instance.new("BillboardGui")
billboardGui.Name = "AdminTag"
billboardGui.Adornee = player.Character.Head
billboardGui.Size = UDim2.new(0, 200, 0, 50)
billboardGui.StudsOffset = Vector3.new(0, 2, 0)
billboardGui.AlwaysOnTop = true
billboardGui.Parent = player.Character.Head
local frame = Instance.new("Frame")
frame.Size = UDim2.new(1, 0, 1, 0)
frame.BackgroundColor3 = Color3.new(0, 0, 0)
frame.BackgroundTransparency = 0.3
frame.BorderSizePixel = 0
frame.Parent = billboardGui
local corner = Instance.new("UICorner")
corner.CornerRadius = UDim.new(0, 8)
corner.Parent = frame
local textLabel = Instance.new("TextLabel")
textLabel.Size = UDim2.new(1, 0, 1, 0)
textLabel.BackgroundTransparency = 1
textLabel.Text = "[" .. tagName .. "]"
textLabel.TextColor3 = TAG_CONFIGS[tagName].Color
textLabel.TextScaled = true
textLabel.Font = Enum.Font.SourceSansBold
textLabel.Parent = frame
local stroke = Instance.new("UIStroke")
stroke.Color = Color3.new(0, 0, 0)
stroke.Thickness = 2
stroke.Parent = textLabel
end
-- Remove the overhead tag from a player’s head
local function removeOverheadTag(player)
if player.Character and player.Character:FindFirstChild("Head") then
local existingTag = player.Character.Head:FindFirstChild("AdminTag")
if existingTag then
existingTag:Destroy()
end
end
end
-- Assign a tag (both overhead & persistence) to a player
local function applyTagToPlayer(username, tagName)
ActiveTags[username] = tagName
savePlayerTag(username, tagName)
local player = Players:FindFirstChild(username)
if player then
createOverheadTag(player, tagName)
end
-- Sync the full ActiveTags table to every client
syncTagsRemote:FireAllClients(ActiveTags)
end
-- Remove a player’s tag (both overhead & persistence)
local function removeTagFromPlayer(username)
local oldTag = ActiveTags[username]
ActiveTags[username] = nil
savePlayerTag(username, nil)
local player = Players:FindFirstChild(username)
if player then
removeOverheadTag(player)
end
-- Sync the updated ActiveTags to every client
syncTagsRemote:FireAllClients(ActiveTags)
end
-- RemoteEvent: Admin sets a tag on a target player
setTagRemote.OnServerEvent:Connect(function(player, targetUsername, tagName)
if not isAdmin(player) then
return
end
if TAG_CONFIGS[tagName] then
applyTagToPlayer(targetUsername, tagName)
print("Admin " .. player.Name .. " set tag " .. tagName .. " on " .. targetUsername)
end
end)
-- RemoteEvent: Admin removes a tag from a target player
removeTagRemote.OnServerEvent:Connect(function(player, targetUsername)
if not isAdmin(player) then
return
end
removeTagFromPlayer(targetUsername)
print("Admin " .. player.Name .. " removed tag from " .. targetUsername)
end)
-- RemoteFunction: Admin GUI asks for current tag data (online players, configs, ActiveTags)
getTagDataRemote.OnServerInvoke = function(player)
if not isAdmin(player) then
return {}
end
local onlinePlayers = {}
for _, p in ipairs(Players:GetPlayers()) do
table.insert(onlinePlayers, {
Name = p.Name,
Tag = ActiveTags[p.Name]
})
end
return {
OnlinePlayers = onlinePlayers,
TagConfigs = TAG_CONFIGS,
ActiveTags = ActiveTags
}
end
-- RemoteFunction: Admin GUI asks for TAG_CONFIGS only
getTagConfigsRemote.OnServerInvoke = function(player)
if not isAdmin(player) then
return {}
end
return TAG_CONFIGS
end
-- When a new player joins:
Players.PlayerAdded:Connect(function(player)
-- 1) Listen for chat command “/admin” to open the admin panel
player.Chatted:Connect(function(message)
if isAdmin(player) and message:lower() == "/admin" then
openAdminPanelRemote:FireClient(player)
end
end)
-- 2) After a short delay, send existing ActiveTags to just that client
task.wait(2)
syncTagsRemote:FireClient(player, ActiveTags)
-- 3) After DataStore loads, re-apply any saved tag to their character
task.wait(5)
local savedTag = loadPlayerTag(player.Name)
if savedTag and TAG_CONFIGS[savedTag] then
ActiveTags[player.Name] = savedTag
player.CharacterAdded:Connect(function()
task.wait(1)
createOverheadTag(player, savedTag)
end)
if player.Character then
createOverheadTag(player, savedTag)
end
print("Loaded saved tag for " .. player.Name .. ": " .. savedTag)
else
print("No saved tag for " .. player.Name)
end
end)
-- When a player leaves, you may optionally clear ActiveTags (DataStore still holds their last tag)
Players.PlayerRemoving:Connect(function(player)
-- ActiveTags[player.Name] = nil
end)
print("TagManager loaded successfully!")
---- This is a script in serverscriptservice
local ChatTags = {}
– Cached ChatService once found
local _cachedChatService = nil
– Attempts to locate and require ChatServiceRunner/ChatService up to 10 times (1 second apart).
– Returns the ChatService module or nil if not found.
local function getChatService()
if _cachedChatService then
return _cachedChatService
end
for attempt = 1, 10 do
local csrFolder = game:GetService("ServerScriptService"):FindFirstChild("ChatServiceRunner")
if csrFolder then
local csModule = csrFolder:FindFirstChild("ChatService")
if csModule and csModule:IsA("ModuleScript") then
local success, module = pcall(require, csModule)
if success then
_cachedChatService = module
return module
else
warn("ChatTags: Failed to require ChatService (pcall error).")
return nil
end
end
end
wait(1)
end
warn("ChatTags: Could not find ChatServiceRunner/ChatService after 10 seconds.")
return nil
end
– Adds a chat tag (tagString) with the given color3 to the specified player.
function ChatTags.Add(player, tagString, color3)
if not player or typeof(tagString) ~= “string” then
return
end
local ChatService = getChatService()
if not ChatService then
return
end
local success, speaker = pcall(function()
return ChatService:GetSpeaker(player.Name)
end)
if success and speaker then
-- Default ChatServiceRunner uses AddExtraData for “Tags” and “TagColor”
speaker:AddExtraData("Tags", tagString)
speaker:AddExtraData("TagColor", {color3.R, color3.G, color3.B})
else
warn("ChatTags.Add: Could not get speaker for " .. tostring(player.Name))
end
end
– Removes any chat tag (“Tags” and “TagColor” extra data) from the specified player.
function ChatTags.Remove(player, tagString)
if not player then
return
end
local ChatService = getChatService()
if not ChatService then
return
end
local success, speaker = pcall(function()
return ChatService:GetSpeaker(player.Name)
end)
if success and speaker then
speaker:RemoveExtraData("Tags")
speaker:RemoveExtraData("TagColor")
else
warn("ChatTags.Remove: Could not get speaker for " .. tostring(player.Name))
end
end
return ChatTags
-----This is a module script in server script service
local Players = game:GetService(“Players”)
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
– TextChatService must be enabled in Game Settings → Chat
local TextChatService = game:GetService(“TextChatService”)
local localPlayer = Players.LocalPlayer
– Wait for our AdminRemotes folder
local remoteFolder = ReplicatedStorage:WaitForChild(“AdminRemotes”)
local syncTagsRemote = remoteFolder:WaitForChild(“SyncTags”)
local getTagConfigsRemote = remoteFolder:WaitForChild(“GetTagConfigs”)
– Local copies:
– activeTags[username] = tagName
– tagConfigs[tagName] = { Color = Color3, Priority = number }
local activeTags = {}
local tagConfigs = {}
– Helper: Convert Color3 → HEX string (e.g. Color3.new(1,0.84,0) → “#FFD900”)
local function color3ToHex(c)
local r = math.clamp(math.floor(c.R * 255 + 0.5), 0, 255)
local g = math.clamp(math.floor(c.G * 255 + 0.5), 0, 255)
local b = math.clamp(math.floor(c.B * 255 + 0.5), 0, 255)
return string.format(“#%02X%02X%02X”, r, g, b)
end
– 1) Fetch TAG_CONFIGS from the server
– (Non-admins will get an empty table; admins receive the full TAG_CONFIGS data.)
local configs = getTagConfigsRemote:InvokeServer()
if typeof(configs) == “table” then
tagConfigs = configs
else
tagConfigs = {}
end
– 2) Listen for SyncTags from the server; always replace our local activeTags map
syncTagsRemote.OnClientEvent:Connect(function(newActiveTags)
activeTags = {}
for username, tagName in pairs(newActiveTags or {}) do
activeTags[username] = tagName
end
end)
– 3) Register incoming‐message callback on TextChatService
– Prepend a colored “[Tier X]” if the sender is in activeTags
– This callback fires for every chat message that the client sees.
if Enum.TextChatMessageStatus and TextChatService.RegisterIncomingMessageCallback then
TextChatService:RegisterIncomingMessageCallback(
function(textChatMessage)
local senderName = textChatMessage.TextSource.Name
local tagName = activeTags[senderName]
if tagName then
local cfg = tagConfigs[tagName]
if cfg and cfg.Color then
local hex = color3ToHex(cfg.Color)
textChatMessage.Text = (“[%s] %s”):format(
hex,
tagName,
textChatMessage.Text
)
else
– Fallback if color not found
textChatMessage.Text = (“[%s] %s”):format(tagName, textChatMessage.Text)
end
end
end
)
else
– If the enum isn’t available, TextChatService isn’t enabled.
warn(“ClientTagHandler: TextChatService or Enum.TextChatMessageEvent is not available. Chat tagging will not work. Make sure ChatType is set to BubbleChat/Classic (TextChat).”)
end
print(“ClientTagHandler initialized.”)
-----This is a script in StarterPlayerScripts
Also Tell me if i have to use TextChatService or Legacy because i tried with both