1
0
mirror of https://github.com/citizenfx/cfx-server-data.git synced 2025-03-13 07:17:23 +08:00

293 lines
7.4 KiB
Lua

RegisterServerEvent('chat:init')
RegisterServerEvent('chat:addTemplate')
RegisterServerEvent('chat:addMessage')
RegisterServerEvent('chat:addSuggestion')
RegisterServerEvent('chat:removeSuggestion')
RegisterServerEvent('_chat:messageEntered')
RegisterServerEvent('chat:clear')
RegisterServerEvent('__cfx_internal:commandFallback')
-- this is a built-in event, but somehow needs to be registered
RegisterNetEvent('playerJoining')
exports('addMessage', function(target, message)
if not message then
message = target
target = -1
end
if not target or not message then return end
TriggerClientEvent('chat:addMessage', target, message)
end)
local hooks = {}
local hookIdx = 1
exports('registerMessageHook', function(hook)
local resource = GetInvokingResource()
hooks[hookIdx + 1] = {
fn = hook,
resource = resource
}
hookIdx = hookIdx + 1
end)
local modes = {}
local function getMatchingPlayers(seObject)
local players = GetPlayers()
local retval = {}
for _, v in ipairs(players) do
if IsPlayerAceAllowed(v, seObject) then
retval[#retval + 1] = v
end
end
return retval
end
exports('registerMode', function(modeData)
if not modeData.name or not modeData.displayName or not modeData.cb then
return false
end
local resource = GetInvokingResource()
modes[modeData.name] = modeData
modes[modeData.name].resource = resource
local clObj = {
name = modeData.name,
displayName = modeData.displayName,
color = modeData.color or '#fff',
isChannel = modeData.isChannel,
isGlobal = modeData.isGlobal,
}
if not modeData.seObject then
TriggerClientEvent('chat:addMode', -1, clObj)
else
for _, v in ipairs(getMatchingPlayers(modeData.seObject)) do
TriggerClientEvent('chat:addMode', v, clObj)
end
end
return true
end)
local function unregisterHooks(resource)
local toRemove = {}
for k, v in pairs(hooks) do
if v.resource == resource then
table.insert(toRemove, k)
end
end
for _, v in ipairs(toRemove) do
hooks[v] = nil
end
toRemove = {}
for k, v in pairs(modes) do
if v.resource == resource then
table.insert(toRemove, k)
end
end
for _, v in ipairs(toRemove) do
TriggerClientEvent('chat:removeMode', -1, {
name = v
})
modes[v] = nil
end
end
local function routeMessage(source, author, message, mode, fromConsole)
if source >= 1 then
author = GetPlayerName(source)
end
local outMessage = {
color = { 255, 255, 255 },
multiline = true,
args = { message },
mode = mode
}
if author ~= "" then
outMessage.args = { author, message }
end
if mode and modes[mode] then
local modeData = modes[mode]
if modeData.seObject and not IsPlayerAceAllowed(source, modeData.seObject) then
return
end
end
local messageCanceled = false
local routingTarget = -1
local hookRef = {
updateMessage = function(t)
-- shallow merge
for k, v in pairs(t) do
if k == 'template' then
outMessage['template'] = v:gsub('%{%}', outMessage['template'] or '@default')
elseif k == 'params' then
if not outMessage.params then
outMessage.params = {}
end
for pk, pv in pairs(v) do
outMessage.params[pk] = pv
end
else
outMessage[k] = v
end
end
end,
cancel = function()
messageCanceled = true
end,
setSeObject = function(object)
routingTarget = getMatchingPlayers(object)
end,
setRouting = function(target)
routingTarget = target
end
}
for _, hook in pairs(hooks) do
if hook.fn then
hook.fn(source, outMessage, hookRef)
end
end
if modes[mode] then
local m = modes[mode]
m.cb(source, outMessage, hookRef)
end
if messageCanceled then
return
end
TriggerEvent('chatMessage', source, #outMessage.args > 1 and outMessage.args[1] or '', outMessage.args[#outMessage.args])
if not WasEventCanceled() then
if type(routingTarget) ~= 'table' then
TriggerClientEvent('chat:addMessage', routingTarget, outMessage)
else
for _, id in ipairs(routingTarget) do
TriggerClientEvent('chat:addMessage', id, outMessage)
end
end
end
if not fromConsole then
print(author .. '^7' .. (modes[mode] and (' (' .. modes[mode].displayName .. ')') or '') .. ': ' .. message .. '^7')
end
end
AddEventHandler('_chat:messageEntered', function(author, color, message, mode)
if not message or not author then
return
end
local source = source
routeMessage(source, author, message, mode)
end)
AddEventHandler('__cfx_internal:commandFallback', function(command)
local name = GetPlayerName(source)
-- route the message as if it were a /command
routeMessage(source, name, '/' .. command, nil, true)
CancelEvent()
end)
-- player join messages
AddEventHandler('playerJoining', function()
if GetConvarInt('chat_showJoins', 1) == 0 then
return
end
TriggerClientEvent('chatMessage', -1, '', { 255, 255, 255 }, '^2* ' .. GetPlayerName(source) .. ' joined.')
end)
AddEventHandler('playerDropped', function(reason)
if GetConvarInt('chat_showQuits', 1) == 0 then
return
end
TriggerClientEvent('chatMessage', -1, '', { 255, 255, 255 }, '^2* ' .. GetPlayerName(source) ..' left (' .. reason .. ')')
end)
RegisterCommand('say', function(source, args, rawCommand)
routeMessage(source, (source == 0) and 'console' or GetPlayerName(source), rawCommand:sub(5), nil, true)
end)
-- command suggestions for clients
local function refreshCommands(player)
if GetRegisteredCommands then
local registeredCommands = GetRegisteredCommands()
local suggestions = {}
for _, command in ipairs(registeredCommands) do
if IsPlayerAceAllowed(player, ('command.%s'):format(command.name)) then
table.insert(suggestions, {
name = '/' .. command.name,
help = ''
})
end
end
TriggerClientEvent('chat:addSuggestions', player, suggestions)
end
end
AddEventHandler('chat:init', function()
local source = source
refreshCommands(source)
for _, modeData in pairs(modes) do
local clObj = {
name = modeData.name,
displayName = modeData.displayName,
color = modeData.color or '#fff',
isChannel = modeData.isChannel,
isGlobal = modeData.isGlobal,
}
if not modeData.seObject or IsPlayerAceAllowed(source, modeData.seObject) then
TriggerClientEvent('chat:addMode', source, clObj)
end
end
end)
AddEventHandler('onServerResourceStart', function(resName)
Wait(500)
for _, player in ipairs(GetPlayers()) do
refreshCommands(player)
end
end)
AddEventHandler('onResourceStop', function(resName)
unregisterHooks(resName)
end)