config.server.lua

Preview File Updated: v1.4.1 - 05/05/2026

SV = {}

-- ██╗    ██╗███████╗██████╗ ██╗  ██╗ ██████╗  ██████╗ ██╗  ██╗███████╗
-- ██║    ██║██╔════╝██╔══██╗██║  ██║██╔═══██╗██╔═══██╗██║ ██╔╝██╔════╝
-- ██║ █╗ ██║█████╗  ██████╔╝███████║██║   ██║██║   ██║█████╔╝ ███████╗
-- ██║███╗██║██╔══╝  ██╔══██╗██╔══██║██║   ██║██║   ██║██╔═██╗ ╚════██║
-- ╚███╔███╔╝███████╗██████╔╝██║  ██║╚██████╔╝╚██████╔╝██║  ██╗███████║
--  ╚══╝╚══╝ ╚══════╝╚═════╝ ╚═╝  ╚═╝ ╚═════╝  ╚═════╝ ╚═╝  ╚═╝╚══════╝
SV.Webhook = function(data)
    if Config.Logs == 'fivemanage' then
        exports.fmsdk:LogMessage(
            "info",
            '[VMS Housing] ' .. data.title,
            data.metadata
        )
    else
        local token = Webhooks[data.id]
        if not token or token == "" then
            return
        end
        local embeds = {{
            ["title"] = data.title,
            ["type"] = "rich",
            ["description"] = data.description,
            ["color"] = 0,
            ["footer"] = {
                ["text"] = data.footer .. ' - ' .. os.date(),
            },
        }}
        PerformHttpRequest(token, function(err, text, headers) end, 'POST', json.encode({embeds = embeds}), {['Content-Type'] = 'application/json'})
    end
end



-- ███████╗██████╗  █████╗ ███╗   ███╗███████╗██╗    ██╗ ██████╗ ██████╗ ██╗  ██╗
-- ██╔════╝██╔══██╗██╔══██╗████╗ ████║██╔════╝██║    ██║██╔═══██╗██╔══██╗██║ ██╔╝
-- █████╗  ██████╔╝███████║██╔████╔██║█████╗  ██║ █╗ ██║██║   ██║██████╔╝█████╔╝ 
-- ██╔══╝  ██╔══██╗██╔══██║██║╚██╔╝██║██╔══╝  ██║███╗██║██║   ██║██╔══██╗██╔═██╗ 
-- ██║     ██║  ██║██║  ██║██║ ╚═╝ ██║███████╗╚███╔███╔╝╚██████╔╝██║  ██║██║  ██╗
-- ╚═╝     ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝     ╚═╝╚══════╝ ╚══╝╚══╝  ╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═╝
SV.GetPlayer = function(src)
    if Config.Core == "ESX" then
        return Core.GetPlayerFromId(src)
    elseif Config.Core == "QB-Core" then
        return Core.Functions.GetPlayer(src)
    end
end

SV.GetSource = function(xPlayer)
    if Config.Core == "ESX" then
        return xPlayer.source
    elseif Config.Core == "QB-Core" then
        return xPlayer.PlayerData.source
    end
end

SV.GetIdentifier = function(xPlayer)
    if Config.Core == "ESX" then
        return xPlayer.identifier
    elseif Config.Core == "QB-Core" then
        return xPlayer.PlayerData.citizenid
    end
end

SV.GetPlayerByIdentifier = function(identifier)
    if Config.Core == "ESX" then
        return Core.GetPlayerFromIdentifier(identifier)
    elseif Config.Core == "QB-Core" then
        return Core.Functions.GetPlayerByCitizenId(identifier)
    end
end

SV.GetCharacterName = function(xPlayer, offline)
    if Config.Core == "ESX" then
        if offline then
            local response = MySQL.query.await('SELECT `firstname`, `lastname` FROM `users` WHERE `identifier` = ?', {
                xPlayer.identifier
            })
    
            if response then
                local data = response[1]
                return data.firstname .. ' ' .. data.lastname
            end
        else
            return xPlayer.getName()
        end

    elseif Config.Core == "QB-Core" then
        if offline then
            local response = MySQL.query.await('SELECT `charinfo` FROM `players` WHERE `citizenid` = ?', {
                xPlayer.identifier
            })
    
            if response then
                local data = response[1]
                local charinfo = json.decode(data.charinfo)
                return charinfo.firstname .. ' ' .. charinfo.lastname
            end
        else
            return xPlayer.PlayerData.charinfo.firstname .. ' ' .. xPlayer.PlayerData.charinfo.lastname
        end

    end
