[UPDATED] Char/Character Command

Char/Character Command by @LoveliestJacob

Click here to view the old post. (Version v1.2)

Hello, I made a command commonly known as “char” for fun.
The model includes the ability to hide usage of the command, match/keep player size, whitelist users, & a gamepass requirement option.

I would like to mention that the code could likely be changed to be more efficient. So if you have any suggestion/changes please tell me.

Model: https://www.roblox.com/library/7084191278/CharCommand-v1-2
Modal (creator marketplace): CharCommand v1.2 - Creator Marketplace (roblox.com)
Place: Open Source Projects - Roblox

Contents:

CharCommand
-- SETTINGS --

local prefix = "/"
local matchsize = false
local hidemessage = true
local gamepassid = nil -- leave nil if you dont want the command to require a gamepass
local needswhitelist = false -- set to true if you want only players in whitelist to use the command
local whitelist = {} 
-- list of user ids of who you would like to use this command, requires needswhitelist to be true
-- owner of the game/group is added by default, so you dont need to add them yourself
-- Examples: local whitelist = {1,2,3}

-- END OF SETTINGS --

local gamepassusers = {} 

local chatservice = require(game:GetService("ServerScriptService"):WaitForChild("ChatServiceRunner"):WaitForChild("ChatService"))

game:GetService("Chat"):RegisterChatCallback(Enum.ChatCallbackType.OnServerReceivingMessage, function(message)
	local chatspeaker = chatservice:GetSpeaker(message.FromSpeaker)
	local arguments = message.Message:lower():split(" ")
	local player = chatspeaker:GetPlayer()
	local normalarguments = message.Message:split(" ")

	if player == nil then
		return message
	end

	if arguments[1] == prefix.."char" then
		if hidemessage == true then
			message.ShouldDeliver = false
		end

		if gamepassid ~= nil then
			if table.find(gamepassusers, player.UserId) == nil then
				chatspeaker:SendSystemMessage("You need to own the gamepass to use this command.", message.OriginalChannel)
				return message
			end
		end	
		
		if needswhitelist == true then
			if table.find(whitelist, player.UserId) == nil then
				chatspeaker:SendSystemMessage("You are not whitelisted to use this command.", message.OriginalChannel)
				return message
			end
		end
		
		if arguments[2] == nil then
			chatspeaker:SendSystemMessage("Please provide a username.", message.OriginalChannel)
		elseif player.Character == nil then
			chatspeaker:SendSystemMessage("Character has not loaded.", message.OriginalChannel)
		else
			script.CharFunction.Fire:Fire(player, chatspeaker.Name, message.OriginalChannel, arguments, normalarguments, matchsize)
		end
	end

	return message
end)

if game.CreatorType == Enum.CreatorType.User then
	table.insert(whitelist, game.CreatorId)	
end

game:GetService("Players").PlayerAdded:Connect(function(player)
	if game.CreatorType == Enum.CreatorType.Group then
		if player:GetRankInGroup(game.CreatorId) == 255 then
			table.insert(whitelist, player.UserId)
		end
	end
	if gamepassid ~= nil then
		local success, err = pcall(function()
			if game:GetService("MarketplaceService"):UserOwnsGamePassAsync(player.UserId, gamepassid) then
				table.insert(gamepassusers, player.UserId)
			end
		end)
	end
end)
CharFunction
local chatservice = require(game:GetService("ServerScriptService"):WaitForChild("ChatServiceRunner"):WaitForChild("ChatService"))

