diff --git a/Loader/Config/Settings.luau b/Loader/Config/Settings.luau
index 9f14428be..cda345f7e 100644
--- a/Loader/Config/Settings.luau
+++ b/Loader/Config/Settings.luau
@@ -238,7 +238,7 @@ settings.SaveAdmins = true -- If true anyone you ;admin or ;headadmin in-game
settings.LoadAdminsFromDS = true -- If false, any admins saved in your DataStores will not load
settings.WhitelistEnabled = false -- If true enables the whitelist/server lock; Only lets admins & whitelisted users join
-settings.Prefix = ";" -- The ; in ;kill me
+settings.Prefix = {";", ":"} -- A list of prefixes for commands, the ; in ;kill me
settings.PlayerPrefix = "!" -- The ! in !donate; Mainly used for commands that any player can run; Do not make it the same as settings.Prefix
settings.SpecialPrefix = "" -- Used for things like "all", "me" and "others" (If changed to ! you would do ;kill !me)
settings.SplitKey = " " -- The space in ;kill me (eg if you change it to / ;kill me would be ;kill/me)
diff --git a/MainModule/Client/UI/Default/UserPanel.luau b/MainModule/Client/UI/Default/UserPanel.luau
index 7d50c7f38..363fcfbdd 100644
--- a/MainModule/Client/UI/Default/UserPanel.luau
+++ b/MainModule/Client/UI/Default/UserPanel.luau
@@ -497,7 +497,8 @@ return function(data, env)
chatMod = Remote.Get("Setting",{"Prefix","SpecialPrefix","BatchKey","AnyPrefix","DonorCommands","DonorCapes"})
settingsData = Remote.Get("AllSettings")
Variables.Aliases = playerData.Aliases or {}
- commandPrefix = chatMod.Prefix
+ commandPrefix = if type(chatMod.Prefix) == "table" then chatMod.Prefix[1] else chatMod.Prefix
+
for _, v in loadingIcons do
v:Destroy()
diff --git a/MainModule/Server/Commands/Admins.luau b/MainModule/Server/Commands/Admins.luau
index f3312c057..c38d42b7e 100644
--- a/MainModule/Server/Commands/Admins.luau
+++ b/MainModule/Server/Commands/Admins.luau
@@ -1,1598 +1,1719 @@
---!nocheck
-return function(Vargs, env)
+server = nil
+service = nil
+Routine = nil
+GetEnv = nil
+origEnv = nil
+logError = nil
+
+--// Admin
+return function(Vargs, GetEnv)
+ local env = GetEnv(nil, {script = script})
+ setfenv(1, env)
+
local server = Vargs.Server;
local service = Vargs.Service;
- local Settings = server.Settings
- local Functions, Commands, Admin, Anti, Core, HTTP, Logs, Remote, Process, Variables, Deps =
- server.Functions, server.Commands, server.Admin, server.Anti, server.Core, server.HTTP, server.Logs, server.Remote, server.Process, server.Variables, server.Deps
-
- if env then setfenv(1, env) end
-
- local Routine = env.Routine
-
- return {
- SetRank = {
- Prefix = Settings.Prefix;
- Commands = {"setrank", "permrank", "permsetrank"};
- Args = {"player/user", "rank"};
- Description = "Sets the admin rank of the target user(s); THIS SAVES!";
- AdminLevel = "Admins";
- Dangerous = true;
- Function = function(plr: Player, args: {string}, data: {any})
- assert(args[1], "Missing target user (argument #1)")
- local rankName = assert(args[2], "Missing rank name (argument #2)")
-
- local newRank = Settings.Ranks[rankName]
- if not newRank then
- for thisRankName, thisRank in Settings.Ranks do
- if thisRankName:lower() == rankName:lower() then
- rankName = thisRankName
- newRank = thisRank
- break
+ local Functions, Admin, Anti, Core, HTTP, Logs, Remote, Process, Variables, Settings, Commands
+ local AddLog, TrackTask, Defaults
+ local CreatorId = game.CreatorType == Enum.CreatorType.User and game.CreatorId or service.GetGroupCreatorId(game.CreatorId)
+ local function Init()
+ Functions = server.Functions;
+ Admin = server.Admin;
+ Anti = server.Anti;
+ Core = server.Core;
+ HTTP = server.HTTP;
+ Logs = server.Logs;
+ Remote = server.Remote;
+ Process = server.Process;
+ Variables = server.Variables;
+ Settings = server.Settings;
+ Commands = server.Commands;
+ Defaults = server.Defaults
+
+ TrackTask = service.TrackTask
+ AddLog = Logs.AddLog;
+
+ TrackTask("Thread: ChatServiceHandler", function()
+
+ --// Support for modern TextChatService
+ if service.TextChatService and service.TextChatService.ChatVersion == Enum.ChatVersion.TextChatService then
+ local function onNewTextchannel(textchannel: TextChannel)
+ AddLog("Script", `Connected to TextChannel: {textchannel.Name}`)
+
+ if Settings.OverrideChatCallbacks ~= false then --// Default to "on" this for all games
+ AddLog("Script", "Overriding ShouldDeliverCallback for " .. textchannel.Name)
+ textchannel.ShouldDeliverCallback = function(chatMessage, textSource)
+ if
+ chatMessage.Status == Enum.TextChatMessageStatus.Success
+ or chatMessage.Status == Enum.TextChatMessageStatus.Sending
+ then
+ local SenderId = chatMessage.TextSource.UserId
+ local SenderPlayer = service.Players:GetPlayerByUserId(SenderId)
+ local Receiver = service.Players:GetPlayerByUserId(textSource.UserId)
+ local slowCache = Admin.SlowCache
+
+ local IsOriginalSender = SenderPlayer == Receiver
+
+ if not SenderPlayer then
+ return true
+ elseif Admin.DoHideChatCmd(SenderPlayer, chatMessage.Text) then -- // Hide chat commands?
+ return false
+ elseif Admin.IsMuted(SenderPlayer) then -- // Mute handler
+ if IsOriginalSender then
+ server.Remote.Send(SenderPlayer, "Function", "DisplaySystemMessageInTextChat", nil, `[Adonis Chat]: You are muted! Other players cannot see your messages.`)
+ end
+
+ return false
+ elseif Admin.SlowMode and not Admin.CheckAdmin(SenderPlayer) and slowCache[SenderPlayer] and os.time() - slowCache[SenderPlayer] < Admin.SlowMode then
+ if IsOriginalSender then --// Only show this for the person sending! Hide for others, however
+ --Functions.Notification("You are chatting too fast!", string.format("[Adonis] :: Slow mode enabled! (%g second(s) remaining)", Admin.SlowMode - (os.time() - slowCache[SenderPlayer])), {SenderPlayer}, 10)
+
+ server.Remote.Send(SenderPlayer, "Function", "DisplaySystemMessageInTextChat", nil, `[Adonis Chat]: You are sending messages too fast! {string.format("(%g second(s) remaining)", Admin.SlowMode - (os.time() - slowCache[SenderPlayer]))}`)
+ end
+
+ return false
+ end
+
+ if Variables.DisguiseBindings[SenderId] then -- // Disguise command handler
+ chatMessage.PrefixText = Variables.DisguiseBindings[SenderId].TargetUsername..":"
+ end
+
+ if Admin.SlowMode and IsOriginalSender then
+ slowCache[SenderPlayer] = os.time()
+ end
+ end
+
+ return true
end
- end
- end
- assert(newRank, `No rank named '{rankName}' exists`)
-
- local newLevel = newRank.Level
- local senderLevel = data.PlayerData.Level
-
- assert(newLevel < senderLevel, string.format("Rank level (%s) cannot be equal to or above your own level (%s)", newLevel, senderLevel))
- local v = Functions.GetPlayers(plr, args[1], {NoFakePlayer = false})
- for _, p in v do
- if senderLevel > Admin.GetLevel(p) then
- Admin.AddAdmin(p, rankName)
- Functions.LogAdminAction(plr, "Set Rank", p.Name, `New rank: {rankName}, New level: {newLevel}`)
- Functions.Notification(
- "Notification",
- `You are {if string.lower(string.sub(rankName, 1, 3)) == "the" then "" elseif string.match(rankName, "^[AEIOUaeiou]") and string.lower(string.sub(rankName, 1, 3)) ~= "uni" then "an " else "a "}{rankName}. Click to view commands.`,
- {p}, 10, "MatIcon://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Settings.Prefix}cmds')`)
- )
- Functions.Hint(`{service.FormatPlayer(p, true)} is now rank {rankName} (Permission Level: {newLevel})`, {plr})
else
- Functions.Hint(`You do not have permission to set the rank of {service.FormatPlayer(p, true)}`, {plr})
+ AddLog("Script", `Using the 'CanSend' method of handling chat connectivity in channel {textchannel.Name}`)
+ server.Variables.TextChatSpeakers = {}
+ local function AddUserToTextChatSpeakers(player: Player, speaker: TextSource)
+ if not server.Variables.TextChatSpeakers[player] then
+ server.Variables.TextChatSpeakers[player] = {}
+ end
+ table.insert(server.Variables.TextChatSpeakers[player], speaker)
+ --// Check if the player is muted or not
+ speaker:SetAttribute("OriginalCanSend", speaker.CanSend)
+ if server.Admin.IsMuted(player) then
+ speaker.CanSend = false
+ end
+ end
+ local function SpeakerAdded(speaker: TextSource)
+ if speaker.UserId and speaker.UserId > 0 then
+ local Player = service.Players:GetPlayerByUserId(speaker.UserId)
+ if Player then
+ AddUserToTextChatSpeakers(Player, speaker)
+ end
+ end
+ end
+ local function SpeakerRemoved(speaker: TextSource)
+ if speaker.UserId and speaker.UserId > 0 then
+ local Player = service.Players:GetPlayerByUserId(speaker.UserId)
+ local Tab = server.Variables.TextChatSpeakers[Player]
+ if Tab then
+ local index = table.find(Tab, speaker)
+ while index do
+ table.remove(Tab, index)
+ index = table.find(Tab, speaker)
+ end
+ task.defer(function()
+ if #Tab == 0 then
+ server.Variables.TextChatSpeakers[Player] = nil
+ end
+ end)
+ end
+ end
+ end
+
+ textchannel.ChildAdded:Connect(function(textSource)
+ if textSource:IsA("TextSource") then
+ SpeakerAdded(textSource)
+ end
+ end)
+
+ textchannel.ChildRemoved:Connect(function(textSource)
+ if textSource:IsA("TextSource") then
+ SpeakerRemoved(textSource)
+ end
+ end)
+
+ for _,inst in textchannel:GetChildren() do
+ if inst:IsA("TextSource") then
+ SpeakerAdded(inst)
+ end
+ end
+
end
end
- end;
- };
-
- SetTempRank = {
- Prefix = Settings.Prefix;
- Commands = {"settemprank", "temprank", "tempsetrank"};
- Args = {"player", "rank"};
- Description = `Identical to {Settings.Prefix}setrank, but doesn't save`;
- AdminLevel = "Admins";
- Dangerous = true;
- Function = function(plr: Player, args: {string}, data: {any})
- assert(args[1], "Missing target player (argument #1)")
- local rankName = assert(args[2], "Missing rank name (argument #2)")
-
- local newRank = Settings.Ranks[rankName]
- if not newRank then
- for thisRankName, thisRank in Settings.Ranks do
- if thisRankName:lower() == rankName:lower() then
- rankName = thisRankName
- newRank = thisRank
- break
+
+ --// Only set this up once
+ --// This is for commands to tell us when a player should be muted
+ if not Settings.OverrideChatCallbacks then
+ service.Events.PlayerMuted:Connect(function(data)
+ local PlayerId = data.Target;
+ local ModId = data.Moderator;
+
+ local Player = service.Players:GetPlayerByUserId(PlayerId)
+ --// Loop through CanSend of a speaker
+ for _,speakers : TextSource in if Player then server.Variables.TextChatSpeakers[Player] or {} else {} do
+ speakers.CanSend = false
end
- end
+ if Player then
+ AddLog("Script", `Muted player {Player.Name}:{Player.UserId} using CanSend method`)
+ end
+ end)
+ service.Events.PlayerUnMuted:Connect(function(data)
+ local PlayerId = data.Target;
+ local ModId = data.Moderator;
+
+ local Player = service.Players:GetPlayerByUserId(PlayerId)
+ --// Loop through CanSend of a speaker
+ for _,speakers : TextSource in if Player then server.Variables.TextChatSpeakers[Player] or {} else {} do
+ local original = speakers:GetAttribute("OriginalCanSend")
+ speakers.CanSend = if original ~= nil then original else true
+ end
+ if Player then
+ AddLog("Script", `UnMuted player {Player.Name}:{Player.UserId} via CanSend method`)
+ end
+ end)
+ service.Events.MutedPlayerChat_UnFiltered:Connect(function(p, ...)
+ server.Remote.Send(p, "Function", "DisplaySystemMessageInTextChat", nil, `[Adonis Chat]: You are muted! Other players cannot see your messages.`)
+ end)
end
- assert(newRank, `No rank named '{rankName}' exists`)
- local newLevel = newRank.Level
- local senderLevel = data.PlayerData.Level
- assert(newLevel < senderLevel, string.format("Rank level (%s) cannot be equal to or above your own level (%s)", newLevel, senderLevel))
+ local function onTextChannelsAdded(textChannels)
+ textChannels.ChildAdded:Connect(function(child)
+ if child:IsA("TextChannel") then
+ task.spawn(onNewTextchannel, child)
+ end
+ end)
- for _, v in service.GetPlayers(plr, args[1]) do
- if senderLevel > Admin.GetLevel(v) then
- Admin.AddAdmin(v, rankName, true)
- Functions.LogAdminAction(plr, "Set Temporary Rank", v.Name, `Temporary rank set to: {rankName}`)
- Functions.Notification("Notification", `You are a temp {rankName}. Click to view commands.`, {v}, 10, "MatIcon://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Settings.Prefix}cmds')`))
- Functions.Hint(`{service.FormatPlayer(v, true)} is now rank {rankName} (Permission Level: {newLevel})`, {plr})
- else
- Functions.Hint(`You do not have permission to set the rank of {service.FormatPlayer(v, true)}`, {plr})
+ for _, v in textChannels:GetChildren() do
+ if v:IsA("TextChannel") then
+ task.spawn(onNewTextchannel, v)
+ end
end
end
- end;
- };
-
- SetLevel = {
- Prefix = Settings.Prefix;
- Commands = {"setlevel", "setadminlevel"};
- Args = {"player", "level"};
- Description = "Sets the target player(s) permission level for the current server; does not save";
- AdminLevel = "Admins";
- Dangerous = true;
- Function = function(plr: Player, args: {string}, data: {any})
- local senderLevel = data.PlayerData.Level
- local newLevel = assert(tonumber(args[2]), "Level must be a number")
-
- assert(newLevel < senderLevel, `Level cannot be equal to or above your own permission level ({senderLevel})`);
-
- for _, v in service.GetPlayers(plr, args[1])do
- if senderLevel > Admin.GetLevel(v) then
- Admin.SetLevel(v, newLevel, args[3] == "true")
- Functions.LogAdminAction(plr, "Set Level", v.Name, `New level: {newLevel}`)
- Functions.Notification("Notification", `Your admin permission level was set to {newLevel} for this server only. Click to view commands.`, {v}, 10, "MatIcon://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Settings.Prefix}cmds')`))
- Functions.Hint(`{service.FormatPlayer(v, true)} is now permission level {newLevel}`, {plr})
- else
- Functions.Hint(`You do not have permission to set the permission level of {service.FormatPlayer(v, true)}`, {plr})
+
+ service.TextChatService.ChildAdded:Connect(function(child)
+ if child.Name == "TextChannels" then
+ task.spawn(onTextChannelsAdded, child)
end
+ end)
+
+ if service.TextChatService:FindFirstChild("TextChannels") then
+ task.spawn(pcall, onTextChannelsAdded, service.TextChatService:FindFirstChild("TextChannels"))
end
- end;
- };
-
- UnAdmin = {
- Prefix = Settings.Prefix;
- Commands = {"unadmin", "unmod", "unowner", "unpadmin", "unheadadmin", "unrank"};
- Args = {"player/user / list entry", "temp? (true/false) (default: false)"};
- Description = "Removes admin/moderator ranks from the target player(s); saves unless is 'true'";
- AdminLevel = "Admins";
- Dangerous = true;
- Function = function(plr: Player, args: {string}, data: {any})
- local target = assert(args[1], "Missing target user (argument #1)")
- local temp = args[2] and args[2]:lower() == "true"
- local senderLevel = data.PlayerData.Level
- local userFound = false
-
- if not string.find(target, ":") then
- for _, v in service.GetPlayers(plr, target, {
- UseFakePlayer = true;
- DontError = true;
- })
- do
- userFound = true
- local targLevel, targRank = Admin.GetLevel(v)
- if targLevel > 0 then
- if senderLevel > targLevel then
- Admin.RemoveAdmin(v, temp)
- Functions.LogAdminAction(plr, "Remove Admin", v.Name, `Temporary: {temp}`)
- Functions.Hint(string.format("Removed %s from rank %s", service.FormatPlayer(v, true), targRank or "[unknown rank]"), {plr})
- Functions.Notification("Notification", `You are no longer a(n) {targRank or "admin"}`, {v}, 10, "MatIcon://Shield")
- else
- Functions.Hint(`You do not have permission to remove {service.FormatPlayer(v, true)}'s rank`, {plr})
- end
- else
- Functions.Hint(`{service.FormatPlayer(v, true)} does not already have any rank to remove`, {plr})
- end
+
+ AddLog("Script", "TextChatService Handler Loaded")
+ end
+
+ --// Support for legacy Lua chat system
+ --// ChatService mute handler (credit to Coasterteam)
+ AddLog("Script", "Starting loading of legacy chatservice handler")
+ local chatService = Functions.GetChatService(300)
+ if chatService then
+ chatService:RegisterProcessCommandsFunction("ADONIS_CMD", function(speakerName, message)
+ local speaker = chatService:GetSpeaker(speakerName)
+ local speakerPlayer = speaker and speaker:GetPlayer()
+
+ if not speakerPlayer then
+ return false
end
- if userFound then
- return
- else
- Functions.Hint("User not found in server; searching datastore", {plr})
+ if Admin.DoHideChatCmd(speakerPlayer, message) then
+ return true
end
- end
- for rankName, rankData in Settings.Ranks do
- if senderLevel <= rankData.Level then
- continue
+ return false
+ end)
+
+ chatService:RegisterProcessCommandsFunction("ADONIS_MUTE_SERVER", function(speakerName, _, channelName)
+ local slowCache = Admin.SlowCache
+
+ local speaker = chatService:GetSpeaker(speakerName)
+ local speakerPlayer = speaker and speaker:GetPlayer()
+
+ if not speakerPlayer then
+ return false
end
- for i, user in rankData.Users do
- if not (user:lower() == target:lower() or user:lower():match(`^{target:lower()}:`) or Admin.DoCheck(target, user)) then
- continue
- end
- if
- Remote.GetGui(plr, "YesNoPrompt", {
- Question = `Remove '{user}' from '{rankName}'?`;
- }) == "Yes"
- then
- table.remove(rankData.Users, i)
- if not temp and Settings.SaveAdmins then
- service.TrackTask("Thread: RemoveAdmin", Core.DoSave, false, {
- Type = "TableRemove";
- Table = {"Settings", "Ranks", rankName, "Users"};
- Value = user;
- });
- Functions.Hint(`Removed entry '{user}' from {rankName}`, {plr})
- Logs:AddLog("Script", `{plr} removed {user} from {rankName}`)
- end
- end
- userFound = true
+
+ if speakerPlayer and Admin.IsMuted(speakerPlayer) then
+ speaker:SendSystemMessage("[Adonis] :: You are muted!", channelName)
+ return true
+ elseif speakerPlayer and Admin.SlowMode and not Admin.CheckAdmin(speakerPlayer) and slowCache[speakerPlayer] and os.time() - slowCache[speakerPlayer] < Admin.SlowMode then
+ speaker:SendSystemMessage(string.format("[Adonis] :: Slow mode enabled! (%g second(s) remaining)", Admin.SlowMode - (os.time() - slowCache[speakerPlayer])), channelName)
+ return true
end
- end
- assert(userFound, `No table entries matching '{args[1]}' were found`)
- end
- };
-
- TempUnAdmin = {
- Prefix = Settings.Prefix;
- Commands = {"tempunadmin", "untempadmin", "tunadmin", "untadmin"};
- Args = {"player"};
- Description = "Removes the target players' admin powers for this server; does not save";
- AdminLevel = "Admins";
- Dangerous = true;
- Function = function(plr: Player, args: {string}, data: {any})
- local senderLevel = data.PlayerData.Level
-
- for _, v in service.GetPlayers(plr, assert(args[1], "Missing target player (argument #1)")) do
- local targetLevel = Admin.GetLevel(v)
- if targetLevel > 0 then
- if senderLevel > targetLevel then
- Admin.RemoveAdmin(v, true)
- Functions.LogAdminAction(plr, "Temporary Unadmin", v.Name, "Admin powers temporarily removed")
- Functions.Hint(`Removed {service.FormatPlayer(v)}'s admin powers`, {plr})
- Functions.Notification("Notification", "Your admin powers have been temporarily removed", {v}, 10, "MatIcons://Remove moderator")
- else
- Functions.Hint(`You do not have permission to remove {service.FormatPlayer(v, true)}'s admin powers`, {plr})
- end
- else
- Functions.Hint(`{service.FormatPlayer(v, true)} is not an admin`, {plr})
+
+ if Admin.SlowMode then
+ slowCache[speakerPlayer] = os.time()
end
- end
+
+ return false
+ end)
+
+ AddLog("Script", "ChatService Handler Loaded")
+ elseif chatService == false then
+ AddLog("Script", "Using TextChatService; Handler Loaded")
+ else
+ warn("Place is missing ChatService; Vanilla Roblox chat related features may not work")
+ AddLog("Script", "ChatService Handler Not Found")
end
- };
-
- TempModerator = {
- Prefix = Settings.Prefix;
- Commands = {"tempmod", "tmod", "tempmoderator", "tmoderator"};
- Args = {"player"};
- Description = "Makes the target player(s) a temporary moderator; does not save";
- AdminLevel = "Admins";
- Dangerous = true;
- Function = function(plr: Player, args: {string}, data: {any})
- local senderLevel = data.PlayerData.Level
-
- for _, v in service.GetPlayers(plr, assert(args[1], "Missing target player (argument #1)")) do
- if senderLevel > Admin.GetLevel(v) then
- Admin.AddAdmin(v, "Moderators", true)
- Functions.LogAdminAction(plr, "Temporary Moderator", v.Name, "N/A")
- Functions.Notification("Notification", "You are a temp moderator. Click to view commands.", {v}, 10, "MatIcons://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Settings.Prefix}cmds')`))
- Functions.Hint(`{service.FormatPlayer(v, true)} is now a temp moderator`, {plr})
- else
- Functions.Hint(`{service.FormatPlayer(v, true)} is already the same admin level as you or higher`, {plr})
+ end)
+
+ --// Make sure the default ranks are always present for compatability with existing commands
+ local Ranks = Settings.Ranks
+ for rank, data in Defaults.Settings.Ranks do
+ if not Ranks[rank] then
+ for r, d in Ranks do
+ if d.Level == data.Level then
+ data.Hidden = true
+ break
end
end
+ Ranks[rank] = data
end
- };
+ end
- Moderator = {
- Prefix = Settings.Prefix;
- Commands = {"permmod", "pmod", "mod", "moderator", "pmoderator"};
- Args = {"player/user"};
- Description = "Makes the target player(s) a moderator; saves";
- AdminLevel = "Admins";
- Dangerous = true;
- Function = function(plr: Player, args: {string}, data: {any})
- local senderLevel = data.PlayerData.Level
+ if Settings.CustomRanks then
+ local Ranks = Settings.Ranks
+ for name, users in Settings.CustomRanks do
+ if not Ranks[name] then
+ Ranks[name] = {
+ Level = 1;
+ Users = users;
+ };
+ end
+ end
+ end
- for _, v in service.GetPlayers(plr, assert(args[1], "Missing target player (argument #1)"), {
- UseFakePlayer = true;
- })
- do
- if senderLevel > Admin.GetLevel(v) then
- Admin.AddAdmin(v, "Moderators")
- Functions.LogAdminAction(plr, "Promoted to Moderator", v.Name, "N/A")
- Functions.Notification("Notification", "You are a moderator. Click to view commands.", {v}, 10, "MatIcons://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Settings.Prefix}cmds')`))
- Functions.Hint(`{service.FormatPlayer(v, true)} is now a moderator`, {plr})
- else
- Functions.Hint(`{service.FormatPlayer(v, true)} is already the same admin level as you or higher`, {plr})
+ if Settings.CommandCooldowns then
+ for cmdName, cooldownData in Settings.CommandCooldowns do
+ local realCmd = Admin.GetCommand(cmdName)
+
+ if realCmd then
+ if cooldownData.Player then
+ realCmd.PlayerCooldown = cooldownData.Player
+ end
+
+ if cooldownData.Server then
+ realCmd.ServerCooldown = cooldownData.Server
+ end
+
+ if cooldownData.Cross then
+ realCmd.CrossCooldown = cooldownData.Cross
end
end
end
- };
-
- Broadcast = {
- Prefix = Settings.Prefix;
- Commands = {"broadcast", "bc"};
- Args = {"Message"};
- Filter = true;
- Description = "Makes a message in the chat window";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string}, data: {any})
- for _, v in service.GetPlayers() do
- if service.TextChatService and service.TextChatService.ChatVersion == Enum.ChatVersion.TextChatService then
- local TextToUse = args[1]
- if data.Options.Chat ~= true then
- TextToUse = service.SanitizeXML(args[1] or "Hello world!")
- end
- Remote.Send(
- v, "Function", "DisplaySystemMessageInTextChat", nil, `{
- string.format(`[%s] %s`, Settings.SystemTitle, service.Filter(TextToUse), plr, v)
- }`)
- else
- Remote.Send(v, "Function", "ChatMessage", string.format("[%s] %s", Settings.SystemTitle, service.Filter(args[1], plr, v)), Color3.fromRGB(255,64,77))
+ end
+
+ Admin.Init = nil;
+ AddLog("Script", "Admin Module Initialized")
+ end;
+
+ local function RunAfterPlugins(data)
+ --// Backup Map
+ if Settings.AutoBackup then
+ TrackTask("Thread: Initial Map Backup", Admin.RunCommand, false, `{Settings.Prefix}backupmap`)
+ end
+
+ --// Run OnStartup Commands
+ for i,v in Settings.OnStartup do
+ print(`Running startup command {v}`)
+ TrackTask(`Thread: Startup_Cmd: {v}`, Admin.RunCommand, false, v)
+ AddLog("Script", {
+ Text = `Startup: Executed {v}`;
+ Desc = `Executed startup command; {v}`;
+ })
+ end
+
+ --// Check if Shutdownlogs is set and if not then set it
+ if Core.DataStore and not Core.GetData("ShutdownLogs") then
+ Core.SetData("ShutdownLogs", {})
+ end
+
+ Admin.RunAfterPlugins = nil;
+ AddLog("Script", "Admin Module RunAfterPlugins Finished")
+ end
+
+ service.MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(player, id, purchased)
+ if Variables and player.Parent and table.find(Variables.DonorPass, id) and purchased then
+ Variables.CachedDonors[tostring(player.UserId)] = os.time()
+ end
+ end)
+
+ local function stripArgPlaceholders(alias)
+ return service.Trim(string.gsub(alias, "<%S+>", ""))
+ end
+
+ local function FormatAliasArgs(alias, aliasCmd, msg)
+ local uniqueArgs = {}
+ local argTab = {}
+ local numArgs = 0
+
+ --// First try to extract args info from the alias
+ for arg in string.gmatch(alias, "<(%S+)>") do
+ if arg ~= "" and arg ~= " " then
+ local arg = `<{arg}>`
+ if not uniqueArgs[arg] then
+ numArgs += 1
+ uniqueArgs[arg] = true
+ table.insert(argTab, arg)
+ end
+ end
+ end
+
+ --// If no args in alias string, check the command string instead and try to guess args based on order of appearance
+ if numArgs == 0 then
+ for arg in string.gmatch(aliasCmd, "<(%S+)>") do
+ if arg ~= "" and arg ~= " " then
+ local arg = `<{arg}>`
+ if not uniqueArgs[arg] then --// Get only unique placeholder args, repeats will be matched to the same arg pos
+ numArgs += 1
+ uniqueArgs[arg] = true --// :cmd
+ table.insert(argTab, arg)
end
end
end
- };
+ end
- ShutdownLogs = {
- Prefix = Settings.Prefix;
- Commands = {"shutdownlogs", "shutdownlog", "slogs", "shutdowns"};
- Args = {};
- Description = "Shows who shutdown or restarted a server and when";
- AdminLevel = "Admins";
- ListUpdater = function(plr: Player)
- local logs = Core.GetData("ShutdownLogs") or {}
- local tab = {}
- for i, v in logs do
- if v.Restart then v.Time ..= " [RESTART]" end
- tab[i] = {
- Text = `{v.Time}: {v.User}`;
- Desc = `Reason: {v.Reason}`;
- }
- end
- return tab
- end;
- Function = function(plr: Player, args: {string})
- Remote.MakeGui(plr, "List", {
- Title = "Shutdown Logs";
- Table = Logs.ListUpdaters.ShutdownLogs(plr);
- Update = "ShutdownLogs";
- })
+ local suppliedArgs = Admin.GetArgs(msg, numArgs) -- User supplied args (when running :alias arg)
+ local out = aliasCmd
+
+ local SanitizePattern = service.SanitizePattern
+ for i,argType in argTab do
+ local replaceWith = suppliedArgs[i]
+ if replaceWith then
+ out = string.gsub(out, SanitizePattern(argType), replaceWith)
end
- };
-
- ServerLock = {
- Prefix = Settings.Prefix;
- Commands = {"slock", "serverlock", "lockserver"};
- Args = {"on/off"};
- Description = "Enables/disables server lock";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- local arg = args[1] and string.lower(args[1])
- Functions.LogAdminAction(plr, "Server Lock", "Server is now " .. (if (arg == "on" or arg == "true") then "locked" else "unlocked"), "")
- if (not arg and Variables.ServerLock ~= true) or arg == "on" or arg == "true" then
- Variables.ServerLock = true
- Functions.Hint("Server Locked", service.Players:GetPlayers())
- elseif Variables.ServerLock == true or arg == "off" or arg == "false" then
- Variables.ServerLock = false
- Functions.Hint("Server Unlocked", service.Players:GetPlayers())
- end
- end
- };
-
- Whitelist = {
- Prefix = Settings.Prefix;
- Commands = {"wl", "enablewhitelist", "whitelist"};
- Args = {"on/off/add/remove/list/clear", "optional player"};
- Description = "Enables/disables the server whitelist; :wl username to add them to the whitelist";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- local sub = string.lower(args[1])
-
- if sub == "on" or sub == "enable" then
- Variables.Whitelist.Enabled = true
- Functions.Hint("Enabled server whitelist", service.Players:GetPlayers())
- elseif sub == "off" or sub == "disable" then
- Variables.Whitelist.Enabled = false
- Functions.Hint("Disabled server whitelist", service.Players:GetPlayers())
- elseif sub == "add" then
- if args[2] then
- local plrs = service.GetPlayers(plr, args[2], {
- DontError = true;
- IsServer = false;
- IsKicking = false;
- NoFakePlayer = false;
- })
- if #plrs>0 then
- for _, v in plrs do
- table.insert(Variables.Whitelist.Lists.Settings, `{v.Name}:{v.UserId}`)
- Functions.Hint(`Added {service.FormatPlayer(v)} to the whitelist`, {plr})
- end
- else
- table.insert(Variables.Whitelist.Lists.Settings, args[2])
- end
+ end
+
+ return out
+ end
+
+ server.Admin = {
+ Init = Init;
+ RunAfterPlugins = RunAfterPlugins;
+
+ SpecialLevels = {};
+ TempAdmins = {};
+
+ PrefixCache = {};
+ CommandCache = {};
+ SlowCache = {};
+ UserIdCache = {};
+ UsernameCache = {};
+ GroupsCache = {};
+
+ BlankPrefix = false;
+
+ --// How long admin levels will be cached (unless forcibly updated via something like :admin user)
+ AdminLevelCacheTimeout = 30;
+
+ CheckSlowMode = function(p: Player)
+ if Admin.SlowMode and Admin.SlowCache[p] and os.time() - Admin.SlowCache[p] < Admin.SlowMode then
+ return true
+ else
+ return false
+ end
+ end,
+
+ DoHideChatCmd = function(p: Player, message: string, data: {[string]: any}?)
+ local pData = data or Core.GetPlayer(p)
+ if pData.Client.HideChatCommands then
+ if Admin.BlankPrefix and
+
+ (string.sub(message,1,1) ~= Settings.Prefix or string.sub(message,1,1) ~= Settings.PlayerPrefix or (type(Settings.Prefix) == "table" and not table.find(Settings.Prefix, string.sub(message,1,1)))) then
+ local isCMD = Admin.GetCommand(message)
+ if isCMD then
+ return true
else
- error("Missing user argument")
+ return false
end
- elseif sub == "remove" then
- if args[2] then
- for i, v in Variables.Whitelist.Lists.Settings do
- if string.sub(string.lower(v), 1,#args[2]) == string.lower(args[2])then
- table.remove(Variables.Whitelist.Lists.Settings,i)
- Functions.Hint(`Removed {v} from the whitelist`, {plr})
- end
- end
- else
- error("Missing user argument")
+ elseif (string.sub(message,1,1) == Settings.Prefix or string.sub(message,1,1) == Settings.PlayerPrefix or (type(Settings.Prefix) == "table" and table.find(Settings.Prefix, string.sub(message,1,1))))
+ and string.sub(message,2,2) ~= string.sub(message,1,1) then
+ return true;
+ end
+ end
+ end;
+
+ GetPlayerGroups = function(p: Player)
+ if not p or p.Parent ~= service.Players then
+ return {}
+ end
+ return Admin.GetGroups(p.UserId)
+ end;
+
+ GetPlayerGroup = function(p, group)
+ local groups = Admin.GetPlayerGroups(p)
+ local isId = type(group) == "number"
+ if groups and #groups > 0 then
+ for _, g in groups do
+ if (isId and g.Id == group) or (not isId and g.Name == group) then
+ return g
end
- elseif sub == "list" then
- local Tab = {}
- for Key, List in Variables.Whitelist.Lists do
- local Prefix = Key == "Settings" and "" or `[{Key}] `
- for _, User in List do
- table.insert(Tab, {Text = Prefix .. User, Desc = User})
- end
+ end
+ end
+ end;
+
+ GetGroups = function(uid, updateCache)
+ uid = tonumber(uid)
+
+ if type(uid) == "number" then
+ local existCache = Admin.GroupsCache[uid]
+ local canUpdate = false
+
+ if not updateCache then
+ --> Feel free to adjust the time to update over or less than 300 seconds (5 minutes).
+ --> 300 seconds is recommended in the event of unexpected server breakdowns with Roblox and faster performance.
+ if (existCache and (os.time()-existCache.LastUpdated > 300)) or not existCache then
+ canUpdate = true
end
- Remote.MakeGui(plr, "List", {Title = "Whitelist List"; Tab = Tab;})
- elseif sub == "clear" then
- Variables.Whitelist.Lists.Settings = {}
- Functions.Hint("Cleared server whitelist", service.Players:GetPlayers())
else
- error("Invalid subcommand (on/off/add/remove/list/clear)")
- end
- end
- };
-
- SystemNotify = {
- Prefix = Settings.Prefix;
- Commands = {"sn", "systemnotify", "sysnotif", "sysnotify", "systemsmallmessage", "snmessage", "snmsg", "ssmsg", "ssmessage"};
- Args = {"message"};
- Filter = true;
- Description = "Makes a system small message";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- assert(args[1], "Missing message")
- for _, v in service.GetPlayers() do
- Remote.RemoveGui(v, "Notify")
- Functions.Notify(Settings.SystemTitle, service.Filter(args[1], plr, v), {v})
- end
- end
- };
-
- Notif = {
- Prefix = Settings.Prefix;
- Commands = {"setmessage", "notif", "setmsg", "permhint"};
- Args = {"message OR off"};
- Filter = true;
- Description = "Sets a small hint message at the top of the screen";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- assert(args[1], "Missing message (or enter 'off' to disable)")
-
- if args[1] == "off" or args[1] == "false" then
- Variables.NotifMessage = nil
- for _, v in service.GetPlayers() do
- Remote.RemoveGui(v, "Notif")
+ canUpdate = true
+ end
+
+ if canUpdate then
+ local cacheTab = {
+ Groups = (existCache and existCache.Groups) or {};
+ LastUpdated = os.time();
+ }
+ Admin.GroupsCache[uid] = cacheTab
+
+ local suc,groups = pcall(function()
+ return service.GroupService:GetGroupsAsync(uid) or {}
+ end)
+
+ if suc and type(groups) == "table" then
+ cacheTab.Groups = groups
+ return cacheTab.Groups
end
+
+ Admin.GroupsCache[uid] = cacheTab
+ return table.clone(cacheTab.Groups)
else
- Variables.NotifMessage = args[1]
- for _, v in service.GetPlayers() do
- Remote.MakeGui(v, "Notif", {
- Message = Variables.NotifMessage;
- })
- end
+ return table.clone((existCache and existCache.Groups) or {})
end
end
- };
-
- SetBanMessage = {
- Prefix = Settings.Prefix;
- Commands = {"setbanmessage", "setbmsg"};
- Args = {"message"};
- Filter = true;
- Description = "Sets the ban message banned players see";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- Variables.BanMessage = assert(args[1], "Missing message (argument #1)")
- end
- };
-
- SetLockMessage = {
- Prefix = Settings.Prefix;
- Commands = {"setlockmessage", "slockmsg", "setlmsg"};
- Args = {"message"};
- Filter = true;
- Description = "Sets the lock message unwhitelisted players see if :whitelist or :slock is on";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- Variables.LockMessage = assert(args[1], "Missing message (argument #1)")
- end
- };
-
- SystemMessage = {
- Prefix = Settings.Prefix;
- Commands = {"sm", "systemmessage", "sysmsg"};
- Args = {"message"};
- Filter = true;
- Description = "Same as message but says SYSTEM MESSAGE instead of your name, or whatever system message title is server to...";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- Functions.Message(Settings.SystemTitle, service.BroadcastFilter(assert(args[1], "Missing message (argument #1)"), plr), service.GetPlayers(), true)
- end
- };
-
- SetCoreGuiEnabled = {
- Prefix = Settings.Prefix;
- Commands = {"setcoreguienabled", "setcoreenabled", "showcoregui", "setcoregui", "setcgui", "setcore", "setcge"};
- Args = {"player", "All/Backpack/Chat/EmotesMenu/Health/PlayerList", "true/false"};
- Description = "Enables or disables CoreGui elements for the target player(s)";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- assert(args[3], "Missing state (argument #3)")
- local enable = if args[3]:lower() == "on" or args[3]:lower() == "true" then true elseif args[3]:lower() == "off" or args[3]:lower() == "false" then false else nil
- assert(enable ~= nil, `Invalid state '{args[3]}'; please supply 'true' or 'false' (argument #3)`)
- for _,v in service.GetPlayers(plr, args[1]) do
- if string.lower(args[3]) == "on" or string.lower(args[3]) == "true" then
- Remote.Send(v, "Function", "SetCoreGuiEnabled", args[2], true)
- elseif string.lower(args[3]) == 'off' or string.lower(args[3]) == "false" then
- Remote.Send(v, "Function", "SetCoreGuiEnabled", args[2], false)
+ end;
+
+ GetGroupLevel = function(uid, groupId)
+ groupId = tonumber(groupId)
+
+ if groupId then
+ local groups = Admin.GetGroups(uid) or {}
+
+ for _, group in groups do
+ if group.Id == groupId then
+ return group.Rank
end
end
end
- };
-
- Alert = {
- Prefix = Settings.Prefix;
- Commands = {"alert", "alarm", "annoy"};
- Args = {"player", "message"};
- Filter = true;
- Description = "Get someone's attention";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- for _, v in service.GetPlayers(plr,string.lower(args[1]))do
- Remote.MakeGui(v, "Alert", {Message = args[2] and service.Filter(args[2],plr, v) or "Wake up; Your attention is required"})
- end
- end
- };
-
- LockMap = {
- Prefix = Settings.Prefix;
- Commands = {"lockmap"};
- Args = {};
- Description = "Locks the map";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- for _, obj in workspace:GetDescendants()do
- if obj:IsA("BasePart")then
- obj.Locked = true
+
+ return 0
+ end;
+
+ CheckInGroup = function(uid, groupId)
+ local groups = Admin.GetGroups(uid) or {}
+ groupId = tonumber(groupId)
+
+ if groupId then
+ for i,group in groups do
+ if group.Id == groupId then
+ return true
end
end
end
- };
- UnlockMap = {
- Prefix = Settings.Prefix;
- Commands = {"unlockmap"};
- Args = {};
- Description = "Unlocks the map";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- for _, obj in workspace:GetDescendants()do
- if obj:IsA("BasePart")then
- obj.Locked = false
- end
+ return false
+ end,
+
+ IsLax = function(str)
+ for _, v in {"plr", "user", "player", "brickcolor"} do
+ if string.match(string.lower(str), v) then
+ return true
end
end
- };
-
- BuildingTools = {
- Prefix = Settings.Prefix;
- Commands = {"btools", "f3x", "buildtools", "buildingtools", "buildertools"};
- Args = {"player"};
- Description = "Gives the target player(s) F3X building tools.";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- local F3X = Deps.Assets:FindFirstChild("F3X Deps") and (function(deps)
- local F3X = service.New("Tool", {
- GripPos = Vector3.new(0, 0, 0.4),
- CanBeDropped = false,
- ManualActivationOnly = false,
- ToolTip = "Building Tools by F3X",
- Name = "Building Tools"
- }, true)
- local clonedDeps = deps:Clone()
-
- for _, obj in clonedDeps:GetDescendants() do
- if obj:IsA("BaseScript") then
- obj.Disabled = false
- end
- end
- for _, obj in clonedDeps:GetChildren() do
- obj.Parent = F3X
- end
- clonedDeps:Destroy()
- return F3X
- end)(Deps.Assets:FindFirstChild("F3X Deps")) or Variables.F3XCached and Variables.F3XCached:Clone() or require(580330877)()
- Variables.F3XCached = Variables.F3XCached or F3X:Clone()
- service.New("StringValue", {
- Name = `__ADONIS_VARIABLES_{Variables.CodeName}`,
- Parent = F3X
- })
+ return false
+ end,
- for _, v in service.GetPlayers(plr, args[1]) do
- local Backpack = v:FindFirstChildOfClass("Backpack")
+ IsMuted = function(player)
+ local DoCheck = Admin.DoCheck
+ for _, v in Settings.Muted do
+ if DoCheck(player, v) then
+ return true
+ end
+ end
- if Backpack then
- F3X:Clone().Parent = Backpack
- end
+ for _, v in HTTP.Trello.Mutes do
+ if DoCheck(player, v) then
+ return true
end
+ end
- F3X:Destroy()
+ if HTTP.WebPanel.Mutes then
+ for _, v in HTTP.WebPanel.Mutes do
+ if DoCheck(player, v) then
+ return true
+ end
+ end
+ end
+ end;
+
+ DoCheck = function(pObj, check, banCheck)
+ local pType = typeof(pObj)
+ local cType = typeof(check)
+
+ local pUnWrapped = service.UnWrap(pObj)
+
+ local plr: Player = if pType == "userdata" and pObj:IsA("Player") then pObj
+ elseif pType == "number" then service.Players:GetPlayerByUserId(pObj)
+ elseif pType == "string" then service.Players:FindFirstChild(pObj)
+ elseif typeof(pUnWrapped) == "Instance" and pUnWrapped:IsA("Player") then pUnWrapped
+ elseif pType == "userdata" then service.Players:GetPlayerByUserId(pObj.UserId)
+ else nil
+ if not plr then
+ return false
end
- };
- Insert = {
- Prefix = Settings.Prefix;
- Commands = {"insert", "ins"};
- Args = {"id"};
- Description = "Inserts whatever object belongs to the ID you supply, the object must be in the place owner's or Roblox's inventory";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- local id = string.lower(args[1])
+ if cType == "number" then
+ return plr.UserId == check
+ elseif cType == "string" then
+ if plr.Name == check then
+ return true
+ end
- for i, v in Variables.InsertList do
- if id == string.lower(v.Name)then
- id = v.ID
- break
+ local filterName, filterData = string.match(check, "^(.-):(.+)$")
+ if filterName then
+ filterName = string.lower(filterName)
+ else
+ return false
+ end
+ if filterName == "group" then
+ local groupId = tonumber((string.match(filterData, "^%d+")))
+ if groupId then
+ local plrRank = Admin.GetGroupLevel(plr.UserId, groupId)
+ local requiredRank,noRank = tonumber((string.match(filterData, "^%d+:(.+)$"))), string.match(filterData,"^%d+$")
+ if requiredRank then
+ if requiredRank < 0 then
+ return plrRank >= math.abs(requiredRank)
+ else
+ return plrRank == requiredRank
+ end
+ elseif noRank then
+ return plrRank > 0
+ end
+ end
+ return false
+ elseif filterName == "item" then
+ local itemId = tonumber((string.match(filterData, "^%d+")))
+ return itemId and service.CheckAssetOwnership(plr, itemId)
+ elseif filterName == "gamepass" then
+ local gamepassId = tonumber((string.match(filterData, "^%d+")))
+ return gamepassId and service.CheckPassOwnership(plr, gamepassId)
+ elseif filterName == "subscription" then
+ local subscriptionId = string.match(filterData, "^EXP%-%d+$")
+ return subscriptionId and service.CheckSubscriptionStatus(plr, subscriptionId)
+ else
+ local username, userId = string.match(check, "^(.*):(.*)")
+ if username and userId and (plr.UserId == tonumber(userId) or string.lower(plr.Name) == string.lower(username)) then
+ return true
end
- end
- for i, v in HTTP.Trello.InsertList do
- if id == string.lower(v.Name) then
- id = v.ID
- break
+ if not banCheck and type(check) == "string" and not string.find(check, ":") then
+ local cache = Functions.GetUserIdFromNameAsync(check)
+ if cache and plr.UserId == cache then
+ return true
+ end
end
end
-
- local obj = service.Insert(tonumber(id), true)
- if obj and plr.Character then
- table.insert(Variables.InsertedObjects, obj)
- obj.Parent = workspace
- pcall(obj.MakeJoints, obj)
- obj:PivotTo(plr.Character:GetPivot())
- end
- end
- };
-
- SaveTool = {
- Prefix = Settings.Prefix;
- Commands = {"addtool", "savetool", "maketool"};
- Args = {"optional player", "optional new tool name"};
- Description = `Saves the equipped tool to the storage so that it can be inserted using {Settings.Prefix}give`;
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- for _, v in service.GetPlayers(plr, args[1]) do
- local tool = v.Character and v.Character:FindFirstChildWhichIsA("BackpackItem")
- if tool then
- tool = tool:Clone()
- if args[2] then tool.Name = args[2] end
- tool.Parent = service.UnWrap(Settings.Storage)
- Variables.SavedTools[tool] = service.FormatPlayer(plr)
- Functions.Hint(`Added tool: {tool.Name}`, {plr})
- elseif not args[1] then
- error("You must have an equipped tool to add to the storage.")
+ elseif cType == "table" then
+ local groupId, rank = check.Group, check.Rank
+ if groupId and rank then
+ local plrGroupInfo = Admin.GetPlayerGroup(plr, groupId)
+ if plrGroupInfo then
+ local plrRank = plrGroupInfo.Rank
+ return plrRank == rank or (rank < 0 and plrRank >= math.abs(rank))
end
end
end
- };
-
- ClearSavedTools = {
- Prefix = Settings.Prefix;
- Commands = {"clraddedtools", "clearaddedtools", "clearsavedtools", "clrsavedtools"};
- Args = {};
- Description = `Removes any tools in the storage added using {Settings.Prefix}savetool`;
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- local count = 0
- for tool in Variables.SavedTools do
- count += 1
- tool:Destroy()
- end
- table.clear(Variables.SavedTools)
- Functions.Hint(string.format("Cleared %d saved tool%s.", count, count == 1 and "" or "s"), {plr})
- end
- };
-
- NewTeam = {
- Prefix = Settings.Prefix;
- Commands = {"newteam", "createteam", "maketeam"};
- Args = {"name", "BrickColor"};
- Filter = true;
- Description = "Make a new team with the specified name and color";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- local teamName = assert(args[1], "Missing team name (argument #1)")
- local teamColor = Functions.ParseBrickColor(args[2])
-
- if service.Teams:FindFirstChild(teamName) then
- Functions.Hint(string.format("Team '%s' already exists!", teamName), {plr})
- return;
- end
-
- service.New("Team", {
- Parent = service.Teams;
- Name = teamName;
- TeamColor = teamColor;
- AutoAssignable = false;
- })
- if Settings.CommandFeedback then
- Functions.Hint(string.format("Created new team '%s' (%s)", teamName, teamColor.Name), {plr})
- end
- end
- };
-
- RemoveTeam = {
- Prefix = Settings.Prefix;
- Commands = {"removeteam", "deleteteam"};
- Args = {"name"};
- Description = "Remove the specified team";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- for _, v in service.Teams:GetTeams() do
- if string.sub(string.lower(v.Name), 1, #args[1]) == string.lower(args[1]) then
- local ans = Remote.GetGui(plr, "YesNoPrompt", { Question = `Remove team: '{v.Name}'?` })
-
- if ans == "Yes" then
- v:Destroy()
- return Functions.Hint(`Removed team {v.Name}`, {plr})
- else
- return Functions.Hint("Cancelled team removal operation", {plr})
- end
- end
+
+ return check == plr
+ end;
+
+ LevelToList = function(lvl)
+ local lvl = tonumber(lvl)
+ if not lvl then return nil end
+ local listName = Admin.LevelToListName(lvl)
+ if listName then
+ local list = Settings.Ranks[listName];
+ if list then
+ return list.Users, listName, list;
end
end
- };
+ end;
- RestoreMap = {
- Prefix = Settings.Prefix;
- Commands = {"restoremap", "maprestore", "rmap"};
- Args = {};
- Description = "Restore the map to the the way it was the last time it was backed up";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- local plrName = plr and service.FormatPlayer(plr) or ""
+ LevelToListName = function(lvl)
+ if lvl > 999 then
+ return "Place Owner"
+ elseif lvl == 0 then
+ return "Players"
+ end
- if not Variables.MapBackup then
- error("Cannot restore when there are no backup maps!", 0)
- return
+ --// Check if this is a default rank and if the level matches the default (so stuff like [Trello] Admins doesn't appear in the command list)
+ for i,v in server.Defaults.Settings.Ranks do
+ local tRank = Settings.Ranks[i];
+ if tRank and tRank.Level == v.Level and v.Level == lvl then
+ return i
end
- if Variables.RestoringMap then
- error("Map has not been backed up",0)
- return
+ end
+
+ for i,v in Settings.Ranks do
+ if v.Level == lvl then
+ return i
end
- if Variables.BackingupMap then
- error("Cannot restore map while backing up map is in process!", 0)
- return
+ end
+ end;
+
+ UpdateCachedLevel = function(p, data)
+ local data = data or Core.GetPlayer(p)
+ local oLevel, oRank = data.AdminLevel, data.AdminRank
+ local level, rank = Admin.GetUpdatedLevel(p, data)
+
+ data.AdminLevel = level
+ data.AdminRank = rank
+ data.LastLevelUpdate = os.time()
+
+ AddLog("Script", {
+ Text = `Updating cached level for {p.Name}`;
+ Desc = `Updating the cached admin level for {p.Name}`;
+ Player = p;
+ })
+
+ if Settings.Console and (oLevel ~= level or oRank ~= rank) then
+ if not Settings.Console_AdminsOnly or (Settings.Console_AdminsOnly and level > 0) then
+ task.defer(Remote.RefreshGui, p, "Console")
+ else
+ task.defer(Remote.RemoveGui, p, "Console")
end
+ end
+
+ return level, rank
+ end;
+
+ GetLevel = function(p)
+ local data = Core.GetPlayer(p)
+ local level = data.AdminLevel
+ local rank = data.AdminRank
+ local lastUpdate = data.LastLevelUpdate or 0
+ local clients = Remote.Clients
+ local key = tostring(p.UserId)
- Variables.RestoringMap = true
- Functions.Hint("Restoring Map...", service.Players:GetPlayers())
- workspace.Gravity = Variables.OriginalGravity
+ local currentTime = os.time()
- for _, obj in workspace:GetChildren() do
- if obj.ClassName ~= "Terrain" and not service.Players:GetPlayerFromCharacter(obj) then
- obj:Destroy()
- service.RunService.Stepped:Wait()
+ if (not level or not lastUpdate or currentTime - lastUpdate > Admin.AdminLevelCacheTimeout) or lastUpdate > currentTime then
+ local newLevel, newRank = Admin.UpdateCachedLevel(p, data)
+
+ if clients[key] and level and newLevel and type(p) == "userdata" and p:IsA("Player") then
+ if newLevel < level then
+ Functions.Hint(`Your admin level has been reduced to {newLevel} [{newRank or "Unknown"}]`, {p})
+ elseif newLevel > level then
+ Functions.Hint(`Your admin level has been increased to {newLevel} [{newRank or "Unknown"}]`, {p})
end
end
- local new = Variables.MapBackup:Clone()
- for _, obj in new:GetChildren() do
- obj.Parent = workspace
- if obj:IsA("Model") then
- obj:MakeJoints()
- end
+ return newLevel, newRank
+ end
+
+ return level or 0, rank;
+ end;
+
+ GetUpdatedLevel = function(p, data)
+ local checkTable = Admin.CheckTable
+ local doCheck = Admin.DoCheck
+
+ for _, admin in Admin.SpecialLevels do
+ if doCheck(p, admin.Player) then
+ return admin.Level, admin.Rank
end
- new:Destroy()
+ end
- local Terrain = workspace.Terrain or workspace:FindFirstChildOfClass("Terrain")
- if Terrain and Variables.TerrainMapBackup then
- Terrain:Clear()
- Terrain:PasteRegion(Variables.TerrainMapBackup, Terrain.MaxExtents.Min, true)
+ local sortedRanks = {}
+ for rank, data in Settings.Ranks do
+ table.insert(sortedRanks, {
+ Rank = rank;
+ Users = data.Users;
+ Level = data.Level;
+ });
+ end
+
+ table.sort(sortedRanks, function(t1, t2)
+ return t1.Level > t2.Level
+ end)
+
+ local highestLevel = 0
+ local highestRank = nil
+
+ for _, data in sortedRanks do
+ local level = data.Level
+ if level > highestLevel then
+ for _, v in data.Users do
+ if doCheck(p, v) then
+ highestLevel, highestRank = level, data.Rank
+ break
+ end
+ end
end
+ end
- task.wait()
+ if Admin.IsPlaceOwner(p) and highestLevel < 1000 then
+ return 1000, "Place Owner"
+ end
- Admin.RunCommand(`{Settings.Prefix}fixlighting`)
- Admin.RunCommand(`{Settings.Prefix}respawn`, "all")
- Variables.RestoringMap = false
- Functions.Hint('Map Restore Complete.',service.Players:GetPlayers())
+ return highestLevel, highestRank
+ end;
- Logs:AddLog("Script", {
- Text = "Map Restoration Complete",
- Desc = `{plrName} has restored the map.`,
- })
+ IsPlaceOwner = function(p)
+ if type(p) == "userdata" and p:IsA("Player") then
+ --// These are my accounts; Lately I've been using my game dev account(698712377) more so I'm adding it so I can debug without having to sign out and back in (it's really a pain)
+ --// Disable CreatorPowers in settings if you don't trust me. It's not like I lose or gain anything either way. Just re-enable it BEFORE telling me there's an issue with the script so I can go to your place and test it.
+ if Settings.CreatorPowers and table.find(Variables.DeveloperWhitelist, p.UserId) then
+ return true
+ end
+
+ if tonumber(CreatorId) and p.UserId == CreatorId then
+ return true
+ end
+
+ if p.UserId == -1 and Variables.IsStudio then --// To account for player emulators in multi-client Studio tests
+ return true
+ end
end
- };
-
- ScriptBuilder = {
- Prefix = Settings.Prefix;
- Commands = {"scriptbuilder", "scriptb", "sb"};
- Args = {"create/remove/edit/close/clear/append/run/stop/list", "localscript/script", "scriptName", "data"};
- Description = "[Deprecated] Script Builder; make a script, then edit it and chat it's code or use :sb append ";
- AdminLevel = "Admins";
- Hidden = true;
- NoFilter = true;
- CrossServerDenied = true;
- Function = function(plr: Player, args: {string})
- assert(Settings.CodeExecution, "CodeExecution must be enabled for this command to work")
- local sb = Variables.ScriptBuilder[tostring(plr.UserId)]
- if not sb then
- sb = {
- Script = {};
- LocalScript = {};
- Events = {};
+ end;
+
+ CheckAdmin = function(p)
+ return Admin.GetLevel(p) > 0
+ end;
+
+ SetLevel = function(p, level, doSave, rankName)
+ local current, rank = Admin.GetLevel(p)
+
+ if tonumber(level) then
+ if current >= 1000 then
+ return false
+ else
+ Admin.SpecialLevels[tostring(p.UserId)] = {
+ Player = p.UserId,
+ Level = level,
+ Rank = rankName
}
- Variables.ScriptBuilder[tostring(plr.UserId)] = sb
end
+ elseif level == "Reset" then
+ Admin.SpecialLevels[tostring(p.UserId)] = nil
+ end
- local action = string.lower(args[1])
- local class = args[2] or "LocalScript"
- local name = args[3]
+ Admin.UpdateCachedLevel(p)
+ end;
- if string.lower(class) == "script" or string.lower(class) == "s" then
- class = "Script"
- elseif string.lower(class) == "clientscript" or string.lower(class) == "cs" then
- class = "ClientScript"
- --elseif string.lower(class) == "localscript" or string.lower(class) == "ls" then
- -- class = "LocalScript"
- else
- class = "LocalScript"
+ IsTempAdmin = function(p)
+ local DoCheck = Admin.DoCheck
+ for i,v in Admin.TempAdmins do
+ if DoCheck(p,v) then
+ return true, i
end
+ end
+ end;
- if action == "create" then
- assert(args[1] and args[2] and args[3], "Missing arguments")
- local code = args[4] or " "
+ RemoveAdmin = function(p, temp, override)
+ local current, rank = Admin.GetLevel(p)
+ local listData = rank and Settings.Ranks[rank]
+ local listName = listData and rank
+ local list = listData and listData.Users
- if sb[class][name] then
- pcall(function()
- sb[class][name].Script.Disabled = true
- sb[class][name].Script:Destroy()
- end)
- if sb.ChatEvent then
- sb.ChatEvent:Disconnect()
- end
- end
+ local isTemp,tempInd = Admin.IsTempAdmin(p)
- local wrapped,scr = Core.NewScript(class,code,false,true)
+ if isTemp then
+ temp = true
+ table.remove(Admin.TempAdmins, tempInd)
+ end
- sb[class][name] = {
- Wrapped = wrapped;
- Script = scr;
- }
+ if override then
+ temp = false
+ end
- if args[4] then
- Functions.Hint(`Created {class} {name} and appended text`, {plr})
- else
- Functions.Hint(`Created {class} {name}`, {plr})
- end
- elseif action == "edit" then
- assert(args[1] and args[2] and args[3], "Missing arguments")
- if sb[class][name] then
- local scr = sb[class][name].Script
- local tab = Core.GetScript(scr)
- if scr and tab then
- sb[class][name].Event = plr.Chatted:Connect(function(msg)
- if string.sub(msg, 1,#(`{Settings.Prefix}sb`)) ~= `{Settings.Prefix}sb` then
- tab.Source ..= `\n{msg}`
- Functions.Hint(`Appended message to {class} {name}`, {plr})
- end
- end)
- Functions.Hint(`Now editing {class} {name}; Chats will be appended`, {plr})
- end
- else
- error(`{class} {name} not found!`)
- end
- elseif action == "close" then
- assert(args[1] and args[2] and args[3], "Missing arguments")
- local scr = sb[class][name].Script
- local tab = Core.GetScript(scr)
- if sb[class][name] then
- if sb[class][name].Event then
- sb[class][name].Event:Disconnect()
- sb[class][name].Event = nil
- Functions.Hint(`No longer editing {class} {name}`, {plr})
+ if type(p) == "userdata" then
+ Admin.SetLevel(p, 0)
+ end
+
+ if list then
+ local DoCheck = Admin.DoCheck
+ for ind,check in list do
+ if DoCheck(p, check) and not (type(check) == "string" and (string.match(check,"^Group:") or string.match(check,"^Item:"))) then
+ table.remove(list, ind)
+
+ if not temp and Settings.SaveAdmins then
+ TrackTask("Thread: RemoveAdmin", Core.DoSave, false, {
+ Type = "TableRemove";
+ Table = {"Settings", "Ranks", listName, "Users"};
+ Value = check;
+ })
end
- else
- error(`{class} {name} not found!`)
end
- elseif action == "clear" then
- assert(args[1] and args[2] and args[3], "Missing arguments")
- local scr = sb[class][name].Script
- local tab = Core.GetScript(scr)
- if scr and tab then
- tab.Source = " "
- Functions.Hint(`Cleared {class} {name}`, {plr})
- else
- error(`{class} {name} not found!`)
+ end
+ end
+
+ Admin.UpdateCachedLevel(p)
+ end;
+
+ AddAdmin = function(p, level, temp)
+ local current, rank = Admin.GetLevel(p)
+ local list = rank and Settings.Ranks[rank]
+ local levelName, newRank, newList
+
+ if type(level) == "string" then
+ local newRank = Settings.Ranks[level]
+ levelName = newRank and level
+ newList = newRank and newRank.Users
+ level = (newRank and newRank.Level) or Admin.StringToComLevel(levelName) or level
+ else
+ local nL, nLN = Admin.LevelToList(level)
+ levelName = nLN
+ newRank = nLN
+ newList = nL
+ end
+
+ Admin.RemoveAdmin(p, temp)
+ Admin.SetLevel(p, level, nil, levelName)
+
+ if temp then
+ table.insert(Admin.TempAdmins, p)
+ end
+
+ if list and type(list) == "table" then
+ local index,value
+
+ for ind,ent in list do
+ if (type(ent)=="number" or type(ent)=="string") and (ent==p.UserId or string.lower(ent)==string.lower(p.Name) or string.lower(ent)==string.lower(`{p.Name}:{p.UserId}`)) then
+ index = ind
+ value = ent
end
- elseif action == "remove" then
- assert(args[1] and args[2] and args[3], "Missing arguments")
- if sb[class][name] then
- pcall(function()
- sb[class][name].Script.Disabled = true
- sb[class][name].Script:Destroy()
- end)
- if sb.ChatEvent then
- sb.ChatEvent:Disconnect()
- sb.ChatEvent = nil
- end
- sb[class][name] = nil
- else
- error(`{class} {name} not found!`)
+ end
+
+ if index and value then
+ table.remove(list, index)
+ end
+ end
+
+ local value = `{p.Name}:{p.UserId}`
+
+ if newList then
+ table.insert(newList, value)
+
+ if Settings.SaveAdmins and levelName and not temp then
+ TrackTask("Thread: SaveAdmin", Core.DoSave, false, {
+ Type = "TableAdd";
+ Table = {"Settings", "Ranks", levelName, "Users"};
+ Value = value
+ })
+ end
+ end
+
+ Admin.UpdateCachedLevel(p)
+ end;
+
+ CheckDonor = function(p)
+ local key = tostring(p.UserId)
+ if Variables.CachedDonors[key] then
+ return true
+ else
+ local pGroup = Admin.GetPlayerGroup(p, 886423)
+ for _, pass in Variables.DonorPass do
+ if p.Parent ~= service.Players then
+ return false
end
- elseif action == "append" then
- assert(args[1] and args[2] and args[3] and args[4], "Missing arguments")
- if sb[class][name] then
- local scr = sb[class][name].Script
- local tab = Core.GetScript(scr)
- if scr and tab then
- tab.Source ..= `\n{args[4]}`
- Functions.Hint(`Appended message to {class} {name}`, {plr})
- end
- else
- error(`{class} {name} not found!`)
+
+ local ran, ret
+ if type(pass) == "number" then
+ ran, ret = pcall(service.MarketPlace.UserOwnsGamePassAsync, service.MarketPlace, p.UserId, pass)
+ elseif type(pass) == "string" and tonumber(pass) then
+ ran, ret = pcall(service.MarketPlace.PlayerOwnsAsset, service.MarketPlace, p, tonumber(pass))
end
- elseif action == "run" then
- assert(args[1] and args[2] and args[3], "Missing arguments")
- if sb[class][name] then
- if class == "LocalScript" then
- sb[class][name].Script.Parent = plr:FindFirstChildOfClass("Backpack")
- else
- sb[class][name].Script.Parent = service.ServerScriptService
- end
- sb[class][name].Script.Disabled = true
- task.wait(0.03)
- sb[class][name].Script.Disabled = false
- Functions.Hint(`Running {class} {name}`, {plr})
- else
- error(`{class} {name} not found!`)
+
+ if (ran and ret) or (pGroup and pGroup.Rank >= 10) then --// Complimentary donor access is given to Adonis contributors & developers.
+ Variables.CachedDonors[key] = os.time()
+ return true
end
- elseif action == "stop" then
- assert(args[1] and args[2] and args[3], "Missing arguments")
- if sb[class][name] then
- sb[class][name].Script.Disabled = true
- Functions.Hint(`Stopped {class} {name}`, {plr})
+ end
+ end
+ end;
+
+ CheckBan = function(p)
+ local doCheck = Admin.DoCheck
+ local banCheck = Admin.DoBanCheck
+
+ for ind, admin in Settings.Banned do
+ if (type(admin) == "table" and ((admin.UserId and doCheck(p, admin.UserId, true)) or (admin.Name and not admin.UserId and doCheck(p, admin.Name, true)))) or doCheck(p, admin, true) then
+ return true, (type(admin) == "table" and admin.Reason)
+ end
+ end
+
+ for ind, ban in Core.Variables.TimeBans do
+ if p.UserId == ban.UserId then
+ if ban.EndTime-os.time() <= 0 then
+ table.remove(Core.Variables.TimeBans, ind)
else
- error(`{class} {name} not found!`)
- end
- elseif action == "list" then
- local tab = {}
- for i, v in sb.Script do
- table.insert(tab, {Text = `Script: {i}`, Desc = `Running: {v.Script.Disabled}`})
+ return true, `\n {ban.Reason or "(No reason provided.)"}\n | Banned until {service.FormatTime(ban.EndTime, {WithWrittenDate = true})}`
end
+ end
+ end
- for i, v in sb.LocalScript do
- table.insert(tab, {Text = `LocalScript: {i}`, Desc = `Running: {v.Script.Disabled}`})
- end
+ for ind, admin in HTTP.Trello.Bans do
+ local name = type(admin) == "table" and admin.Name or admin
+ if doCheck(p, name) or banCheck(p, name) then
+ return true, (type(admin) == "table" and admin.Reason and service.Filter(admin.Reason, p, p))
+ end
+ end
- Remote.MakeGui(plr, "List", {Title = "SB Scripts", Table = tab})
- end
- end
- };
-
- MakeScript = {
- Prefix = Settings.Prefix;
- Commands = {"s", "ss", "serverscript", "sscript", "script", "makescript"};
- Args = {"code"};
- Description = "Executes the given Lua code on the server";
- AdminLevel = "Admins";
- NoFilter = true;
- CrossServerDenied = true;
- Function = function(plr: Player, args: {string})
- assert(Settings.CodeExecution, "CodeExecution config must be enabled for this command to work")
- local bytecode = Core.Bytecode(assert(args[1], "Missing Script code (argument #2)"))
- assert(string.find(bytecode, "\27Lua"), `Script unable to be created: {string.gsub(bytecode, "Loadstring%.LuaX:%d+:", "")}`)
-
- local cl = Core.NewScript("Script", args[1], true)
- cl.Name = "[Adonis] Script"
- cl.Parent = service.ServerScriptService
- task.wait()
- cl.Disabled = false
- Functions.Hint("Ran Script", {plr})
- end
- };
-
- MakeLocalScript = {
- Prefix = Settings.Prefix;
- Commands = {"ls", "localscript", "lscript"};
- Args = {"code"};
- Description = "Executes the given code on your client";
- AdminLevel = "Admins";
- NoFilter = true;
- Function = function(plr: Player, args: {string})
- Commands.LoadLocalScript.Function(plr, {`@{plr.Name}`, args[1]})
- end
- };
-
- LoadLocalScript = {
- Prefix = Settings.Prefix;
- Commands = {"cs", "cscript", "clientscript"};
- Args = {"player", "code"};
- Description = "Executes the given code on the client of the target player(s)";
- AdminLevel = "Admins";
- NoFilter = true;
- Function = function(plr: Player, args: {string})
- assert(args[2], "Missing LocalScript code (argument #2)")
-
- local bytecode = Core.Bytecode(args[2])
- assert(string.find(bytecode, "\27Lua"), `LocalScript unable to be created: {string.gsub(bytecode, "Loadstring%.LuaX:%d+:", "")}`)
-
- local new = Core.NewScript("LocalScript", `script.Parent = game:GetService('Players').LocalPlayer.PlayerScripts; {args[2]}`, true)
- local function cloneScript(targetPlayer)
- local playerName = if targetPlayer == plr then "your client" else service.FormatPlayer(targetPlayer)
-
- local backpack = targetPlayer:FindFirstChildOfClass("Backpack")
- if not backpack then
- Functions.Hint(`Couldn't run LocalScript on {playerName} (Backpack missing?)`, {plr})
- return
+ if HTTP.WebPanel.Bans then
+ for ind, admin in HTTP.WebPanel.Bans do
+ if doCheck(p, admin) or banCheck(p, admin) then
+ return true, (type(admin) == "table" and admin.Reason)
end
+ end
+ end
+ end;
+
+ AddBan = function(p, reason, doSave, moderator, banType)
+ local value = {
+ Name = p.Name;
+ UserId = p.UserId;
+ Reason = reason;
+ Moderator = if moderator then service.FormatPlayer(moderator) else "%SYSTEM%";
+ BanType = banType
+ }
+
+ table.insert(Settings.Banned, value)
+
+ if doSave then
+ Core.DoSave({
+ Type = "TableAdd";
+ Table = "Banned";
+ Value = value;
+ })
- local cl = new:Clone()
- cl.Name = "[Adonis] LocalScript"
- cl.Disabled = true
- cl.Parent = targetPlayer:FindFirstChildOfClass("Backpack")
- task.wait(.1)
- cl.Disabled = false
- Functions.Hint(`Ran LocalScript on {playerName}`, {plr})
- end
-
- for i, v in service.GetPlayers(plr, args[1]) do
- task.spawn(cloneScript, v)
- end
- end
- };
-
- CreateStarterScript = {
- Prefix = Settings.Prefix;
- Commands = {"starterscript", "clientstarterscript", "starterclientscript", "createstarterscript"};
- Args = {"name", "code"};
- Description = "Executes the given code on everyone's client upon respawn";
- AdminLevel = "Admins";
- NoFilter = true;
- Function = function(plr: Player, args: {string})
- assert(args[1], "Missing starter script name (argument #1)")
- assert(args[2], "Missing LocalScript code (argument #2)")
-
- local bytecode = Core.Bytecode(args[2])
- assert(string.find(bytecode, "\27Lua"), `LocalScript unable to be created: {string.gsub(bytecode, "Loadstring%.LuaX:%d+:", "")}`)
-
- local new = Core.NewScript("LocalScript", args[2], true)
- new.Name = `[Adonis] {args[1]}`
- new.Parent = service.StarterGui
- new.Disabled = false
- Functions.Hint("Created starter script", {plr})
- end
- };
-
-
- StarterScripts = {
- Prefix = Settings.Prefix;
- Commands = {"starterscripts", "clientstarterscripts", "starterclientscripts"};
- Args = {};
- Description = "Show existing starterscripts";
- AdminLevel = "Admins";
- NoFilter = true;
- Function = function(plr: Player, args: {string})
- local result = {}
-
- for _,v : Instance in service.StarterGui:GetChildren() do
- if v:IsA("LocalScript") and v.Name:find("[Adonis]") then
- table.insert(result, (v.Name:gsub("%[Adonis%] ", "")))
- end
+ Core.CrossServer("RemovePlayer", p.Name, Variables.BanMessage, value.Reason or "No reason provided")
+ end
+
+ if type(p) ~= "table" then
+ if not service.Players:FindFirstChild(p.Name) then
+ Remote.Send(p,'Function','KillClient')
+ else
+ if p then pcall(function() p:Kick(`{Variables.BanMessage} | Reason: {value.Reason or "No reason provided"}`) end) end
end
+ end
+ service.Events.PlayerBanned:Fire(p, reason, doSave, moderator)
+ end;
- Remote.MakeGui(plr,"List",{
- Title = "Starter Scripts";
- Tab = result;
- })
+ AddTimeBan = function(p : Player | {[string]: any}, duration: number, reason: string, moderator: Player?)
+ local value = {
+ Name = p.Name;
+ UserId = p.UserId;
+ EndTime = os.time() + tonumber(duration);
+ Reason = reason;
+ Moderator = if moderator then service.FormatPlayer(moderator) else "%SYSTEM%";
+ }
+
+ table.insert(Core.Variables.TimeBans, value)
+
+ Core.DoSave({
+ Type = "TableAdd";
+ Table = {"Core", "Variables", "TimeBans"};
+ Value = value;
+ })
+
+ Core.CrossServer("RemovePlayer", p.Name, Variables.BanMessage, value.Reason or "No reason provided")
+
+ if type(p) ~= "table" then
+ if not service.Players:FindFirstChild(p.Name) then
+ Remote.Send(p, "Function", "KillClient")
+ else
+ if p then pcall(function() p:Kick(`{Variables.BanMessage} | Reason: {value.Reason or "No reason provided"}`) end) end
+ end
end
- };
+ service.Events.PlayerBanned:Fire(p, reason, true, moderator)
+ end,
- RemoveStarterScript = {
- Prefix = Settings.Prefix;
- Commands = {"removestarterscript", "removeclientstarterscripts", "removestarterclientscripts", "unstarterscript"};
- Args = {"name"};
- Description = "Remove a starterscript";
- AdminLevel = "Admins";
- NoFilter = true;
- Function = function(plr: Player, args: {string})
- assert(args[1], "No starterscript name provided!")
+ DoBanCheck = function(name: string | number | Instance, check: string | {[string]: any})
+ local id = type(name) == "number" and name
- for _,v : Instance in service.StarterGui:GetChildren() do
- if v:IsA("LocalScript") and v.Name:find("[Adonis]") then
- if v.Name:gsub("%[Adonis%] ", ""):lower() == args[1]:lower() or args[1]:lower() == "all" then
- service.Delete(v)
- Functions.Hint("Removed starter script "..v.Name, {plr})
- end
+ if type(name) == "userdata" and name:IsA("Player") then
+ id = name.UserId
+ name = name.Name
+ end
+
+ if type(check) == "table" then
+ if type(name) == "string" and check.Name and string.lower(check.Name) == string.lower(name) then
+ return true
+ elseif id and check.UserId and check.UserId == id then
+ return true
+ end
+ elseif type(check) == "string" then
+ local cName, cId = string.match(check, "(.*):(.*)")
+ if not cName and cId then cName = check end
+
+ if cName then
+ if string.lower(cName) == string.lower(name) then
+ return true
+ elseif id and cId and id == tonumber(cId) then
+ return true
end
+ else
+ return string.lower(tostring(check)) == string.lower(tostring(name))
end
end
- };
-
- Note = {
- Prefix = Settings.Prefix;
- Commands = {"note", "writenote", "makenote"};
- Args = {"player", "note"};
- Filter = true;
- Description = "Makes a note on the target player(s) that says ";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- assert(args[2], "Missing note (argument #2)")
- for _, v in service.GetPlayers(plr, args[1]) do
- local PlayerData = Core.GetPlayer(v)
- if not PlayerData.AdminNotes then PlayerData.AdminNotes = {} end
- table.insert(PlayerData.AdminNotes, args[2])
- Functions.Hint(`Added {service.FormatPlayer(v)} Note {args[2]}`, {plr})
- Core.SavePlayer(v, PlayerData)
- end
- end
- };
-
- DeleteNote = {
- Prefix = Settings.Prefix;
- Commands = {"removenote", "remnote", "deletenote", "clearnote"};
- Args = {"player", "note (specify 'all' to delete all notes)"};
- Description = "Removes a note on the target player(s)";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- assert(args[2], "Missing note (argument #2)")
- for _, v in service.GetPlayers(plr, args[1]) do
- local PlayerData = Core.GetPlayer(v)
- if PlayerData.AdminNotes then
- if string.lower(args[2]) == "all" then
- PlayerData.AdminNotes = {}
- else
- for k, m in PlayerData.AdminNotes do
- if string.sub(string.lower(m), 1, #args[2]) == string.lower(args[2]) then
- Functions.Hint(`Removed {service.FormatPlayer(v)} Note {m}`, {plr})
- table.remove(PlayerData.AdminNotes, k)
- end
+
+ return false
+ end;
+
+ RemoveBan = function(name, doSave)
+ local ret
+ for i,v in Settings.Banned do
+ if Admin.DoBanCheck(name, v) then
+ ret = table.remove(Settings.Banned, i)
+ if doSave then
+ Core.DoSave({
+ Type = "TableRemove";
+ Table = "Banned";
+ Value = ret;
+ LaxCheck = true;
+ })
+ end
+ end
+ end
+ return ret
+ end;
+
+ RemoveTimeBan = function(name : string | number | Instance)
+ local ret
+ for i,v in Core.Variables.TimeBans do
+ if Admin.DoBanCheck(name, v) then
+ table.remove(Core.Variables.TimeBans, i)
+ ret = v
+ Core.DoSave({
+ Type = "TableRemove";
+ Table = {"Core", "Variables", "TimeBans"};
+ Value = v;
+ LaxCheck = true;
+ })
+ end
+ end
+ return ret
+ end,
+
+ RunCommand = function(coma: string, ...)
+ local _, com = Admin.GetCommand(coma)
+ if com then
+ local cmdArgs = com.Args or com.Arguments
+ local args = Admin.GetArgs(coma, #cmdArgs, ...)
+
+ TrackTask(`Command: {coma}`, com.Function, function(err)
+ warn(`Encountered an error while running a command: {coma}\n{err}\n{debug.traceback()}`)
+ end, false, args)
+ end
+ end;
+
+ RunCommandAsPlayer = function(coma, plr, ...)
+ local ind, com = Admin.GetCommand(coma)
+ if com then
+ local adminLvl = Admin.GetLevel(plr)
+
+ local cmdArgs = com.Args or com.Arguments
+ local args = Admin.GetArgs(coma, #cmdArgs, ...)
+ local ran, error = TrackTask(
+ `{plr.Name}: {coma}`,
+ com.Function,
+ function(err)
+ err = string.match(err, ":(.+)$") or "Unknown error"
+ Remote.MakeGui(plr, "Output", {
+ Title = "Error",
+ Message = error,
+ Color = Color3.new(1, 0, 0),
+ })
+ warn(`Encountered an error while running a command: {coma}\n{err}\n{debug.traceback()}`)
+ end,
+ plr,
+ args,
+ {
+ PlayerData = {
+ Player = plr,
+ Level = adminLvl,
+ isDonor = ((Settings.DonorCommands or com.AllowDonors) and Admin.CheckDonor(plr)) or false,
+ },
+ }
+ )
+ end
+ end;
+
+ RunCommandAsNonAdmin = function(coma, plr, ...)
+ local ind, com = Admin.GetCommand(coma)
+ if com and com.AdminLevel == 0 then
+ local cmdArgs = com.Args or com.Arguments
+ local args = Admin.GetArgs(coma, #cmdArgs, ...)
+ local _, error = TrackTask(
+ `{plr.Name}: {coma}`,
+ com.Function,
+ function(err)
+ err = string.match(err, ":(.+)$") or "Unknown error"
+ Remote.MakeGui(plr, "Output", {
+ Title = "",
+ Message = error,
+ Color = Color3.new(1, 0, 0),
+ })
+ warn(`Encountered an error while running a command: {coma}\n{err}\n{debug.traceback()}`)
+ end,
+ plr,
+ args,
+ { PlayerData = {
+ Player = plr,
+ Level = 0,
+ isDonor = false,
+ } }
+ )
+ end
+ end;
+
+ CacheCommands = function()
+ local tempTable = {}
+ local tempPrefix = {}
+ for ind, data in Commands do
+ if type(data) == "table" then
+ for i,cmd in data.Commands do
+ if type(data.Prefix) ~= "table" and data.Prefix == "" then Admin.BlankPrefix = true end
+ if type(data.Prefix) == "table" then
+ for _,p in data.Prefix do
+ tempPrefix[p] = true
+ tempTable[string.lower(p..cmd)] = ind
end
+ else
+ tempPrefix[data.Prefix] = true
+ tempTable[string.lower(data.Prefix..cmd)] = ind
end
- Core.SavePlayer(v, PlayerData)
end
end
end
- };
-
- ShowNotes = {
- Prefix = Settings.Prefix;
- Commands = {"notes", "viewnotes"};
- Args = {"player"};
- Description = "Views notes on the target player(s)";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- for _, v in service.GetPlayers(plr, args[1]) do
- local PlayerData = Core.GetPlayer(v)
- local notes = PlayerData.AdminNotes
- if not notes then
- Functions.Hint(`No notes found on {service.FormatPlayer(v)}`, {plr})
- continue
+
+ Admin.PrefixCache = tempPrefix
+ Admin.CommandCache = tempTable
+
+ if Variables.ChatCreateRobloxCommands then
+ -- // Support for commands to be ran via TextChat
+ task.spawn(function()
+ local container = service.TextChatService.ChatVersion == Enum.ChatVersion.TextChatService and service.TextChatService:WaitForChild("TextChatCommands", 9e9)
+
+ if container then
+ for _, v in container:GetChildren() do
+ if string.sub(v.Name, 1, 7) == "Adonis_" then
+ v:Destroy()
+ end
+ end
+
+ local blacklistedCommands = {}
+
+ for _, v in container:GetDescendants() do
+ if v:IsA("TextChatCommand") then
+ blacklistedCommands[v.PrimaryAlias] = true
+ blacklistedCommands[v.SecondaryAlias] = true
+ end
+ end
+
+ for name, data in Commands do
+ local command1, command2 = nil, nil
+
+ if type(data) ~= "table" or data.Hidden then
+ continue
+ end
+
+ for _, v in data.Commands do
+ if type(data.Prefix) == "table" then
+ for _, p in data.Prefix do
+ if not blacklistedCommands["/"..p..v] then
+ if not command1 then
+ command1 = "/"..p..v
+ else
+ command2 = "/"..p..v
+ end
+ end
+ end
+ else
+ if not blacklistedCommands["/"..data.Prefix..v] then
+ if not command1 then
+ command1 = "/"..data.Prefix..v
+ else
+ command2 = "/"..data.Prefix..v
+ end
+ end
+
+ end
+
+ end
+
+ if command1 then
+ local command = service.New("TextChatCommand")
+
+ command.Name = "Adonis_"..name
+ command.PrimaryAlias = command1
+ command.SecondaryAlias = command2 or ""
+ command.Archivable = false
+ command:SetAttribute("AdminLevel", tonumber(data.AdminLevel) or data.AdminLevel or nil)
+ command.Parent = container
+ command.Triggered:Connect(function(textSource, text)
+ local player = service.Players:GetPlayerByUserId(textSource.UserId)
+
+ if player then
+ Process.Command(player, string.sub(text, 2))
+ end
+ end)
+ end
+ end
end
- Remote.MakeGui(plr, "List", {Title = service.FormatPlayer(v), Table = notes})
- end
- end
- };
-
- LoopKill = {
- Prefix = Settings.Prefix;
- Commands = {"loopkill"};
- Args = {"player", "num (optional)"};
- Description = "Repeatedly kills the target player(s)";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- local num = tonumber(args[2]) or 9999
-
- for _, v in service.GetPlayers(plr, args[1]) do
- service.StopLoop(`{v.UserId}LOOPKILL`)
- local count = 0
- Routine(service.StartLoop, `{v.UserId}LOOPKILL`, 3, function()
- local hum = v.Character and v.Character:FindFirstChildOfClass("Humanoid")
- if hum and hum.Health > 0 then
- hum.Health = 0
- count += 1
+ end)
+ end
+ end;
+
+ GetCommand = function(Command)
+ if Admin.PrefixCache[string.sub(Command, 1, 1)] or Admin.BlankPrefix then
+ local matched
+ matched = if string.find(Command, Settings.SplitKey) then
+ string.match(Command, `^(%S+){Settings.SplitKey}`)
+ else string.match(Command, "^(%S+)")
+
+ if matched then
+ local found = Admin.CommandCache[string.lower(matched)]
+ if found then
+ local real = Commands[found]
+ if real then
+ return found,real,matched
end
- if count == num then
- service.StopLoop(`{v.UserId}LOOPKILL`)
+ end
+ end
+ end
+ end;
+
+ FindCommands = function(Command)
+ local prefixChar = string.sub(Command, 1, 1)
+ local checkPrefix = Admin.PrefixCache[prefixChar] and prefixChar
+ local matched
+
+ if checkPrefix then
+ Command = string.sub(Command, 2)
+ end
+
+ if string.find(Command, Settings.SplitKey) then
+ matched = string.match(Command, `^(%S+){Settings.SplitKey}`)
+ else
+ matched = string.match(Command, "^(%S+)")
+ end
+
+ if matched then
+ local foundCmds = {}
+ matched = string.lower(matched)
+
+ for ind,cmd in Commands do
+ if type(cmd) == "table" and ((checkPrefix and prefixChar == cmd.Prefix) or not checkPrefix) then
+ for _, alias in cmd.Commands do
+ if string.lower(alias) == matched then
+ foundCmds[ind] = cmd
+ break
+ end
end
- end)
+ end
end
+
+ return foundCmds
end
- };
+ end;
- UnLoopKill = {
- Prefix = Settings.Prefix;
- Commands = {"unloopkill"};
- Args = {"player"};
- Description = "Un-Loop Kill";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- for _, v in service.GetPlayers(plr, args[1]) do
- service.StopLoop(`{v.UserId}LOOPKILL`)
+ SetPermission = function(comString, newLevel)
+ local cmds = Admin.FindCommands(comString)
+ if cmds then
+ for ind, cmd in cmds do
+ cmd.AdminLevel = newLevel
end
end
- };
+ end;
- Lag = {
- Prefix = Settings.Prefix;
- Commands = {"lag", "fpslag"};
- Args = {"player"};
- Description = "Makes the target player(s)'s FPS drop";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string}, data: {any})
- for _, v in service.GetPlayers(plr, args[1]) do
- if Admin.CheckAuthority(plr, v, "lag") then
- Remote.Send(v, "Function", "SetFPS", 5.6)
- end
+ FormatCommandArguments = function(command)
+ local text = table.create(#command.Args)
+ for i, arg in command.Args do
+ text[i] = `<{arg}>`
+ end
+ return table.concat(text, Settings.SplitKey)
+ end;
+
+ FormatCommand = function(command, cmdn)
+ return table.concat({
+ (if type(command.Prefix) == "table" then command.Prefix[1] else command.Prefix or ""),
+ tostring(command.Commands[cmdn or 1]),
+ #command.Args > 0 and Settings.SplitKey or "",
+ #command.Args > 0 and Admin.FormatCommandArguments(command) or ""
+ })
+ end;
+
+ FormatCommandAdminLevel = function(command)
+ local levels = if type(command.AdminLevel) == "table"
+ then table.clone(command.AdminLevel)
+ else {command.AdminLevel}
+ local permissionDesc = table.create(#levels)
+
+ for i, lvl in levels do
+ if type(lvl) == "number" then
+ local list, name, data = Admin.LevelToList(lvl)
+ permissionDesc[i] = `{name or "No Rank"}; Level {lvl}`
+ elseif type(lvl) == "string" then
+ local numLvl = Admin.StringToComLevel(lvl)
+ permissionDesc[i] = `{lvl}; Level {numLvl or "Unknown"}`
+ else
+ permissionDesc[i] = "N/A"
end
end
- };
- UnLag = {
- Prefix = Settings.Prefix;
- Commands = {"unlag", "unfpslag"};
- Args = {"player"};
- Description = "Un-Lag";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- for _, v in service.GetPlayers(plr, args[1]) do
- Remote.Send(v, "Function", "RestoreFPS")
+ return table.concat(permissionDesc, ", ")
+ end;
+
+ CheckTable = function(p, tab)
+ local doCheck = Admin.DoCheck
+ for i,v in tab do
+ if doCheck(p, v) then
+ return true
end
end
- };
+ end;
+
+ --// Make it so you can't accidentally overwrite certain existing commands... resulting in being unable to add/edit/remove aliases (and other stuff)
+ CheckAliasBlacklist = function(alias)
+ local playerPrefix = Settings.PlayerPrefix;
+ local prefix = Settings.Prefix
+ local blacklist = {
+ [`{playerPrefix}alias`] = true;
+ [`{playerPrefix}newalias`] = true;
+ [`{playerPrefix}removealias`] = true;
+ [`{playerPrefix}client`] = true;
+ [`{playerPrefix}userpanel`] = true;
+ [":adonissettings"] = true;
+ }
+ --return Admin.CommandCache[alias:lower()] --// Alternatively, we could make it so you can't overwrite ANY existing commands...
+ return blacklist[alias];
+ end;
+
+ GetArgs = function(msg, num, ...)
+ local newArgs = table.pack(...)
+ local args = Functions.Split(string.match(msg, `^.-{Settings.SplitKey}(.+)`) or "", Settings.SplitKey, num) or table.create(newArgs.n)
+ for i = 1, newArgs.n do
+ table.insert(args, newArgs[i])
+ end
+ return args
+ end;
- Crash = {
- Prefix = Settings.Prefix;
- Commands = {"crash"};
- Args = {"player"};
- Description = "Crashes the target player(s)";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string}, data: {any})
- for _, v in service.GetPlayers(plr, args[1], {
- IsKicking = true;
- NoFakePlayer = false;
- })
- do
- if Admin.CheckAuthority(plr, v, "crash") then
- Remote.Send(v, "Function", "Crash")
+ AliasFormat = function(aliases, msg)
+ local foundPlayerAlias = false --// Check if there's a player-defined alias first then otherwise check settings aliases
+
+ local CheckAliasBlacklist, SanitizePattern = Admin.CheckAliasBlacklist, service.SanitizePattern
+
+ if aliases then
+ for alias, cmd in aliases do
+ local tAlias = stripArgPlaceholders(alias)
+ if not Admin.CheckAliasBlacklist(tAlias) then
+ local escAlias = SanitizePattern(tAlias)
+ --// Ignore any "empty" aliases, aka aliases that would basically match any command
+ if string.len(Functions.Trim(escAlias)) == 0 then
+ continue
+ end
+ local trimmedMsg = Functions.Trim(msg)
+ --// Use Adonis split to better support various characters that string.split can't handle properly
+ local aliasCharacters = Functions.Split(trimmedMsg, Settings.SplitKey)
+ --// Matching an alias can result in an infinite loop like running !fire with the alias !f, it will infinitely run the !f alias
+ --// If you have an alias !f
+ if escAlias == aliasCharacters[1] or string.match(trimmedMsg, `%s{escAlias}`) then
+ msg = FormatAliasArgs(alias, cmd, msg)
+ end
end
end
end
- };
- HardCrash = {
- Prefix = Settings.Prefix;
- Commands = {"hardcrash"};
- Args = {"player"};
- Description = "Hard-crashes the target player(s)";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string}, data: {any})
- for _, v in service.GetPlayers(plr, args[1], {
- IsKicking = true;
- NoFakePlayer = false;
- })
- do
- if Admin.CheckAuthority(plr, v, "hard-crash") then
- Remote.Send(v, "Function", "HardCrash")
+ for alias, cmd in Variables.Aliases do
+ local tAlias = stripArgPlaceholders(alias)
+ if not CheckAliasBlacklist(tAlias) then
+ local escAlias = SanitizePattern(tAlias)
+ if string.match(msg, `^{escAlias}`) or string.match(msg, `%s{escAlias}`) then
+ msg = FormatAliasArgs(alias, cmd, msg)
end
end
end
- };
- RAMCrash = {
- Prefix = Settings.Prefix;
- Commands = {"ramcrash", "memcrash"};
- Args = {"player"};
- Description = "RAM-crashes the target player(s)";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string}, data: {any})
- for _, v in service.GetPlayers(plr, args[1], {
- IsKicking = true;
- NoFakePlayer = false;
- })
- do
- if Admin.CheckAuthority(plr, v, "RAM-crash") then
- Remote.Send(v, "Function", "RAMCrash")
+ return msg
+ end;
+
+ StringToComLevel = function(str)
+ local strType = type(str)
+ if strType == "string" and string.lower(str) == "players" then
+ return 0
+ end
+ if strType == "number" then
+ return str
+ end
+
+ local lvl = Settings.Ranks[str]
+ return (lvl and lvl.Level) or tonumber(str)
+ end;
+
+ CheckComLevel = function(plrAdminLevel, comLevel)
+ if type(comLevel) == "string" then
+ comLevel = Admin.StringToComLevel(comLevel)
+ elseif type(comLevel) == "table" then
+ for _, level in comLevel do
+ if Admin.CheckComLevel(plrAdminLevel, level) then
+ return true
end
end
+ return false
end
- };
- GPUCrash = {
- Prefix = Settings.Prefix;
- Commands = {"gpucrash"};
- Args = {"player"};
- Description = "GPU crashes the target player(s)";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string}, data: {any})
- for _, v in service.GetPlayers(plr, args[1], {
- IsKicking = true;
- NoFakePlayer = false;
- })
- do
- if Admin.CheckAuthority(plr, v, "GPU-crash") then
- Remote.Send(v, "Function", "GPUCrash")
- end
+ return type(comLevel) == "number" and plrAdminLevel >= comLevel
+ end;
+
+ IsBlacklisted = function(p)
+ local CheckTable = Admin.CheckTable
+ for _, list in Variables.Blacklist.Lists do
+ if CheckTable(p, list) then
+ return true
end
end
- };
+ end;
- CustomKick = {
- Prefix = Settings.Prefix;
- Commands = {"ckick", "customkick", "customcrash"};
- Args = {"player", "title", "message"};
- Description = "Disconnects (crashes) the target player with a custom Roblox dialog";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- assert(args[3], "Argument(s) missing or nil")
+ CheckPermission = function(pDat, cmd, ignoreCooldown, opts)
+ opts = opts or {}
- local title = service.BroadcastFilter(args[2], plr)
- assert(title == args[2], "Title was filtered: "..title)
+ local adminLevel = pDat.Level
+ local comLevel = cmd.AdminLevel
- local msg = service.BroadcastFilter(args[3], plr)
- assert(msg == args[3], "Message was filtered: "..msg)
+ if cmd.Disabled then
+ return false, "This command has been disabled."
+ end
- for _, v in service.GetPlayers(plr, args[1], {
- IsKicking = true;
- NoFakePlayer = false;
- })
- do
- if not Admin.CheckAuthority(plr, v, "custom-kick") then
- continue
- end
+ if Variables.IsStudio and cmd.NoStudio then
+ return false, "This command cannot be used in Roblox Studio."
+ end
- local plrgui = v:FindFirstChildOfClass("PlayerGui")
- if not plrgui then
- Remote.MakeGui(plr, "Output", {
- Message = `Failed to custom-kick {service.FormatPlayer(v)} (PlayerGui not found)`;
- })
- continue
- end
+ if opts.CrossServer and cmd.CrossServerDenied then -- Ignore when disabled then
+ return false, "This command may not be run across servers (cross-server-blacklisted)."
+ end
- local promptGui = Deps.Assets.RobloxPromptGui:Clone()
- promptGui.promptOverlay.ErrorPrompt.TitleFrame.ErrorTitle.Text = title
- promptGui.promptOverlay.ErrorPrompt.MessageArea.ErrorFrame.ErrorMessage.Text = msg
- promptGui.Parent = plrgui
+ if cmd.CrossServer and not Settings.CrossServerCommands then
+ return false, "This command has been disabled due to CrossServerCommands being disabled"
+ end
- Remote.Send(v, "Function", "CustomKick")
- task.delay(5, function()
- if v.Parent == service.Players then
- -- make sure they're really kicked
- v:Kick("Unexpected Error")
- end
- end)
- Functions.Hint(`Custom-kicking {service.FormatPlayer(v)}`, {plr})
- end
- end
- };
-
- Shutdown = {
- Prefix = Settings.Prefix;
- Commands = {"shutdown"};
- Args = {"reason"};
- Description = "Shuts the server down";
- Filter = true;
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- if Core.DataStore then
- Core.UpdateData("ShutdownLogs", function(logs)
- table.insert(logs, 1, {
- User = plr and plr.Name or "[Server]",
- Time = os.time(),
- Reason = args[1] or "N/A"
- })
+ if Admin.IsPlaceOwner(pDat.Player) or adminLevel >= Settings.Ranks.Creators.Level then
+ return true, nil
+ end
- local nlogs = #logs
- if nlogs > Logs.OldCommandLogsLimit then
- table.remove(logs, nlogs)
- end
+ if Admin.IsBlacklisted(pDat.Player) then
+ return false, "You are blacklisted from running commands."
+ end
- return logs
- end)
- end
+ if (comLevel == 0 or comLevel == "Players") and adminLevel <= 0 and not Settings.PlayerCommands then
+ return false, "Player commands are disabled in this game."
+ end
- Functions.Shutdown(args[1])
+ if cmd.Fun and not Settings.FunCommands then
+ return false, "Fun commands are disabled in this game."
end
- };
- ServerBan = {
- Prefix = Settings.Prefix;
- Commands = {"serverban", "ban"};
- Args = {"player/user", "reason"};
- Description = "Bans the target player(s) from the server";
- AdminLevel = "Admins";
- Filter = true;
- Function = function(plr: Player, args: {string}, data: {any})
- local reason = args[2] or "No reason provided"
- for _, v in service.GetPlayers(plr, args[1], {
- IsKicking = true;
- NoFakePlayer = false;
- })
- do
- if Admin.CheckAuthority(plr, v, "server-ban", false) then
- Admin.AddBan(v, reason, false, plr, "Server")
- Functions.LogAdminAction(plr, "Server Ban", v.Name, `Reason: {reason}`)
- Functions.Hint(`Server-banned {service.FormatPlayer(v, true)}`, {plr})
+ if opts.Chat and cmd.Chattable == false then
+ return false, "This command is not permitted as chat message (non-chattable command)."
+ end
+
+ local permAllowed = (cmd.Donors and (pDat.isDonor and (Settings.DonorCommands or cmd.AllowDonors))) or (cmd.Agent and HTTP.Trello.CheckAgent) and HTTP.Trello.CheckAgent(pDat.Player)
+ or Admin.CheckComLevel(adminLevel, comLevel)
+
+ if permAllowed and not ignoreCooldown and type(pDat.Player) == "userdata" then
+ local playerCooldown = tonumber(cmd.PlayerCooldown)
+ local serverCooldown = tonumber(cmd.ServerCooldown)
+ local crossCooldown = tonumber(cmd.CrossCooldown)
+
+ local cmdFullName = cmd._fullName or (function()
+ local aliases = cmd.Aliases or cmd.Commands or {}
+ cmd._fullName = `{cmd.Prefix}{aliases[1] or `{service.HttpService:GenerateGUID(false)}-RANDOM_COMMAND`}`
+ return cmd._fullName
+ end)()
+
+ local pCooldown_Cache = cmd._playerCooldownCache or (function()
+ local tab = {}
+ cmd._playerCooldownCache = tab
+ return tab
+ end)()
+
+ local sCooldown_Cache = cmd._serverCooldownCache or (function()
+ local tab = {}
+ cmd._serverCooldownCache = tab
+ return tab
+ end)()
+
+ local crossCooldown_Cache = cmd._crossCooldownCache or (function()
+ local tab = {}
+ cmd._crossCooldownCache = tab
+ return tab
+ end)()
+
+ local cooldownIndex = tostring(pDat.Player.UserId)
+ local pCooldown_playerCache = pCooldown_Cache[cooldownIndex]
+ local sCooldown_playerCache = sCooldown_Cache[cooldownIndex]
+
+ if playerCooldown and pCooldown_playerCache then
+ local secsTillPass = os.clock() - pCooldown_playerCache
+ if secsTillPass < playerCooldown then
+ return false, string.format("[PlayerCooldown] You must wait %.0f seconds to run the command.", playerCooldown - secsTillPass)
end
end
- end
- };
- UnBan = {
- Prefix = Settings.Prefix;
- Commands = {"unserverban", "unban"};
- Args = {"user"};
- Description = "Unbans the target user(s) from the server";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- for _, v in service.GetPlayers(plr, assert(args[1], "Missing user (argument #1)"), {
- UseFakePlayer = true;
- })
- do
- if Admin.RemoveBan(v) then
- Functions.LogAdminAction(plr, "Unban", v.Name, "User has been unbanned.")
- Functions.Hint(`{service.FormatPlayer(v, true)} has been unbanned`, {plr})
- else
- Functions.Hint(`{service.FormatPlayer(v, true)} is not currently banned`, {plr})
+ if serverCooldown and sCooldown_playerCache then
+ local secsTillPass = os.clock() - sCooldown_playerCache
+ if secsTillPass < serverCooldown then
+ return false, string.format("[ServerCooldown] You must wait %.0f seconds to run the command.", serverCooldown - secsTillPass)
end
end
- end
- };
-
- BanMenu = {
- Prefix = Settings.Prefix;
- Commands = {"banmenu"};
- Args = {};
- Description = "Opens the ban menu";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string}, data: {any})
- Remote.MakeGui(plr,"BanMenu",{
- AdminLevel = Admin.GetLevel(plr);
- CanBan = Admin.CheckComLevel(Admin.GetLevel(plr),Commands.ServerBan.AdminLevel);
- CanTimeBan = Admin.CheckComLevel(Admin.GetLevel(plr),Commands.TimeBan.AdminLevel);
- CanPermBan = Admin.CheckComLevel(Admin.GetLevel(plr),Commands.PermanentBan.AdminLevel);
- Prefix = Settings.Prefix;
- })
- end,
- };
-
- CustomMessage = {
- Prefix = Settings.Prefix;
- Commands = {"cm", "custommessage"};
- Args = {"Upper message", "message"};
- Filter = true;
- Description = "Same as message but says whatever you want upper message to be instead of your name.";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- assert(args[1], "Missing message title (argument #1)")
- assert(args[2], "Missing message (argument #2)")
- for _, v in service.Players:GetPlayers() do
- Remote.RemoveGui(v, "Message")
- Remote.MakeGui(v, "Message", {
- Title = args[1];
- Message = args[2];
- Time = (#tostring(args[1]) / 19) + 2.5;
- --service.Filter(args[1],plr, v);
- })
+
+ if crossCooldown then
+ local playerData = Core.GetPlayer(pDat.Player) or {}
+ local crossCooldown_Cache = playerData._crossCooldownCache or (function()
+ local tab = {}
+ playerData._crossCooldownCache = tab
+ return tab
+ end)()
+ local crossCooldown_playerCache = crossCooldown_Cache[cmdFullName]
+
+ if crossCooldown_playerCache then
+ local secsTillPass = os.clock() - crossCooldown_playerCache
+ if secsTillPass < crossCooldown then
+ return false, string.format("[CrossServerCooldown] You must wait %.0f seconds to run the command.", crossCooldown - secsTillPass)
+ end
+ end
end
end
- };
-
- Nil = {
- Prefix = Settings.Prefix;
- Commands = {"nil"};
- Args = {"player"};
- Hidden = true;
- Description = `Deletes the player forcefully, causing them to be kicked for "Player has been removed from the DataModel"`;
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- for _, v in service.GetPlayers(plr, args[1]) do
- v.Character = nil
- v.Parent = nil
- Functions.Hint(`Sent {service.FormatPlayer(v)} to nil`, {plr})
- end
- end
- };
-
- PromptPremiumPurchase = {
- Prefix = Settings.Prefix;
- Commands = {"promptpremiumpurchase", "premiumpurchaseprompt"};
- Args = {"player"};
- Description = "Opens the Roblox Premium purchase prompt for the target player(s)";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- for _, v in service.GetPlayers(plr, args[1]) do
- service.MarketplaceService:PromptPremiumPurchase(v)
- end
- end
- };
-
- RobloxNotify = {
- Prefix = Settings.Prefix;
- Commands = {"rbxnotify", "robloxnotify", "robloxnotif", "rblxnotify", "rnotif", "rn"};
- Args = {"player", "duration (seconds)", "text"};
- Filter = true;
- Description = "Sends a Roblox-styled notification for the target player(s)";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- for _, v in service.GetPlayers(plr, args[1]) do
- Remote.Send(v, "Function", "SetCore", "SendNotification", {
- Title = "Notification";
- Text = args[3] or "Hello, from Adonis!";
- Duration = tonumber(args[2]) or 5;
- })
- end
+
+ return permAllowed, nil
+ end;
+
+ UpdateCooldown = function(pDat, cmd)
+ if pDat.Player == "SYSTEM" then return end
+ local playerCooldown = tonumber(cmd.PlayerCooldown)
+ local serverCooldown = tonumber(cmd.ServerCooldown)
+ local crossCooldown = tonumber(cmd.CrossCooldown)
+
+ local cmdFullName = cmd._fullName or (function()
+ local aliases = cmd.Aliases or cmd.Commands or {}
+ cmd._fullName = `{cmd.Prefix}{aliases[1] or `{service.HttpService:GenerateGUID(false)}-RANDOM_COMMAND`}`
+ return cmd._fullName
+ end)()
+
+ local pCooldown_Cache = cmd._playerCooldownCache or (function()
+ local tab = {}
+ cmd._playerCooldownCache = tab
+ return tab
+ end)()
+
+ local sCooldown_Cache = cmd._serverCooldownCache or (function()
+ local tab = {}
+ cmd._serverCooldownCache = tab
+ return tab
+ end)()
+
+ local crossCooldown_Cache = cmd._crossCooldownCache or (function()
+ local tab = {}
+ cmd._crossCooldownCache = tab
+ return tab
+ end)()
+
+ local cooldownIndex = tostring(pDat.Player.UserId)
+ local pCooldown_playerCache = pCooldown_Cache[cooldownIndex]
+ local sCooldown_playerCache = sCooldown_Cache[cooldownIndex]
+ local lastUsed = os.clock()
+
+ if playerCooldown then
+ pCooldown_Cache[cooldownIndex] = lastUsed
end
- };
- Disguise = {
- Prefix = Settings.Prefix;
- Commands = {"disguise", "masquerade"};
- Args = {"player", "username"};
- Description = "Names the player, chars the player, and modifies the player's chat tag";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- assert(args[2], "Argument missing or nil")
- local userId = Functions.GetUserIdFromNameAsync(args[2])
- assert(userId, "Invalid username supplied/user not found")
+ if serverCooldown then
+ sCooldown_Cache[cooldownIndex] = lastUsed
+ end
- local username = select(2, xpcall(function()
- return service.Players:GetNameFromUserIdAsync(userId)
- end, function() return args[2] end))
+ --// Cross cooldown
+ do
+ local playerData = Core.GetPlayer(pDat.Player)
+ local crossCooldown_Cache = playerData._crossCooldownCache or {}
+ local crossCooldown_playerCache = crossCooldown_Cache[cmdFullName]
- if service.Players:GetPlayerByUserId(userId) then
- error("You cannot disguise as this player (currently in server)")
+ if not crossCooldown and crossCooldown_playerCache then
+ crossCooldown_playerCache[cmdFullName] = nil
+ elseif crossCooldown then
+ crossCooldown_Cache[cmdFullName] = lastUsed
end
+ end
+ end;
- Commands.Char.Function(plr, args)
- Commands.DisplayName.Function(plr, {args[1], username})
-
- local ChatService = Functions.GetChatService()
+ SearchCommands = function(p, search)
+ local checkPerm = Admin.CheckPermission
+ local tab = {}
+ local pDat = {
+ Player = p;
+ Level = Admin.GetLevel(p);
+ isDonor = Admin.CheckDonor(p);
+ }
- for _, v in service.GetPlayers(plr, args[1]) do
- if Variables.DisguiseBindings[v.UserId] then
- Variables.DisguiseBindings[v.UserId].Rename:Disconnect()
- Variables.DisguiseBindings[v.UserId].Rename = nil
- if ChatService then
- ChatService:RemoveSpeaker(Variables.DisguiseBindings[v.UserId].TargetUsername)
- ChatService:UnregisterProcessCommandsFunction(`Disguise_{v.Name}`)
- end
- end
+ for ind, cmd in Commands do
+ if checkPerm(pDat, cmd, true) then
+ tab[ind] = cmd
+ end
+ end
- Variables.DisguiseBindings[v.UserId] = {
- TargetUsername = username;
- Rename = v.CharacterAppearanceLoaded:Connect(function(char)
- Commands.DisplayName.Function(v, {v.Name, username})
- end);
- }
+ return tab
+ end;
- if ChatService then
- local disguiseSpeaker = ChatService:AddSpeaker(username)
- disguiseSpeaker:JoinChannel("All")
- ChatService:RegisterProcessCommandsFunction(`Disguise_{v.Name}`, function(speaker, message, channelName)
- if speaker == v.Name then
- local filteredMessage = select(2, xpcall(function()
- return service.TextService:FilterStringAsync(message, v.UserId, Enum.TextFilterContext.PrivateChat):GetChatForUserAsync(v.UserId)
- end, function()
- Remote.Send(v, "Function", "ChatMessage", "A message filtering error occurred.", Color3.new(1, 64/255, 77/255))
- return
- end))
- if filteredMessage and not server.Admin.DoHideChatCmd(v, message) then
- disguiseSpeaker:SayMessage(filteredMessage, channelName)
- if v.Character then
- service.Chat:Chat(v.Character, filteredMessage, Enum.ChatColor.White)
- end
- end
- return true
- end
- return false
- end)
- end
+ CheckAuthority = function(p, target, actionName, allowSelf)
+ if p == target then
+ if allowSelf == false then
+ Functions.Hint(`You cannot {actionName} yourself`, {p})
+ return false
end
+
+ return allowSelf or Remote.GetGui(p, "YesNoPrompt", {
+ Question = `Are you sure you want to {actionName} yourself?`;
+ }) == "Yes"
+
+ elseif Admin.GetLevel(p) > Admin.GetLevel(target) then
+ return true
end
- };
-
- UnDisguise = {
- Prefix = Settings.Prefix;
- Commands = {"undisguise", "removedisguise", "cleardisguise", "nodisguise"};
- Args = {"player"};
- Description = "Removes the player's disguise";
- AdminLevel = "Admins";
- Function = function(plr: Player, args: {string})
- local ChatService = Functions.GetChatService()
- for _, v in service.GetPlayers(plr, args[1]) do
- if Variables.DisguiseBindings[v.UserId] then
- Variables.DisguiseBindings[v.UserId].Rename:Disconnect()
- Variables.DisguiseBindings[v.UserId].Rename = nil
- pcall(function()
- ChatService:RemoveSpeaker(Variables.DisguiseBindings[v.UserId].TargetUsername)
- ChatService:UnregisterProcessCommandsFunction(`Disguise_{v.Name}`)
- end)
- end
- Variables.DisguiseBindings[v.UserId] = nil
+
+ Functions.Hint(`You don't have permission to {actionName} {service.FormatPlayer(target)}`, {p})
+ return false
+ end;
+
+ GetAliases = function(player)
+ local aliases = table.clone(Variables.Aliases)
+ local pData = player and Core.GetPlayer(player)
+
+ if pData and pData.Aliases then
+ for alias, command in pData.Aliases do
+ aliases[alias] = command
end
- Commands.UnChar.Function(plr, args)
- Commands.UnDisplayName.Function(plr, args)
end
- };
+
+ return aliases
+ end;
}
end
diff --git a/MainModule/Server/Commands/Moderators.luau b/MainModule/Server/Commands/Moderators.luau
index 95b042098..1dff5e841 100644
--- a/MainModule/Server/Commands/Moderators.luau
+++ b/MainModule/Server/Commands/Moderators.luau
@@ -292,7 +292,7 @@ return function(Vargs, env)
Commands = {"cn", "customsmallmessage", "cnmessage"};
Args = {"title", "message"};
Filter = true;
- Description = `Same as {Settings.Prefix}n but says whatever you want the title to be instead of your name.`;
+ Description = `Same as {Functions.GetMainPrefix()}n but says whatever you want the title to be instead of your name.`;
AdminLevel = "Moderators";
Function = function(plr: Player, args: {string})
Functions.Notify(service.BroadcastFilter(assert(args[1], "Missing title"), plr), service.BroadcastFilter(assert(args[2], "Missing message") , plr), service.GetPlayers())
@@ -377,7 +377,7 @@ return function(Vargs, env)
Remote.RemoveGui(v, "Notify")
Functions.Notify(`Warning from {service.FormatPlayer(plr)}`, reason, {v})
- Functions.Notification("Notification", `Warned {service.FormatPlayer(v)}`, {plr}, 5, "MatIcons://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Settings.Prefix}warnings {v.Name}')`))
+ Functions.Notification("Notification", `Warned {service.FormatPlayer(v)}`, {plr}, 5, "MatIcons://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Functions.GetMainPrefix()}warnings {v.Name}')`))
end
end
end
@@ -416,7 +416,7 @@ return function(Vargs, env)
Core.CrossServer("RemovePlayer", v.Name, `Warning from {service.FormatPlayer(plr)}`, reason)
end
- Functions.Notification("Notification", `Kick-warned {service.FormatPlayer(v)}`, {plr}, 5, "MatIcons://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Settings.Prefix}warnings {v.Name}')`))
+ Functions.Notification("Notification", `Kick-warned {service.FormatPlayer(v)}`, {plr}, 5, "MatIcons://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Functions.GetMainPrefix()}warnings {v.Name}')`))
end
end
end
@@ -725,7 +725,7 @@ return function(Vargs, env)
for k, t in v.Backpack:GetChildren() do
t.Parent = tools
end
- Admin.RunCommand(`{Settings.Prefix}name`, v.Name, `-AFK-_{service.FormatPlayer(v)}_-AFK-`)
+ Admin.RunCommand(`{Functions.GetMainPrefix()}name`, v.Name, `-AFK-_{service.FormatPlayer(v)}_-AFK-`)
local torso = v.Character.HumanoidRootPart
local pos = torso.CFrame
local running=true
@@ -738,7 +738,7 @@ return function(Vargs, env)
for k, t in tools:GetChildren() do
t.Parent = v.Backpack
end
- Admin.RunCommand(`{Settings.Prefix}unname`, v.Name)
+ Admin.RunCommand(`{Functions.GetMainPrefix()}unname`, v.Name)
event:Disconnect()
end)
repeat torso.CFrame = pos wait() until not v or not v.Character or not torso or not running or not torso.Parent
@@ -811,7 +811,7 @@ return function(Vargs, env)
Prefix = Settings.Prefix;
Commands = {"fullgod", "totalgod"};
Args = {"player"};
- Description = `Same as {server.Settings.Prefix}god, but also provides blast protection`;
+ Description = `Same as {Functions.GetMainPrefix()}god, but also provides blast protection`;
AdminLevel = "Moderators";
Function = function(plr: Player, args: {string})
for _, v in service.GetPlayers(plr, args[1]) do
@@ -1243,7 +1243,7 @@ return function(Vargs, env)
TitleButtons = {
{
Text = "";
- OnClick = Core.Bytecode(`client.Remote.Send('ProcessCommand','{Settings.Prefix}tools')`);
+ OnClick = Core.Bytecode(`client.Remote.Send('ProcessCommand','{Functions.GetMainPrefix()}tools')`);
Children = {
{
Class = "ImageLabel";
@@ -1594,7 +1594,7 @@ return function(Vargs, env)
local command = args[3]
local name = string.lower(plr.Name)
assert(command, "Missing command name to repeat")
- if string.lower(string.sub(command, 1, #Settings.Prefix+string.len("repeat"))) == string.lower(`{Settings.Prefix}repeat`) or string.sub(command, 1, #Settings.Prefix+string.len("loop")) == string.lower(`{Settings.Prefix}loop`) or string.find(command, `^{Settings.Prefix}loop`) or string.find(command, `^{Settings.Prefix}repeat`) then
+ if string.lower(string.sub(command, 1, #Functions.GetMainPrefix()+string.len("repeat"))) == string.lower(`{Settings.Prefix}repeat`) or string.sub(command, 1, #Functions.GetMainPrefix()+string.len("loop")) == string.lower(`{Settings.Prefix}loop`) or string.find(command, `^{Settings.Prefix}loop`) or string.find(command, `^{Settings.Prefix}repeat`) then
error("Cannot repeat the loop command in a loop command")
return
end
@@ -2116,7 +2116,7 @@ return function(Vargs, env)
local data = {
Tools = {};
SavedTools = {};
- Prefix = Settings.Prefix;
+ Prefix = Functions.GetMainPrefix();
SplitKey = Settings.SplitKey;
SpecialPrefix = Settings.SpecialPrefix;
}
@@ -4468,7 +4468,7 @@ return function(Vargs, env)
AdminLevel = "Moderators";
Function = function(plr: Player, args: {[number]:string})
Remote.MakeGui(plr, "Teams", {
- CmdPrefix = Settings.Prefix; CmdPlayerPrefix = Settings.PlayerPrefix; CmdSpecialPrefix = Settings.SpecialPrefix; CmdSplitKey = Settings.SplitKey;
+ CmdPrefix = Functions.GetMainPrefix(); CmdPlayerPrefix = Settings.PlayerPrefix; CmdSpecialPrefix = Settings.SpecialPrefix; CmdSplitKey = Settings.SplitKey;
})
end
};
diff --git a/MainModule/Server/Commands/Players.luau b/MainModule/Server/Commands/Players.luau
index 233703bd1..ee7ac02db 100644
--- a/MainModule/Server/Commands/Players.luau
+++ b/MainModule/Server/Commands/Players.luau
@@ -31,11 +31,11 @@ return function(Vargs, env)
for _,alias in cmd.Commands do
if #cmdAliases >= 4 and (#cmd.Commands - #cmdAliases) ~= 0 then
- table.insert(cmdAliases, `and {#cmd.Commands - #cmdAliases} more...`)
+ table.insert(cmdAliases, `and {#cmd.Commands - #cmdAliases} more...`)
break
end
end
-
+
table.remove(cmdAliases, 1)
local permissionDesc = Admin.FormatCommandAdminLevel(cmd)
@@ -83,7 +83,7 @@ return function(Vargs, env)
local cmd, ind
for i, v in Admin.SearchCommands(plr, "all") do
for _, p in v.Commands do
- if (v.Prefix or "")..string.lower(p) == string.lower(args[1]) then
+ if (if type(v.Prefix) == "table" then v.Prefix[1] else v.Prefix or "")..string.lower(p) == string.lower(args[1]) then
cmd, ind = v, i
break
end
@@ -107,7 +107,7 @@ return function(Vargs, env)
Title = "Command Info";
Icon = server.MatIcons.Info;
Table = {
- {Text = `Prefix: {cmd.Prefix}`, Desc = "Prefix used to run the command"},
+ {Text = `Prefix: {if type(cmd.Prefix) =="table" then table.concat(cmd.Prefix, ", ") else cmd.Prefix}`, Desc = "Prefix used to run the command"},
{Text = `Commands: {SanitizeXML(table.concat(cmd.Commands, ", "))}`, Desc = "Valid default aliases for the command"},
{Text = `Arguments: {if cmdArgs == "" then "-" else SanitizeXML(cmdArgs)}`, Desc = "Parameters taken by the command"},
{Text = `Admin Level: {Admin.FormatCommandAdminLevel(cmd)}`, Desc = "Rank required to run the command"},
@@ -150,7 +150,7 @@ return function(Vargs, env)
Description = "Shows you the command prefix using the :cmds command";
AdminLevel = "Players";
Function = function(plr: Player, args: {string})
- Functions.Hint(`{Settings.Prefix}cmds`, {plr})
+ Functions.Hint(`{if type(Settings.Prefix) == "table" then Settings.Prefix[1] else Settings.Prefix}cmds`, {plr})
end
};
@@ -567,46 +567,47 @@ return function(Vargs, env)
Description = "Shows you how to use some syntax related things";
AdminLevel = "Players";
Function = function(plr: Player, args: {string})
+ local Prefix = if type(Settings.Prefix) == "table" then Settings.Prefix[1] else Settings.Prefix
local usage = {
"";
"Mouse over things in lists to expand them";
"You can also resize windows by dragging the edges";
"";
- `Put /e in front to silence commands in chat (/e {Settings.Prefix}kill scel) or enable chat command hiding in client settings`;
+ `Put /e in front to silence commands in chat (/e {Prefix}kill scel) or enable chat command hiding in client settings`;
`Player commands can be used by anyone, these commands have {Settings.PlayerPrefix} infront, such as {Settings.PlayerPrefix}info and {Settings.PlayerPrefix}rejoin`;
"";
`――――― Player Selectors ―――――`;
- `Usage example: {Settings.Prefix}kill {Settings.SpecialPrefix}all (where {Settings.SpecialPrefix}all is the selector)`;
+ `Usage example: {Prefix}kill {Settings.SpecialPrefix}all (where {Settings.SpecialPrefix}all is the selector)`;
`{Settings.SpecialPrefix}me - Yourself`;
`{Settings.SpecialPrefix}all - Everyone in the server`;
`{Settings.SpecialPrefix}admins - All admins in the server`;
`{Settings.SpecialPrefix}nonadmins - Non-admins (normal players) in the server`;
`{Settings.SpecialPrefix}others - Everyone except yourself`;
`{Settings.SpecialPrefix}random - A random person in the server excluding those removed with -SELECTION`;
- `@USERNAME - Targets a specific player with that exact username Ex: {Settings.Prefix}god @Sceleratis would give a player with the username 'Sceleratis' god powers`;
- `#NUM - NUM random players in the server {Settings.Prefix}ff #5 will ff 5 random players excluding those removed with -SELECTION.`;
+ `@USERNAME - Targets a specific player with that exact username Ex: {Prefix}god @Sceleratis would give a player with the username 'Sceleratis' god powers`;
+ `#NUM - NUM random players in the server {Prefix}ff #5 will ff 5 random players excluding those removed with -SELECTION.`;
`{Settings.SpecialPrefix}friends - Your friends who are in the server`;
- `%TEAMNAME - Members of the team TEAMNAME Ex: {Settings.Prefix}kill %raiders`;
+ `%TEAMNAME - Members of the team TEAMNAME Ex: {Prefix}kill %raiders`;
`$GROUPID - Members of the group with ID GROUPID (number in the Roblox group webpage URL)`;
- `-SELECTION - Inverts the selection, ie. will remove SELECTION from list of players to run command on. {Settings.Prefix}kill all,-%TEAM will kill everyone except players on TEAM`;
- `+SELECTION - Readds the selection, ie. will readd SELECTION from list of players to run command on. {Settings.Prefix}kill all,-%TEAM,+Lethalitics will kill everyone except players on TEAM but also Lethalitics`;
- `radius-NUM -- Anyone within a NUM-stud radius of you. {Settings.Prefix}ff radius-5 will ff anyone within a 5-stud radius of you.`;
+ `-SELECTION - Inverts the selection, ie. will remove SELECTION from list of players to run command on. {Prefix}kill all,-%TEAM will kill everyone except players on TEAM`;
+ `+SELECTION - Readds the selection, ie. will readd SELECTION from list of players to run command on. {Prefix}kill all,-%TEAM,+Lethalitics will kill everyone except players on TEAM but also Lethalitics`;
+ `radius-NUM -- Anyone within a NUM-stud radius of you. {Prefix}ff radius-5 will ff anyone within a 5-stud radius of you.`;
"";
`――――― Repetition ―――――`;
- `Multiple player selections - {Settings.Prefix}kill me,noob1,noob2,{Settings.SpecialPrefix}random,%raiders,$123456,{Settings.SpecialPrefix}nonadmins,-scel`;
- `Multiple Commands at a time - {Settings.Prefix}ff me {Settings.BatchKey} {Settings.Prefix}sparkles me {Settings.BatchKey} {Settings.Prefix}rocket jim`;
- `You can add a delay if you want; {Settings.Prefix}ff me {Settings.BatchKey} !wait 10 {Settings.BatchKey} {Settings.Prefix}m hi we waited 10 seconds`;
- `{Settings.Prefix}repeat 10(how many times to run the cmd) 1(how long in between runs) {Settings.Prefix}respawn jim`;
+ `Multiple player selections - {Prefix}kill me,noob1,noob2,{Settings.SpecialPrefix}random,%raiders,$123456,{Settings.SpecialPrefix}nonadmins,-scel`;
+ `Multiple Commands at a time - {Prefix}ff me {Settings.BatchKey} {Prefix}sparkles me {Settings.BatchKey} {Prefix}rocket jim`;
+ `You can add a delay if you want; {Prefix}ff me {Settings.BatchKey} !wait 10 {Settings.BatchKey} {Prefix}m hi we waited 10 seconds`;
+ `{Prefix}repeat 10(how many times to run the cmd) 1(how long in between runs) {Prefix}respawn jim`;
"";
`――――― Reference Info ―――――`;
- `{Settings.Prefix}cmds for a list of available commands`;
- `{Settings.Prefix}cmdinfo <command> for detailed info about a specific command`;
+ `{Prefix}cmds for a list of available commands`;
+ `{Prefix}cmdinfo <command> for detailed info about a specific command`;
`{Settings.PlayerPrefix}brickcolors for a list of BrickColors`;
`{Settings.PlayerPrefix}materials for a list of materials`;
"";
- `{Settings.Prefix}capes for a list of preset admin capes`;
- `{Settings.Prefix}musiclist for a list of preset audios`;
- `{Settings.Prefix}insertlist for a list of insertable assets using {Settings.Prefix}insert`;
+ `{Prefix}capes for a list of preset admin capes`;
+ `{Prefix}musiclist for a list of preset audios`;
+ `{Prefix}insertlist for a list of insertable assets using {Prefix}insert`;
}
Remote.MakeGui(plr, "List", {
Title = "Usage";
@@ -937,7 +938,7 @@ return function(Vargs, env)
IsDonor = Admin.CheckDonor(v);
GameData = gameData;
IsServerOwner = v.UserId == game.PrivateServerOwnerId;
- CmdPrefix = Settings.Prefix;
+ CmdPrefix = Functions.GetMainPrefix();
CmdSplitKey = Settings.SplitKey;
OnlineFriends = Remote.Get(v, "Function", "GetFriendsOnline");
})
@@ -1018,7 +1019,7 @@ return function(Vargs, env)
ServerAge = service.FormatTime(os.time() - server.ServerStartTime);
ServerInternetInfo = serverInfo;
Refreshables = Logs.ListUpdaters.ServerDetails(plr);
- CmdPrefix = Settings.Prefix;
+ CmdPrefix = if type(Settings.Prefix) == "table" then Settings.Prefix[1] else Settings.Prefix,
CmdPlayerPrefix = Settings.PlayerPrefix;
SplitKey = Settings.SplitKey;
})
diff --git a/MainModule/Server/Core/Admin.luau b/MainModule/Server/Core/Admin.luau
index 0ad9ff97a..551cdbaf8 100644
--- a/MainModule/Server/Core/Admin.luau
+++ b/MainModule/Server/Core/Admin.luau
@@ -1193,9 +1193,16 @@ return function(Vargs, GetEnv)
for ind, data in Commands do
if type(data) == "table" then
for i,cmd in data.Commands do
- if data.Prefix == "" then Admin.BlankPrefix = true end
- tempPrefix[data.Prefix] = true
- tempTable[string.lower(data.Prefix..cmd)] = ind
+ if type(data.Prefix) ~= "table" and data.Prefix == "" then Admin.BlankPrefix = true end
+ if type(data.Prefix) == "table" then
+ for _,p in data.Prefix do
+ tempPrefix[p] = true
+ tempTable[string.lower(p..cmd)] = ind
+ end
+ else
+ tempPrefix[data.Prefix] = true
+ tempTable[string.lower(data.Prefix..cmd)] = ind
+ end
end
end
end
@@ -1232,13 +1239,27 @@ return function(Vargs, GetEnv)
end
for _, v in data.Commands do
- if not blacklistedCommands["/"..data.Prefix..v] then
- if not command1 then
- command1 = "/"..data.Prefix..v
- else
- command2 = "/"..data.Prefix..v
+ if type(data.Prefix) == "table" then
+ for _, p in data.Prefix do
+ if not blacklistedCommands["/"..p..v] then
+ if not command1 then
+ command1 = "/"..p..v
+ else
+ command2 = "/"..p..v
+ end
+ end
end
+ else
+ if not blacklistedCommands["/"..data.Prefix..v] then
+ if not command1 then
+ command1 = "/"..data.Prefix..v
+ else
+ command2 = "/"..data.Prefix..v
+ end
+ end
+
end
+
end
if command1 then
@@ -1336,7 +1357,7 @@ return function(Vargs, GetEnv)
FormatCommand = function(command, cmdn)
return table.concat({
- (command.Prefix or ""),
+ (if type(command.Prefix) == "table" then command.Prefix[1] else command.Prefix or ""),
tostring(command.Commands[cmdn or 1]),
#command.Args > 0 and Settings.SplitKey or "",
#command.Args > 0 and Admin.FormatCommandArguments(command) or ""
diff --git a/MainModule/Server/Core/Commands.luau b/MainModule/Server/Core/Commands.luau
index c8edb0711..e9b9ad82b 100644
--- a/MainModule/Server/Core/Commands.luau
+++ b/MainModule/Server/Core/Commands.luau
@@ -34,7 +34,7 @@ return function(Vargs, GetEnv)
t = server.Typechecker;
local ValidateCommandDefinition = t.interface({
- Prefix = t.string,
+ Prefix = t.union(t.string, t.array(t.string)),
Commands = t.array(t.string),
Description = t.string,
AdminLevel = t.union(t.string, t.number, t.nan, t.array(t.union(t.string, t.number, t.nan))),
@@ -99,7 +99,13 @@ return function(Vargs, GetEnv)
Admin.PrefixCache[cmd.Prefix] = true
for _, v in cmd.Commands do
- Admin.CommandCache[string.lower(cmd.Prefix..v)] = ind
+ if type(cmd.Prefix) == "table" then
+ for _,p in cmd.Prefix do
+ Admin.CommandCache[string.lower(p..v)] = ind
+ end
+ else
+ Admin.CommandCache[string.lower(cmd.Prefix..v)] = ind
+ end
end
cmd.Args = cmd.Args or cmd.Arguments or {}
diff --git a/MainModule/Server/Core/Functions.luau b/MainModule/Server/Core/Functions.luau
index e81407b0e..f137444a8 100644
--- a/MainModule/Server/Core/Functions.luau
+++ b/MainModule/Server/Core/Functions.luau
@@ -1664,5 +1664,11 @@ return function(Vargs, GetEnv)
end
return if allowNil then nil else BrickColor.random()
end;
- };
+
+ GetMainPrefix = function()
+ if type(Settings.Prefix) == "table" then return Settings.Prefix[1] else return Settings.Prefix end
+ end;
+ }
+
+
end
diff --git a/MainModule/Server/Dependencies/DefaultSettings.luau b/MainModule/Server/Dependencies/DefaultSettings.luau
index 4f1b082dd..d3eceb87d 100644
--- a/MainModule/Server/Dependencies/DefaultSettings.luau
+++ b/MainModule/Server/Dependencies/DefaultSettings.luau
@@ -140,10 +140,10 @@ settings.HideScript = true -- When the game starts the Adonis_Loader model
settings.DataStore = "Adonis_1" -- DataStore the script will use for saving data; Changing this will lose any saved data
settings.DataStoreKey = "CHANGE_THIS" -- CHANGE THIS TO ANYTHING RANDOM! Key used to encrypt all datastore entries; Changing this will lose any saved data
settings.DataStoreEnabled = true -- Disable if you don't want to load settings and admins from the datastore; PlayerData will still save
-settings.LocalDatastore = false -- If this is turned on, a mock DataStore will forcibly be used instead and shall never save across servers
+settings.LocalDatastore = false -- If this is turned on, a mock DataStore will forcibly be used instead and shall never save across servers
-settings.Storage = game:GetService("ServerStorage") -- Where things like tools are stored
-settings.RecursiveTools = false -- Whether tools that are included in sub-containers within settings.Storage will be available via the ;give command (useful if your tools are organized into multiple folders)
+settings.Storage = game:GetService("ServerStorage") -- Where things like tools are stored
+settings.RecursiveTools = false -- Whether tools that are included in sub-containers within settings.Storage will be available via the ;give command (useful if your tools are organized into multiple folders)
settings.Theme = "Default" -- UI theme;
settings.MobileTheme = "Mobilius" -- Theme to use on mobile devices; Some UI elements are disabled
@@ -157,7 +157,7 @@ settings.MobileTheme = "Mobilius" -- Theme to use on mobile devices; Some UI el
settings.Ranks = {
["Moderators"] = {
Level = 100;
- Users = {"Username"; "Username:UserId"; UserId; "Group:GroupId:GroupRank"; "Group:GroupId"; "Item:ItemID"; "GamePass:GamePassID";}
+ Users = {"Username"; "Username:UserId"; UserId; "Group:GroupId:GroupRank"; "Group:GroupId"; "Item:ItemID"; "GamePass:GamePassID"; "Subscription:SubscriptionId";}
}
}
@@ -210,7 +210,6 @@ settings.Aliases = {
[";examplealias "] = ";ff | ;fling | ;fire " --// Order arguments appear in alias string determines their required order in the command message when ran later
};
---// Use the below table to define pre-set cameras
settings.Cameras = {
--[[
"Camera Name" would be the name of your camera
@@ -239,7 +238,7 @@ settings.SaveAdmins = true -- If true anyone you ;admin or ;headadmin in-game
settings.LoadAdminsFromDS = true -- If false, any admins saved in your DataStores will not load
settings.WhitelistEnabled = false -- If true enables the whitelist/server lock; Only lets admins & whitelisted users join
-settings.Prefix = ";" -- The ; in ;kill me
+settings.Prefix = {";", ":"} -- A list of prefixes for commands, the ; in ;kill me
settings.PlayerPrefix = "!" -- The ! in !donate; Mainly used for commands that any player can run; Do not make it the same as settings.Prefix
settings.SpecialPrefix = "" -- Used for things like "all", "me" and "others" (If changed to ! you would do ;kill !me)
settings.SplitKey = " " -- The space in ;kill me (eg if you change it to / ;kill me would be ;kill/me)
@@ -294,7 +293,7 @@ settings.ChatCommands = true -- If false you will not be able to run commands
settings.CreatorPowers = true -- Gives me creator-level admin; This is strictly used for debugging; I can't debug without full access to the script
settings.CodeExecution = true -- Enables the use of code execution in Adonis; Scripting related (such as ;s) and a few other commands require this
settings.SilentCommandDenials = false -- If true, there will be no differences between the error messages shown when a user enters an invalid command and when they have insufficient permissions for the command
-settings.OverrideChatCallbacks = true -- If the TextChatService ShouldDeliverCallbacks of all channels are overridden by Adonis on load. Required for slowmode. Mutes use a CanSend method to mute when this is set to false.
+settings.OverrideChatCallbacks = true -- If the TextChatService ShouldDeliverCallbacks of all channels are overridden by Adonis on load. Required for slowmode. Mutes use a CanSend method to mute when this is set to false.
settings.BanMessage = "Banned" -- Message shown to banned users upon kick
settings.LockMessage = "Not Whitelisted" -- Message shown to people when they are kicked while the game is ;slocked
@@ -605,6 +604,7 @@ order = {
"AutoBackup";
" ";
"PlayerList";
+ " ";
"Console";
"Console_AdminsOnly";
" ";
diff --git a/MainModule/Server/Server.luau b/MainModule/Server/Server.luau
index 486f3ca2a..3294a0e6e 100644
--- a/MainModule/Server/Server.luau
+++ b/MainModule/Server/Server.luau
@@ -597,6 +597,23 @@ return service.NewProxy({
end
end
+ --// Attempts to patch Settings.Prefix to fix issues
+ if type(server.Settings.Prefix) == "table" then
+ setmetatable(server.Settings.Prefix, {
+ __concat = function(self, value)
+ return `{self[1]}{value}`
+ end,
+ __tostring = function(self)
+ return self[1]
+ end,
+
+ })
+ end
+
+
+
+
+
--// Bind cleanup
service.DataModel:BindToClose(function(...)
server.CleanUp(...)