end

SV.GetPlayerGroup = function(xPlayer)
    if Config.Core == "ESX" then
        return xPlayer.group
    elseif Config.Core == "QB-Core" then
        if GetResourceState('qbx_core') == 'started' then
            -- Sort your permissions from highest to lowest so that when it detects the highest role, the function returns the highest one!
            if IsPlayerAceAllowed(xPlayer.PlayerData.source, 'god') then
                return 'god'
            elseif IsPlayerAceAllowed(xPlayer.PlayerData.source, 'admin') then
                return 'admin'
            elseif IsPlayerAceAllowed(xPlayer.PlayerData.source, 'mod') then
                return 'mod'
            elseif IsPlayerAceAllowed(xPlayer.PlayerData.source, 'support') then
                return 'support'
            end
        else
            local permissions = Core.Functions.GetPermission(xPlayer.PlayerData.source)
    
            -- Sort your permissions from highest to lowest so that when it detects the highest role, the function returns the highest one!
            if permissions['god'] then
                return 'god'
            elseif permissions['admin'] then
                return 'admin'
            elseif permissions['mod'] then
                return 'mod'
            end
        end

        return nil
    end
end

SV.GetPlayerJob = function(xPlayer, type)
    if Config.Core == "ESX" and xPlayer.job then
        if type == "table" then
            return xPlayer.job
        end
        if type == "name" then
            return xPlayer.job.name
        end
        if type == "label" then
            return xPlayer.job.label
        end
        if type == "grade" then
            return xPlayer.job.grade
        end
        if type == "grade_name" then
            return xPlayer.job.grade_name
        end
    elseif Config.Core == "QB-Core" and xPlayer.PlayerData.job then
        if type == "table" then
            return xPlayer.PlayerData.job
        end
        if type == "name" then
            return xPlayer.PlayerData.job.name
        end
        if type == "label" then
            return xPlayer.PlayerData.job.label
        end
        if type == "grade" then
            return xPlayer.PlayerData.job.grade.level
        end
        if type == "grade_name" then
            return xPlayer.PlayerData.job.grade.name
        end
    end
    return nil
end

SV.GetJobsOnline = function(jobName)
    if Config.Core == "ESX" then
        return #Core.GetExtendedPlayers('job', jobName)

    elseif Config.Core == "QB-Core" then
        local players, count = Core.Functions.GetPlayersOnDuty(jobName)
        return count
        
    end
end

SV.RegisterCommand = function(command, groups, cb, help, suggestions)
    if Config.Core == "ESX" then
        local arguments = {}

        if suggestions then
            for k, v in pairs(suggestions) do
                table.insert(arguments, {
                    name = v.name,
                    help = v.label,
                    type = 'any'
                })
            end
        end
        
        Core.RegisterCommand(command, groups, function(xPlayer, args, showError)
            local src = SV.GetSource(xPlayer)
            cb(src, args)
        end, false, {
            help = help,
            arguments = arguments
        })
    elseif Config.Core == "QB-Core" then
        local arguments = {}

        if suggestions then
            for k, v in pairs(suggestions) do
                table.insert(arguments, {
                    name = v.name,
                    help = v.label
                })
            end
        end
        
        Core.Commands.Add(command, help, arguments, false, function(source, args)
            cb(source, args)
        end, groups)
    end
end


-- ███╗   ███╗ ██████╗ ███╗   ██╗███████╗██╗   ██╗
-- ████╗ ████║██╔═══██╗████╗  ██║██╔════╝╚██╗ ██╔╝
-- ██╔████╔██║██║   ██║██╔██╗ ██║█████╗   ╚████╔╝ 
-- ██║╚██╔╝██║██║   ██║██║╚██╗██║██╔══╝    ╚██╔╝  
-- ██║ ╚═╝ ██║╚██████╔╝██║ ╚████║███████╗   ██║   
-- ╚═╝     ╚═╝ ╚═════╝ ╚═╝  ╚═══╝╚══════╝   ╚═╝   
SV.GetMoney = function(xPlayer, moneyType)
    if Config.Core == "ESX" then
        local moneyType = moneyType == 'cash' and 'money' or moneyType
        return xPlayer.getAccount(moneyType).money
    elseif Config.Core == "QB-Core" then
        return xPlayer.Functions.GetMoney(moneyType)
    end
end