script.Fire.Event:Connect(function(player, name, channel, arguments, normalarguments, matchsize)
	local chatspeaker = chatservice:GetSpeaker(name)
	
	local humanoiddescription = nil

	local success, err = pcall(function()
		humanoiddescription = game:GetService("Players"):GetHumanoidDescriptionFromUserId(game:GetService("Players"):GetUserIdFromNameAsync(arguments[2]))
	end)

	if humanoiddescription ~= nil then
		if player.Character.Humanoid:FindFirstChild("HumanoidDescription") then
			if matchsize == true then
				local pasthumanoiddescription = player.Character.Humanoid.HumanoidDescription
				humanoiddescription.BodyTypeScale = pasthumanoiddescription.BodyTypeScale
				humanoiddescription.DepthScale = pasthumanoiddescription.DepthScale
				humanoiddescription.HeadScale = pasthumanoiddescription.HeadScale
				humanoiddescription.HeightScale = pasthumanoiddescription.HeightScale
				humanoiddescription.ProportionScale = pasthumanoiddescription.ProportionScale
				humanoiddescription.WidthScale = pasthumanoiddescription.WidthScale
			end
		end
	end
	
	if success then
		player.Character.Humanoid:ApplyDescription(humanoiddescription)
		chatspeaker:SendSystemMessage("Successfully made your character look like "..normalarguments[2].."!", channel)
	else
		chatspeaker:SendSystemMessage("An error occurred, did you provide the correct username? If so, that user might be terminated.", channel)
	end
end)

(Better) Setting Explanations

Name Default Value Explanation
prefix "/" Chat command prefix. For example, if the prefix is “;” the command would then be “;char”.
matchsize false If false, your character will change to the size of the person you are changing to. (R15 Characters Only)
hidemessage true If true, the command will not show in chat when used. Please note that this does not hide the command response, which is only shown to the player who used the command anyways.
gamepassid nil This setting allows you to provide a gamepass requirement. If someone doesn’t own the gamepass, they will not have permission to use the command. Important: If needswhitelist is true then the player will still need to be in the whiteliest even if they own the gamepass.
needswhitelist false If true, players will need to be in the whiteliest in order to be able to use the command. Owner of the group/game is added by default. Important: If gamepassid is not nil then the player will be required to own the gamepass, even if they are in the whiteliest.
whitelist {} Table of userids of who should be in the whitelist. If you wanted Roblox, Builderman, and Stickmasterluke to be in your whitelist for example: {1, 156, 80254}

Edit (5/6/2022): If you’re looking for a video guide, check this video out!
https://youtu.be/H3cEVOXA7b8

:sparkles: Recently updated: I’ve recently updated the module to support TextChatService. (Legacy chat is still supported.) I’ve also added a bit more customization, while keeping the purpose of the module the same. Command permissions was heavily changed, now going by a “if any of these are true, let them in” kind of system.

Char/Character Command enables players in your game to run a command to turn their character into someone else’s. Includes methods to lock the command behind a gamepass or whitelist.

It’s important to note that this does not do anything other than modify the character’s customization. Name display and etc do not get modified. It’s possible for bad actors to trick younger players into thinking they are someone else (usually popular figures). Please use this module with consideration.

Installation

Grab the model/scripts from one of the following locations:

Click here to view the raw source.

CharModuleLoader (Script)

local CharModule = require(script.CharModule)("char", "/")

CharModule:ToggleKeepSize(false)

-- Examples

-- CharModule:ToggleKeepSize(true)
-- CharModule.Access:AddWhitelist({"Roblox", 1})
-- CharModule.Access:AddGamepass(0)

CharModuleLoader/CharModule (Module Script)

local PlayersService = game:GetService("Players")
local ChatService = game:GetService("Chat")
local TextChatService = game:GetService("TextChatService")
local StarterPlayerService = game:GetService("StarterPlayer")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local MarketPlaceService = game:GetService("MarketplaceService")

local AccessTable = {}
local Settings = {
	KeepSize = false,
	PromptGamepassPurchase = nil
}

local UsingLegacyChat = false
local NotificationsRemoteEvent: RemoteEvent | nil = nil

if TextChatService.ChatVersion == Enum.ChatVersion.LegacyChatService then UsingLegacyChat = true end

if script:FindFirstChild("CharModuleLocal") and UsingLegacyChat == false then 
	script.CharModuleLocal.Parent = StarterPlayerService.StarterPlayerScripts	
	NotificationsRemoteEvent = Instance.new("RemoteEvent")
	NotificationsRemoteEvent.Name = "CharModuleNotificationsRemoteEvent"
	NotificationsRemoteEvent.Parent = ReplicatedStorage
