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) |