Hierarquia
O sistema de chat legado usa o modelo cliente-servidor.Os componentes do módulo de chat do lado do servidor são gerenciados pelo ChatChannel e ChatSpeaker na administração, enquanto o cliente é responsável pela entrada e pelo exibição de mensagens.A comunicação entre o servidor e os clientes é tratada automaticamente usando RemoteEvents.
O próprio serviço do motor Chat é a unidade de armazenamento essencial para o sistema de chat: quando um local Roblox carrega (ou seja, no cliente ou no Studio ao executar ou jogar), os seguintes componentes são carregados automaticamente no serviço Chat se Chat.LoadDefaultChat for verdadeiro.
- Módulos de bate-papo — Este Folder é uma coleção de módulos que são necessários pelo ChatServiceRunner .Todo o conteúdo desta pasta é necessário pelo script e é usado para criar comportamento personalizado no servidor.
- ClientChatModules — Este diretório contém vários ModuleScripts necessários pelo ChatScript .
- Módulos de Comando — Contém módulos usados para implementar comandos de chat do lado do cliente.
- MessageCreatorModules — Contém módulos usados para lidar e formatar mensagens.
- Estados de bate-papo — Contém constantes compartilhadas pelo servidor e pelo cliente.
- Configurações de Chat — Armazena várias configurações para configurar diferentes aspectos da janela de chat.
- Localização de Chat — Estrutura de dados que armazena traduções de texto.
- Corredor de Serviço de Chat — Este Script executa o componente do servidor do chat.Em geral, isso não precisa ser modificado para criar um comportamento e funcionalidade de chat personalizados.
- BubbleChat — Mostra mensagens de bate-papo do usuário acima de seu avatar no jogo (se habilitado).
- ChatScript — Este LocalScript executa o componente do cliente do chat.Como o ChatServiceRunner, isso não deve precisar ser modificado para personalizar o chat.Quando o jogo é executado, isso é clonado automaticamente para o StarterPlayerScripts .
Modificar o sistema de chat
Para modificar ou personalizar o sistema de chat legado, você deve primeiro fazer uma cópia da hierarquia acima.
Na janela Explorer, localize TextChatService .Então, na janela Propriedades, defina a propriedade ChatVersion para LegacyChatService .
Execute a experiência usando o botão Jogar ( F5 ).
Selecione e copie ( CtrlC ou ⌘C ) os objetos que são adicionados a Chat .
Pare a experiência usando o botão Parar ( ShiftF5 ).
Certifique-se de que Chat.LoadDefaultChat esteja habilitado.
Fluxo de trabalho de chat
Antes de fazer módulos para personalizar o chat, é importante entender o fluxo de trabalho que uma mensagem de chat passa.Além de enviar mensagens de texto, há vários comandos construídos no sistema de chat, portanto, cada mensagem precisa ser verificada para ver se ela precisa ser interpretada como um comando ou apenas uma mensagem de texto.Mesmo mensagens de texto podem ser modificadas e filtradas no processo.
Depois que um usuário tem foco na entrada de chat e entra em um personagem, várias verificações são feitas imediatamente no cliente.Se o personagem for Esc, a caixa de entrada fecha e nenhuma ação é tomada.Se o personagem for qualquer coisa além de Enter, o texto será passado pelos processadores de comando Em andamento.Estes são usados para avaliar o texto para ver se alguma ação precisa ser tomada.Por exemplo, quando um usuário começa a sussurrar com o comando /whisper, assim que um nome de usuário for inserido após o comando, a caixa de entrada muda para indicar que o usuário está agora entrando em um canal de sussurros.
No lado do cliente do chat, existem dois tipos de processadores: Em andamento e Concluído.O primeiro avalia depois que cada personagem foi digitado, enquanto o último avalia somente quando o usuário terminou de digitar e atingiu Enter.
Quando o usuário termina de digitar e atinge Enter o texto, sua entrada é enviada através de vários mais processadores de comando.Se um comando Em andamento fizer um estado de chat personalizado, o chat verifica o estado para ver se um comando final deve ser executado e se a mensagem deve continuar.Se a mensagem for permitida continuar, o texto é enviado através de outro conjunto de processadores chamados Processadores Concluídos.Se algum desses processadores retornar verdadeiro, a mensagem deixa de ser enviada.Caso contrário, a mensagem é enviada ao servidor.
Uma vez que a mensagem chegue ao servidor, ela passa por outro conjunto de processadores de comando.Assim como os processadores Concluídos no cliente, se algum desses processadores retornar verdadeiro, a mensagem deixa de ser executada.Caso contrário, a mensagem é passada por um conjunto de filtros (incluindo o filtro de chat padrão do Roblox).Uma vez que tudo isso é feito, a mensagem é enviada a todos os canais e oradores apropriados.
Módulos do servidor
Módulos colocados em Módulos de Chat podem ser usados para uma variedade de propósitos.Esses módulos podem ser usados para gerenciar canais de chat e oradores, adicionar funções de filtro e comando, executar chatbots ou qualquer outra coisa que precise ser tratada no servidor.Para interagir com o sistema de chat, cada módulo é passado um ObjetoChatService.
Quando o ChatServiceRunner é iniciado, ele requer cada módulo dentro de ChatModules .Espera que cada módulo retorne uma função como então chama cada um dos módulos em turn, passando seu objeto ChatService para cada função.Independentemente do que o módulo pretende fazer (executar um bot, adicionar uma função de filtro, etc), ele precisa seguir esse formulário para funcionar.
Estrutura de Módulo de Amostra
local function Run(ChatService)
-- Código vai aqui
end
return Run
Adicionar canais
Uma das coisas mais simples que um ChatModule pode fazer é gerenciar canais .Objetos de canal podem ser criados com o método AddChannel() do Serviço de Chat.Observe que o objeto de canal só precisa ser usado ao chamar membros desse canal (como suas propriedades e funções).Ao se referir a canais do contexto de Serviço de Chat ou Oradores de Chat, o nome do canal é usado para referenciá-lo.
local function Run(ChatService)
local myChannel = ChatService:AddChannel("MyChannel")
end
return Run
Configuração básica do canal
Canais têm várias propriedades que podem ser usadas para modificá-los levemente.Por exemplo, este módulo cria um canal e define a Mensagem de Boas-Vindas e faz com que os usuários entrem automaticamente no canal quando entram na experiência.
local function Run(ChatService)
local myChannel = ChatService:AddChannel("MyChannel")
-- Defina a mensagem que é exibida quando um usuário se junta ao canal
myChannel.WelcomeMessage = "Welcome to my channel!"
-- Faz com que os jogadores entrem automaticamente no canal quando entram no jogo
myChannel.AutoJoin = true
end
return Run
Eventos de canal
Canais têm vários eventos que podem ser subscritos.Esses eventos são acionados quando uma Mensagem de bate-papo é postada no canal, quando um Palestrante sai ou entra ou quando um Palestrante é silenciado ou dessilenciado.Por exemplo, este módulo criará um canal com o nome MyChannel .Sempre que um orador se junta ou deixa o canal, uma mensagem do sistema será enviada a todos os oradores no canal, informando-os do evento.
local function Run(ChatService)
local myChannel = ChatService:AddChannel("MyChannel")
local function onSpeakerJoined(speakerName)
myChannel:SendSystemMessage(speakerName .. " has joined the channel.")
end
local function onSpeakerLeft(speakerName)
myChannel:SendSystemMessage(speakerName .. " has left the channel.")
end
myChannel.SpeakerJoined:Connect(onSpeakerJoined)
myChannel.SpeakerLeft:Connect(onSpeakerLeft)
end
return Run
Funções de comando
Outra coisa poderosa que Módulos de Chat pode fazer são comandos de chat >.Quando uma mensagem é enviada para o servidor, o chat enviará a mensagem através de cada função de comando que foi registrada no Serviço de Chat e no canal relevante.Essas funções são enviadas ao altofalante, mensagem e canal para o qual a mensagem está sendo enviada.A função pode tomar qualquer ação que precise e, em seguida, retornar verdadeiro ou falso.Se a função retornar verdadeiro, a mensagem deixa de ser processada pelo sistema de chat.Não será enviado para mais funções de comando nem será exibido na janela de chat.Se a função retornar falso, a mensagem continua através de todas as outras funções de comando.Se nenhuma das funções de comando retornar verdadeiro, a mensagem então será enviada através de filtros e depois exibida.
As funções de comando são frequentemente usadas para implementar Comandos de Administração, que são comandos de texto que certos usuários podem usar para manipular o estado da experiência através de texto específico dito no chat.
Neste exemplo, um ChatModule é usado para criar um Part se um usuário digitar /part no chat.Observe que essa função retorna verdadeiro se uma peça foi criada, o que fará com que a mensagem não prossiga e nenhuma mensagem será exibida.Se uma peça não for criada, esta função precisa retornar falso para que a mensagem possa continuar a funcionar através do sistema.
local Workspace = game:GetService("Workspace")
local function Run(ChatService)
local function createPart(speakerName, message, channelName)
if string.sub(message, 1, 5) == "/part" then
local newPart = Instance.new("Part")
newPart.Parent = Workspace
return true
end
return false
end
ChatService:RegisterProcessCommandsFunction("createPart", createPart)
end
return Run
Tanto Canais de Chat e Serviço de Chat em si podem ter comandos de chat. Os processadores de comando do ChatService executarão em cada mensagem enviada ao servidor, enquanto os comandos de canal só serão executados se a mensagem foi enviada para o canal ao qual o comando está registrado.
Funções de filtro
Mensagens que não são interrompidas por uma Função de Comando não vão passar por todas as funções de filtro que estão registradas no ChatService e nos canais relevantesCada função de filtro é passada para o altofalante, Objetode mensagem e nome do canal.Quaisquer alterações feitas no objeto de mensagem persistirão e cada função de filtro seguinte verá a mensagem atualizada.Observe que as funções de filtro não precisam retornar um valor.
Neste exemplo, uma função de filtro simples é registrada para fazer com que cada mensagem apareça em minúsculas.
local function Run(ChatService)
local function makeLowercase(sender, messageObject, channelName)
messageObject.Message = string.lower(messageObject.Message)
end
ChatService:RegisterFilterMessageFunction("makeLowercase", makeLowercase)
end
return Run
Módulos do cliente
Módulos colocados em ClientChatModules podem ser usados para criar comportamento personalizado para clientes.Esses módulos são divididos em dois diferentes pastas: Módulos de Comando e Módulos Criador de Mensagens.
Módulos de comando
Módulos de comando funcionam de maneira muito semelhante a módulos no servidor que registram funções de comando.Esses módulos definem funções que serão disparadas após o usuário ter inserido texto.Esse texto pode ser lido e o comando pode deixar a mensagem passar para o servidor ou interromper o progresso da mensagem.Comandos que são avaliados no final da mensagem são marcados com COMPLETED_MESSAGE_PROCESSOR enquanto comandos que são avaliados após cada personagem são marcados com IN_PROGRESS_MESSAGE_PROCESSOR .
Em ambos os tipos de comandos, o módulo deve retornar um dicionário que diga que tipo de processador o comando deve usar e qual função deve ser executada quando o processador for chamado.Por exemplo, um processador de mensagens concluído deve tomar a forma:
local util = require(script.Parent:WaitForChild("Util"))
function ProcessMessage(message, ChatWindow, ChatSettings)
end
return {
[util.KEY_COMMAND_PROCESSOR_TYPE] = util.COMPLETED_MESSAGE_PROCESSOR,
[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage
}
Observe que o KEY_COMMAND_PROCESSOR_TYPE enum é definido no Util dentro da pasta ModuleScript dentro da pasta CommandModules .
Comandos de mensagem concluídos
Comandos de Mensagem Concluídos são avaliados quando o usuário termina de digitar e atinge Enter.A função do processador é passada o ObjetoMensagem de Chat , a janela de chat do cliente e a tabela Configurações de Chat .Se a função retornar verdadeiro, a mensagem deixa de ser processada e não será enviada ao servidor.Caso contrário, será enviado através de todos os outros processadores e, eventualmente, para o servidor se nenhum dos outros processadores o parar.
Por exemplo, o seguinte processador removerá a mensagem mais antiga no canal atual se o usuário digitar o comando /last.
local util = require(script.Parent:WaitForChild("Util"))
function ProcessMessage(message, ChatWindow, ChatSettings)
if string.sub(message, 1, 5) == "/last" then
local currentChannel = ChatWindow:GetCurrentChannel()
if currentChannel then
currentChannel:RemoveLastMessageFromChannel()
end
return true
end
return false
end
return {
[util.KEY_COMMAND_PROCESSOR_TYPE] = util.COMPLETED_MESSAGE_PROCESSOR,
[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage
}
Comandos em andamento
Comandos em andamento são avaliados sempre que um usuário digita um personagem na entrada de chat.Por exemplo, o seguinte código toca depois de cada pressão de tecla para fazê-lo soar como se o usuário estivesse digitando em uma máquina de escribir:
local util = require(script.Parent:WaitForChild("Util"))
local keyEffect = Instance.new("Sound")
keyEffect.SoundId = "rbxassetid://12221976"
keyEffect.Parent = script
function ProcessMessage(message, ChatWindow, ChatBar, ChatSettings)
keyEffect:Play()
end
return {
[util.KEY_COMMAND_PROCESSOR_TYPE] = util.IN_PROGRESS_MESSAGE_PROCESSOR,
[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage
}
Comandos em andamento são frequentemente usados para criar um estado personalizado para o chat enviar mensagens para usuários específicos em vez do canal atual.Por exemplo, os sistemas Whisper e Team Chat verificam se o usuário digitou /whisper ou /team respectivamente e enviam a mensagem final para apenas os usuários apropriados.
Espera-se que um estado personalizado seja uma tabela com as seguintes funções:
- TextUpdated() — Chamado quando o texto na caixa de entrada muda.
- GetMessage() — Chamado depois que o usuário terminar de inserir a mensagem e acertar Enter. Esta função é esperada para retornar uma string / cadeia / texto.
- ProcessCompletedMessage() — Chamado quando a mensagem está sendo processada.Um processador de estado personalizado sempre será acionado antes dos processadores de mensagens concluídos.Como outros processadores, essa função deve retornar verdadeiro se a mensagem deve parar de ser enviada, caso contrário, deve retornar falso.
- Destroy() — Chamado depois que a mensagem foi enviada. Deve ser usado para limpar qualquer coisa configurada pelo estado personalizado.
Para usar um estado personalizado, a função ProcessMessage() da biblioteca de comando deve retornar o estado.Um estado personalizado básico tomaria a seguinte forma:
local util = require(script.Parent:WaitForChild("Util"))
local oneLineState = {}
oneLineState.__index = oneLineState
function oneLineState:TextUpdated()
local text = self.TextBox.Text
local length = string.len(text)
if length > 20 then
local chopLength = length - 20
local addToPrefix = string.sub(text, 1, chopLength)
self.Prefix = self.Prefix .. addToPrefix
self.TextBox.Text = string.sub(text, chopLength + 1)
end
end
function oneLineState:GetMessage()
local fullString = self.Prefix .. self.TextBox.Text
return fullString
end
function oneLineState:ProcessCompletedMessage()
return false
end
function oneLineState:Destroy()
self.Destroyed = true
end
function oneLineState.new(ChatWindow, ChatBar, ChatSettings)
local obj = {}
setmetatable(obj, oneLineState)
obj.Destroyed = false
obj.ChatWindow = ChatWindow
obj.ChatBar = ChatBar
obj.ChatSettings = ChatSettings
obj.TextBox = ChatBar:GetTextBox()
obj.MessageModeLabel = ChatBar:GetMessageModeTextLabel()
obj.Prefix = ""
return obj
end
local function ProcessMessage(message, ChatWindow, ChatBar, ChatSettings)
return oneLineState.new(ChatWindow, ChatBar, ChatSettings)
end
return {
[util.KEY_COMMAND_PROCESSOR_TYPE] = util.IN_PROGRESS_MESSAGE_PROCESSOR,
[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage
}
Uma das principais vantagens de usar um estado personalizado é que um módulo pode editar a barra de chat e seu texto contendo enquanto o usuário digita tanto em termos de função quanto de aparência, e então redefinir facilmente depois (uma vez que uma mensagem é enviada, um estado personalizado é automaticamente removido e tudo é redefinido de volta ao normal).Por exemplo, este código configura um estado personalizado que permite que apenas 20 caracteres sejam mostrados na caixa de texto de cada vez.Se o usuário continuar digitando, os caracteres no começo da string são removidos temporariamente.Quando o usuário envia a mensagem, todos os caracteres removidos são adicionados de volta à mensagem.
local util = require(script.Parent:WaitForChild("Util"))
local oneLineState = {}
oneLineState.__index = oneLineState
function oneLineState:TextUpdated()
local text = self.TextBox.Text
local length = string.len(text)
if length > 20 then
local chopLength = length - 20
local addToPrefix = string.sub(text, 1, chopLength)
self.Prefix = self.Prefix .. addToPrefix
self.TextBox.Text = string.sub(text, chopLength + 1)
end
end
function oneLineState:GetMessage()
local fullString = self.Prefix .. self.TextBox.Text
return fullString
end
function oneLineState:ProcessCompletedMessage()
return false
end
function oneLineState:Destroy()
self.Destroyed = true
end
function oneLineState.new(ChatWindow, ChatBar, ChatSettings)
local obj = {}
setmetatable(obj, oneLineState)
obj.Destroyed = false
obj.ChatWindow = ChatWindow
obj.ChatBar = ChatBar
obj.ChatSettings = ChatSettings
obj.TextBox = ChatBar:GetTextBox()
obj.MessageModeLabel = ChatBar:GetMessageModeTextLabel()
obj.Prefix = ""
return obj
end
local function ProcessMessage(message, ChatWindow, ChatBar, ChatSettings)
return oneLineState.new(ChatWindow, ChatBar, ChatSettings)
end
return {
[util.KEY_COMMAND_PROCESSOR_TYPE] = util.IN_PROGRESS_MESSAGE_PROCESSOR,
[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage
}
Como mencionado antes, uma vez que uma mensagem foi enviada, qualquer estado personalizado é removido e o chat é restaurado ao normal.Se for necessário redefinir um estado personalizado antes de enviar a mensagem, o estado pode ser redefinido com ChatBar:ResetCustomState() .Observe que isso removerá o foco da caixa de texto da barra de chat também.
Módulos de criador de mensagens
Outro tipo de módulo que pode ser usado no componente do cliente é um módulo Criador de Mensagens .Esse tipo de módulo é usado para criar os elementos da GUI na janela de chat para exibir a mensagem.Cada tipo de criador de mensagens define um novo digitarde mensagem, para que diferentes mensagens possam ser criadas com diferentes formatos.Além disso, elementos de GUI podem ser adicionados à exibição de mensagens dessa maneira, o que permite imagens, botões e muito mais.
Esses módulos requerem configuração em vários locais diferentes.Para cada digitarde mensagem, deve haver um dentro de MessageCreatorModules.Além disso, o Chat Становья ModuleScript precisa ser editado para incluir o novo tipo de mensagem.Por último, os módulos são usados apenas se um componente do servidor de chat criar uma nova mensagem com o digitarde mensagem dado.
O seguinte exemplo passará por fazer um bot que diga a hora a cada 5 segundos e a mensagem que for enviada terá um fundo vermelho.
Para iniciar, o Chatasets precisa adicionar um campo para o novo tipo de mensagem.
-- Constantes de Chatlocal module = {}--[[Tipos de Mensagem]]module.MessageTypeDefault = "Message"module.MessageTypeSystem = "System"module.MessageTypeMeCommand = "MeCommand"module.MessageTypeWelcome = "Welcome"module.MessageTypeSetCore = "SetCore"module.MessageTypeWhisper = "Whisper"module.MessageTypeTime = "Time"module.MajorVersion = 0module.MinorVersion = 2return module
O próprio bot é criado em um novo Módulo de Chat no servidor.Observe que uma função de filtro é usada para adicionar o novo tipo de mensagem aos e-mails que o bot envia.
-- Novo ModuleScript a ser colocado em ChatModules
local Chat = game:GetService("Chat")
local ReplicatedModules = Chat:WaitForChild("ClientChatModules")
local ChatConstants = require(ReplicatedModules:WaitForChild("ChatConstants"))
local function Run(ChatService)
local timeBot = ChatService:AddSpeaker("TimeBot")
timeBot:JoinChannel("All")
local function addMessageType(speaker, messageObject, channelName)
if speaker == "TimeBot" then
messageObject.MessageType = ChatConstants.MessageTypeTime
end
end
ChatService:RegisterFilterMessageFunction("TimeBotFilter", addMessageType)
task.spawn(function()
while task.wait(5) do
timeBot:SayMessage("The current time is: " .. os.time(), "All", {})
end
end)
end
return Run
Por último, um módulo de criador de mensagens deve ser feito.Este módulo deve retornar um dicionário com dois elementos: o tipo da mensagem, indexado com KEY_MESSAGE_TYPE , e a função a ser chamada ao criar os elementos da GUI da mensagem, indexados com KEY_CREATOR_FUNCTION .
A função armazenada por KEY_CREATOR_FUNCTION precisa retornar um dicionário com vários componentes.Primeiro, precisa incluir um Frame e TextLabel que serão exibidos na janela de chat.Elas podem ser criadas com a função util:CreateBaseMessage().O dicionário também precisa incluir uma função para executar se o texto da mensagem for atualizado.Quando as mensagens aparecem primeiro no cliente, elas têm texto de espaço reservado em branco enquanto a mensagem está sendo processada e filtrada, então objetos de mensagem como este precisam lidar com o que acontece quando recebem uma chamada para atualização.Em seguida, o dicionário precisa incluir uma função para determinar a altura do quadro.Essa função frequentemente chama a função util:GetMessageHeight().Por último, o dicionário precisa incluir várias funções que definem como os elementos devem desaparecer quando a janela desaparecer (a função de utilidade para isso é util:CreateFadeFunctions() ).
-- novo ModuleScript a ser incluído em MessageCreatorModules
local messageCreatorModules = script.Parent
local util = require(messageCreatorModules:WaitForChild("Util"))
local clientChatModules = messageCreatorModules.Parent
local ChatSettings = require(clientChatModules:WaitForChild("ChatSettings"))
local ChatConstants = require(clientChatModules:WaitForChild("ChatConstants"))
local function CreateMessageLabel(messageData, channelName)
-- Crie os objetos de GUI para o Frame e TextLabel para conter a mensagem
local BaseFrame, BaseMessage = util:CreateBaseMessage("", ChatSettings.DefaultFont, ChatSettings.ChatWindowTextSize, ChatSettings.DefaultMessageColor)
-- Mude o plano de fundo do Frame para vermelho
BaseFrame.BackgroundColor3 = Color3.new(1,0,0)
BaseFrame.BackgroundTransparency = 0
-- Manusear o texto da mensagem de atualização de espaço reservado
local function UpdateTextFunction(messageObject)
if messageObject.IsFiltered then
BaseMessage.Text = messageObject.Message
end
end
UpdateTextFunction(messageData)
-- Use a função util para determinar a altura do quadro
local function GetHeightFunction(xSize)
return util:GetMessageHeight(BaseMessage, BaseFrame, xSize)
end
-- Crie funções de desaparecimento que são chamadas quando a janela de chat desaparece
local FadeParameters = {}
FadeParameters[BaseMessage] = {
TextTransparency = {FadedIn = 0, FadedOut = 1},
TextStrokeTransparency = {FadedIn = 0.75, FadedOut = 1}
}
local FadeInFunction, FadeOutFunction, UpdateAnimFunction = util:CreateFadeFunctions(FadeParameters)
-- Retornar dicionário que define a etiqueta de etiqueta / rótulo
return {
[util.KEY_BASE_FRAME] = BaseFrame,
[util.KEY_BASE_MESSAGE] = BaseMessage,
[util.KEY_UPDATE_TEXT_FUNC] = UpdateTextFunction,
[util.KEY_GET_HEIGHT] = GetHeightFunction,
[util.KEY_FADE_IN] = FadeInFunction,
[util.KEY_FADE_OUT] = FadeOutFunction,
[util.KEY_UPDATE_ANIMATION] = UpdateAnimFunction
}
end
return {
[util.KEY_MESSAGE_TYPE] = ChatConstants.MessageTypeTime,
[util.KEY_CREATOR_FUNCTION] = CreateMessageLabel
}