end

return function(CommandString: string | nil, Prefix: string | nil) 
	CommandString = (CommandString or "char"):lower()
	Prefix = (Prefix or "/"):lower()

	local function ChangeCharacter(Player: Player, TargetUsername: string): string 
		local HasAccess = false

		if #AccessTable == 0 then
			HasAccess = true
		else
			for _, AccessFunction in ipairs(AccessTable) do
				if AccessFunction(Player) == true then
					HasAccess = true
					break
				end
			end
		end

		if HasAccess == false then 
			if Settings.PromptGamepassPurchase ~= nil then
				MarketPlaceService:PromptGamePassPurchase(Player, Settings.PromptGamepassPurchase)
				return "You do not have access to use this command. You have been prompted to purchase the gamepass which gives you access to use the command." 
			else
				return "You do not have access to use this command." 
			end
		end

		local Success, ErrorMessageOrUsername = pcall(function()
			local Character: Model = Player.Character
			local Humanoid: Humanoid = Character.Humanoid
			local UserId = PlayersService:GetUserIdFromNameAsync(TargetUsername)
			local NewHumanoidDescription = PlayersService:GetHumanoidDescriptionFromUserId(UserId)

			if Settings.KeepSize == true then
				local CurrentHumanoidDescription: HumanoidDescription | nil = Humanoid:FindFirstChildOfClass("HumanoidDescription")
				if CurrentHumanoidDescription ~= nil then
					local Properties = {"BodyTypeScale", "DepthScale", "HeadScale", "HeightScale", "ProportionScale", "WidthScale"}
					for _, Property in ipairs(Properties) do
						NewHumanoidDescription[Property] = CurrentHumanoidDescription[Property]
					end
				end
			end

			Humanoid:ApplyDescription(NewHumanoidDescription)

			return TargetUsername
		end)
		if not Success then
			return "Something went wrong: "..ErrorMessageOrUsername
		else
			return "Successfully changed your character to " .. ErrorMessageOrUsername .. "."
		end
	end

	if UsingLegacyChat == true then
		local ChatMessagesService = require(ServerScriptService:WaitForChild("ChatServiceRunner"):WaitForChild("ChatService"))
		ChatService:RegisterChatCallback(Enum.ChatCallbackType.OnServerReceivingMessage, function(Message)
			local Speaker = ChatMessagesService:GetSpeaker(Message.FromSpeaker)
			local Player = Speaker:GetPlayer()

			if not Player then return Message end

			if Message.Message:lower():split(" ")[1] == Prefix .. CommandString then
				Message.ShouldDeliver = false
				task.spawn(function()
					local ResultMessage = ChangeCharacter(Player, Message.Message:lower():split(" ")[2])
					Speaker:SendSystemMessage("[System Message]: " .. ResultMessage, Message.OriginalChannel)
				end)
			end

			return Message
		end) 
	else
		local TextChatCommands = TextChatService:WaitForChild("TextChatCommands")
		local Command = Instance.new("TextChatCommand")
		Command.Name = "CharCommand"
		Command.PrimaryAlias = Prefix .. CommandString

		Command.Triggered:Connect(function(Source, Message)
			local Player = PlayersService:GetPlayerByUserId(Source.UserId)
			if not Player then return end

			local ResultMessage = ChangeCharacter(Player, Message:lower():split(" ")[2])
			pcall(function()
				return NotificationsRemoteEvent:FireClient(Player, ResultMessage)
			end)
		end)

		Command.Parent = TextChatCommands
	end

	local Methods = {}
	Methods.Access = {}

	function Methods:ToggleKeepSize(Value: boolean)
		Settings.KeepSize = Value
	end

	function Methods.Access:AddFunction(AccessFunction: (Player: Player) -> boolean)
		table.insert(AccessTable, AccessFunction)
	end

	function Methods.Access:AddGamepass(GamepassId: number)
		Settings.PromptGamepassPurchase = GamepassId
		table.insert(AccessTable, function(Player)
			local Success, Result = pcall(function()
				return MarketPlaceService:UserOwnsGamePassAsync(Player.UserId, GamepassId)
			end)
			if Success then
				return Result
			else
				return false
			end
		end)
	end

	function Methods.Access:AddWhitelist(Players: {number | string})
		table.insert(AccessTable, function(Player)
			local InWhitelisted = false
			for _, UsernameOrId in ipairs(Players) do
				if type(UsernameOrId) == "string" then
					if Player.Name == UsernameOrId then
						InWhitelisted = true
						break
					end
				elseif type(UsernameOrId) == "number" then
					if Player.UserId == UsernameOrId then
						InWhitelisted = true
						break
					end
				end
			end
			return InWhitelisted
		end)
	end

	return Methods