---@class AddMoney
---@param reason string
---| 'automaticSale'
---| 'sellFurniture'
---| 'rentalBillPayment'
---| 'rentalContract'
---| 'salesContract'
---| 'salesMarketplace'
---| 'rentalMarketplace'
---| 'deliveredFurniture'
SV.AddMoney = function(xPlayer, moneyType, count, reason)
    if Config.Core == "ESX" then
        local moneyType = moneyType == 'cash' and 'money' or moneyType == 'dirty' and 'black_money' or moneyType
        xPlayer.addAccountMoney(moneyType, count)
    elseif Config.Core == "QB-Core" then
        xPlayer.Functions.AddMoney(moneyType, count)
    end
end

---@class RemoveMoney
---@param reason string
---| 'furniturePurchase'
---| 'purchaseProperty'
---| 'rentalProperty'
---| 'purchaseKey'
---| 'lockReplacement'
---| 'upgradeProperty'
---| 'serviceBillPayment'
---| 'rentalBillPayment'
---| 'rentalContract'
---| 'salesContract'
---| 'salesMarketplace'
---| 'rentalMarketplace'
SV.RemoveMoney = function(xPlayer, moneyType, count, reason)
    if Config.Core == "ESX" then
        local moneyType = moneyType == 'cash' and 'money' or moneyType
        xPlayer.removeAccountMoney(moneyType, count)
    elseif Config.Core == "QB-Core" then
        xPlayer.Functions.RemoveMoney(moneyType, count)
    end
end

---@class AddMoneyOffline
---@param reason string
---| 'rentalBillPayment'
---| 'salesMarketplace'
---| 'rentalMarketplace'
SV.AddMoneyOffline = function(identifier, moneyType, count, reason)
    if Config.Core == "ESX" then
        local moneyType = moneyType == 'cash' and 'money' or moneyType == 'dirty' and 'black_money' or moneyType

        local data = MySQL.query.await("SELECT accounts FROM `users` WHERE `identifier` = @identifier", {
            ['@identifier'] = identifier
        })
        if not data or not data[1] then return end

        local accounts = json.decode(data[1].accounts)
        if not accounts or not accounts[moneyType] then return end

        accounts[moneyType] = tonumber(accounts[moneyType]) + tonumber(count)
        local updatedAccountsJson = json.encode(accounts)
        MySQL.Async.execute('UPDATE `users` SET accounts = @accounts WHERE `identifier` = @identifier', {
            ['@accounts'] = updatedAccountsJson,
            ['@identifier'] = identifier
        })

    elseif Config.Core == "QB-Core" then
        local data = MySQL.query.await("SELECT money FROM `players` WHERE `citizenid` = @citizenid", {
            ['@citizenid'] = identifier
        })
        if not data or not data[1] then return end

        local accounts = json.decode(data[1].money)
        if not accounts or not accounts[moneyType] then return end

        accounts[moneyType] = tonumber(accounts[moneyType]) + tonumber(count)
        local updatedAccountsJson = json.encode(accounts)
        MySQL.Async.execute('UPDATE `players` SET money = @money WHERE `citizenid` = @citizenid', {
            ['@money'] = updatedAccountsJson,
            ['@citizenid'] = identifier
        })
    end
end



-- ██╗████████╗███████╗███╗   ███╗███████╗
-- ██║╚══██╔══╝██╔════╝████╗ ████║██╔════╝
-- ██║   ██║   █████╗  ██╔████╔██║███████╗
-- ██║   ██║   ██╔══╝  ██║╚██╔╝██║╚════██║
-- ██║   ██║   ███████╗██║ ╚═╝ ██║███████║
-- ╚═╝   ╚═╝   ╚══════╝╚═╝     ╚═╝╚══════╝
SV.RegisterUsableItem = function(name, cb)
    if RegisterUsableItem then -- The script looks for functions in ./integration/[inventory]/*
        RegisterUsableItem(name, cb)
    else
        if Config.Core == "ESX" then
            Core.RegisterUsableItem(name, function(src, itemName, itemData)
                cb(src, itemName, {metadata = itemData.metadata})
            end)
        elseif Config.Core == "QB-Core" then
            Core.Functions.CreateUseableItem(name, function(src, item)
                cb(src, item.name, {metadata = item.info})
            end)
        end
    end
end

SV.GetItem = function(src, xPlayer, name, data, search)
    if search == 'key' then
        if GetItem then -- The script looks for functions in ./integration/[inventory]/*
            return GetItem(src, xPlayer, 'house_key', data, search)
        end
    end
end

SV.GetItemCount = function(xPlayer, name)
    if Config.Core == "ESX" then
        return xPlayer.getInventoryItem(name).count
    elseif Config.Core == "QB-Core" then
        return xPlayer.Functions.GetItemByName(name) and xPlayer.Functions.GetItemByName(name).amount or 0
    end
end

SV.AddItem = function(src, xPlayer, name, count, metadata)
    if AddItem then -- The script looks for functions in ./integration/[inventory]/*
        AddItem(src, xPlayer, name, count, metadata)
    else
        if Config.Core == "ESX" then
            xPlayer.addInventoryItem(name, count)
        elseif Config.Core == "QB-Core" then
            xPlayer.Functions.AddItem(name, count)
        end
    end
end

SV.RemoveItem = function(src, xPlayer, name, count)
    if RemoveItem then -- The script looks for functions in ./integration/[inventory]/*
        RemoveItem(src, xPlayer, name, count)
    else
        if Config.Core == "ESX" then
            xPlayer.removeInventoryItem(name, count)
        elseif Config.Core == "QB-Core" then
            xPlayer.Functions.RemoveItem(name, count)
        end
    end
end



--  ██████╗██╗████████╗██╗   ██╗██╗  ██╗ █████╗ ██╗     ██╗     
-- ██╔════╝██║╚══██╔══╝╚██╗ ██╔╝██║  ██║██╔══██╗██║     ██║     
-- ██║     ██║   ██║    ╚████╔╝ ███████║███████║██║     ██║     
-- ██║     ██║   ██║     ╚██╔╝  ██╔══██║██╔══██║██║     ██║     
-- ╚██████╗██║   ██║      ██║   ██║  ██║██║  ██║███████╗███████╗
--  ╚═════╝╚═╝   ╚═╝      ╚═╝   ╚═╝  ╚═╝╚═╝  ╚═╝╚══════╝╚══════╝
SV.AddTax = function(identifier, characterName, propertyName, propertyAddress, type, fromAmount)
    local isCityHall = GetResourceState('vms_cityhall') == 'started'
    if not isCityHall then return false end
    if not Config.CityHallTaxes then return false end

    local amount = 0
    local percentage = 0

    if type == 'property_purchase_tax' then
        if Config.CityHallTaxes.PropertyPurchase.Type == 'percentage' then
            amount = fromAmount
            percentage = Config.CityHallTaxes.PropertyPurchase.Value
        else
            amount = Config.CityHallTaxes.PropertyPurchase.Value
            percentage = 100
        end
        
        if amount >= 1 and percentage >= 1 then
            exports['vms_cityhall']:addPlayerCustomTaxToPay({
                identifier = identifier,
                characterName = characterName,
            }, amount, percentage, (Config.CityHallTaxes.PropertyPurchase.Title):format(propertyName, propertyAddress))
        end

    elseif type == 'property_resale_tax' then
        if Config.CityHallTaxes.PropertyResale.Type == 'percentage' then
            amount = fromAmount
            percentage = Config.CityHallTaxes.PropertyResale.Value
        else
            amount = Config.CityHallTaxes.PropertyResale.Value
            percentage = 100
        end

        if amount >= 1 and percentage >= 1 then
            exports['vms_cityhall']:addPlayerCustomTaxToPay({
                identifier = identifier,
                characterName = characterName,
            }, amount, percentage, (Config.CityHallTaxes.PropertyResale.Title):format(propertyName, propertyAddress))
        end

    elseif type == 'rental_income_tax' then
        if Config.CityHallTaxes.RentalIncome.Type == 'percentage' then
            amount = fromAmount
            percentage = Config.CityHallTaxes.RentalIncome.Value
        else
            amount = Config.CityHallTaxes.RentalIncome.Value
            percentage = 100
        end
        
        if amount >= 1 and percentage >= 1 then
            exports['vms_cityhall']:addPlayerCustomTaxToPay({
                identifier = identifier,
                characterName = characterName,
            }, amount, percentage, (Config.CityHallTaxes.RentalIncome.Title):format(propertyName, propertyAddress))
        end
        
    elseif type == 'property_cadastral_tax' then
        if Config.CityHallTaxes.PropertyCadastralTax.Type == 'percentage' then
            amount = fromAmount
            percentage = Config.CityHallTaxes.PropertyCadastralTax.Value
        else
            amount = Config.CityHallTaxes.PropertyCadastralTax.Value
            percentage = 100
        end
        
        if amount >= 1 and percentage >= 1 then
            exports['vms_cityhall']:addPlayerCustomTaxToPay({
                identifier = identifier,
                characterName = characterName,
            }, amount, percentage, (Config.CityHallTaxes.PropertyCadastralTax.Title):format(propertyName, propertyAddress))
        end

    elseif type == 'agency_rental_income_tax' then
        if Config.CityHallTaxes.AgencyRentalIncome.Type == 'percentage' then
            amount = fromAmount
            percentage = Config.CityHallTaxes.AgencyRentalIncome.Value
        else
            amount = Config.CityHallTaxes.AgencyRentalIncome.Value
            percentage = 0
        end
        
        if percentage == 0 then
            exports['vms_cityhall']:addCompanyFlatTaxAmount(identifier, amount)
        else
            exports['vms_cityhall']:addCompanyCustomTaxAmount(identifier, amount, percentage)
        end

    elseif type == 'agency_purchase_tax' then
        if Config.CityHallTaxes.AgencyPropertyPurchase.Type == 'percentage' then
            amount = fromAmount
            percentage = Config.CityHallTaxes.AgencyPropertyPurchase.Value
        else
            amount = Config.CityHallTaxes.AgencyPropertyPurchase.Value
            percentage = 0
        end
        
        if percentage == 0 then
            exports['vms_cityhall']:addCompanyFlatTaxAmount(identifier, amount)
        else
            exports['vms_cityhall']:addCompanyCustomTaxAmount(identifier, amount, percentage)
        end

    elseif type == 'agency_resale_tax' then
        if Config.CityHallTaxes.AgencyPropertyResale.Type == 'percentage' then
            amount = fromAmount
            percentage = Config.CityHallTaxes.AgencyPropertyResale.Value
        else
            amount = Config.CityHallTaxes.AgencyPropertyResale.Value
            percentage = 0
        end
        
        if percentage == 0 then
            exports['vms_cityhall']:addCompanyFlatTaxAmount(identifier, amount)
        else
            exports['vms_cityhall']:addCompanyCustomTaxAmount(identifier, amount, percentage)
        end
        
    elseif type == 'project_sale_tax' then
        if Config.CityHallTaxes.AuthorProjectSale.Type == 'percentage' then
            amount = fromAmount
            percentage = Config.CityHallTaxes.AuthorProjectSale.Value
        else
            amount = Config.CityHallTaxes.AuthorProjectSale.Value
            percentage = 100
        end

        if amount >= 1 and percentage >= 1 then
            exports['vms_cityhall']:addPlayerCustomTaxToPay({
                identifier = identifier,
                characterName = characterName,
            }, amount, percentage, (Config.CityHallTaxes.AuthorProjectSale.Title):format(propertyName))
        end

    elseif type == 'studio_project_sale_tax' then
        if Config.CityHallTaxes.StudioProjectSale.Type == 'percentage' then
            amount = fromAmount
            percentage = Config.CityHallTaxes.StudioProjectSale.Value
        else
            amount = Config.CityHallTaxes.StudioProjectSale.Value
            percentage = 100
        end

        if percentage == 0 then
            exports['vms_cityhall']:addCompanyFlatTaxAmount(identifier, amount)
        else
            exports['vms_cityhall']:addCompanyCustomTaxAmount(identifier, amount, percentage)
        end
        
    end

    return true -- DO NOT REMOVE!
end



-- ██╗   ██╗ █████╗ ██████╗ ██╗ █████╗ ██████╗ ██╗     ███████╗███████╗
-- ██║   ██║██╔══██╗██╔══██╗██║██╔══██╗██╔══██╗██║     ██╔════╝██╔════╝
-- ██║   ██║███████║██████╔╝██║███████║██████╔╝██║     █████╗  ███████╗
-- ╚██╗ ██╔╝██╔══██║██╔══██╗██║██╔══██║██╔══██╗██║     ██╔══╝  ╚════██║
--  ╚████╔╝ ██║  ██║██║  ██║██║██║  ██║██████╔╝███████╗███████╗███████║
--   ╚═══╝  ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝╚═╝  ╚═╝╚═════╝ ╚══════╝╚══════╝╚══════╝
SV.CanEnterHouse = function(src, xPlayer)
    -- This function is triggered when a player tries to enter the property.
    -- You can return false to block access, for example if they are handcuffed or anything else
    return true
end

SV.CanExitHouse = function(src, xPlayer)
    -- This function is triggered when a player tries to exit the property.
    -- You can return false to block access, for example if they are handcuffed or anything else
    return true
end

Last updated