293 lines
		
	
	
		
			No EOL
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			293 lines
		
	
	
		
			No EOL
		
	
	
		
			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) | 