end 

CharModuleLoader/CharModule/CharModuleLocal (Local Script)

local TextChatService = game:GetService("TextChatService")
local PlayersService = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local LocalPlayer = PlayersService.LocalPlayer
local NotificationsRemote: RemoteEvent = ReplicatedStorage:WaitForChild("CharModuleNotificationsRemoteEvent")
local Channels = TextChatService:WaitForChild("TextChannels")

NotificationsRemote.OnClientEvent:Connect(function(Message: string)
	if type(Message) ~= "string" then return end
	for _, Channel: TextChannel in ipairs(Channels:GetChildren()) do
		if Channel:FindFirstChild(LocalPlayer.Name) then
			Channel:DisplaySystemMessage("<b>[System Message]:</b> " .. Message)
			break
		end
	end 
end)

Place the script in ServerScriptService, then you’re all set. (See next section for customization.)

Customization/API

If you don't know how to script, click here for a beginner explanation.

To make changes, open the CharModuleLoader script that you placed inside ServerScriptService.

  • To edit the prefix and the name of the command. Edit the following line:
local CharModule = require(script.CharModule)("char", "/")
-- Examples:
-- * Replace "char" with "character" to make the command /character.
-- * Replace "/" with "!" to make the command !char.
  • If you do not want a player’s character to change sizes when they use the command. Edit the following line:
CharModule:ToggleKeepSize(false)
-- Change the word false to true to apply this change.
  • To lock the command behind a whitelist. Add the following to the end of the script:
CharModule.Access:AddWhitelist({"Username", 0})
-- The list supports user ids and username. (User ids are recommended.)
-- For example, if you wanted Roblox to be able to use the command, then you would do:
CharModule.Access:AddWhitelist({1}) -- Or CharModule.Access:AddWhitelist({"Roblox"})
-- Since the `AddWhitelist` takes a table, you can add as many ids or usernames as you wish:
CharModule.Access:AddWhitelist({1, 2, "Example1", 3, "Example2", "Example3"}) 
  • If you would like to lock the command behind a gamepass. Then add the following to the end of the script:
CharModule.Access:AddGamepass(0)
-- Replace 0 with the id of the gamepass.

Scripts

  • CharModuleLoader - An example loader for the module, can be easily replaced with your own.
    • CharModule - The main module with methods for customization.
      • CharModuleLocal - A local script which is used to display chat messages. Can be deleted without breaking any core functionality. (Not used at all when legacy chat is enabled.)

API

require(CharModule)(CommandString: string | nil, Prefix: string | nil)Methods{}

  • CommandString - Optional name of the command. Default is "char".
  • Prefix - Optional prefix for the command. Default is "/".
  • Returns a table of methods for further customization.

Methods:ToggleKeepSize(Value: boolean)void

Set the value of KeepSize. True will prevent the size of a character from changing when the command is used.

  • Value - The boolean to set KeepSize to.

Methods.Access → AccessMethods{}

A table of access methods which modify the requirements for using the command.


AccessMethods:AddFunction(AccessFunction: (Player: Player) -> boolean)void

Adds an access function to the table of requirements.

  • AccessFunction - A function which takes the player as the first argument and returns a boolean indicating whether the player has access or not.

Example:

CharModule.Access:AddFunction(function(Player)
	return Player.Name == "Roblox"
end)

AccessMethods:AddGamepass(GamepassId: number)void

Adds an access function which is based on if a player owns the provided gamepass.

  • GamepassId - The id of the gamepass.

AccessMethods:AddWhitelist(Players: {number | string})void

Adds an access function which checks if the player’s username or id is in a whitelist/table.

  • Players - A table of usernames and/or ids which should have access.

Usage Showcase

Char/Character Command Showcase (youtube.com)

Questions/Support/Etc

If you need assistance or have any suggestions for the module, please let me know!
Happy coding! :heart:

15 Likes

Hey its really amazing! I would use it on my game.

1 Like

I’m pretty sure every other admin system already has a char command. There’s no need for this.

1 Like

Thank you for your reply, however I do believe if someone was looking for a char command, they might decide to use this or something similar to this instead of a whole admin module. And as I said I made this for fun, so what’s the harm in sharing something someone else could possibly learn from.

5 Likes

but its not always safe to use a admin system using these types of commands are safe because now hackers can easily hack admin systems example (khols, HD, etc…) i will recommended using scripts like these which are safe because admin systems are not much safe always. thank you.

1 Like

Where did you even get this false information from?? Unless you have a malicious version of an admin system (which is probably the case) the most popular ones should be the safest ones.

This isn’t secure either; there’s absolutely 0 checks to see if a player can use the command or not, allowing for easy impersonation.

5 Likes

Thank you for your reply. I don’t see what your point is, the only thing that changes is the person’s avatar. Same as most admin modules. If someone was worried about impersonation that much, they likely wouldn’t add a char command in the first place.

2 Likes

Thank you for your reply. I agree that some admin modules could likely be exploited, but that isn’t the case for admin modules such as HD admin as @RealExoctic said. They have checks to prevent such from happening.

3 Likes

That’s… where permissions come in? Why do you think there’s ranks in EVERY. SINGLE. ADMIN SYSTEM? Why do you think there’s permissions on modern operating systems?

2 Likes

Thank you for your reply. I’m still a bit confused, are you worried about who uses it? If that’s the case, I might add a whitelist in a future version of the model.

5 Likes

Added features in v1.2:

  • Moved settings into the main script.
  • Added whitelist support.
  • Added game pass support.
  • A few minor changes, including making hide message true by default.
1 Like

Why do you need to go so bonkers over this resource? It’s not like it’s the only resource on earth. This is meant to help people who don’t know how to make it. If you don’t like, don’t use it. No need to scream your head off about it.

3 Likes

There is no need to publicly tell off another person on this thread, that’s not the point of this thread. If you care to tell them this information, take it to developer forum private messages, best advice.

To OP,

I can see how people would need this resource for setting avatars, however at the end of the day it’s just way behind in every other free admin system or even proprietary admin system that a user could make. I like the effort, and I think this could go a long way if you were to compile this to be a full stacked admin system. :ok_hand:

3 Likes

Thank you for your reply. I agree, this project was made for fun in the end. I didn’t expect it to get so many replies. :sweat_smile:

3 Likes

Since this is a Char Command and what not, maybe you could add other character customizing traits to this project (if you want to that is), such as commands like setting body colors, removing current hats/accessories, and being able to insert one from the catalog using the Hats ID, etc. Would make this more interesting for a standalone character editor admin

4 Likes

Thank you for your reply. That’s a great idea, I’ll probably organize this a bit more so It doesn’t turn into a long if/else chain. But I’ll make sure to include your suggestions in the next version.

4 Likes

It’s been a while, lol. I’ve come to notice that the module likely wasn’t going to work in any new experiences. So, I’ve updated it.

Update includes:

  • Better code overall.
  • A little more customization.
  • Full support for the new chat system.

Happy coding! :heart: