✏️Configuration

Detailed configuration information

All config files fields are explained inside the config file in commented code. Some more complicated fields are explained in this page.

Custom mods

You can create custom modifications in the config files that will affect handling fields or assign variables to vehicles when installed. By default custom mods are installed by using an inventory item. If you'd like your own implementation, you could use Additional Handlings API or edit server_config.lua Items section.

You can find custom mods data in config.luaunder Custom Mods section.

Fields

  • uniqueId: string

    • Required id to be used to identify each mod. Must be unique for each mod.

  • itemData: table

    • Contains data for inventory item.

      • consume: boolean

        • Wether or not the item should be removed from inventory after installing the mod.

      • name: ?string

        • Item name (not label) that is used to identify your inventory item. If not entered will default to uniqueId. Make sure to create the items in your database / items config for your inventory for these to work.

  • label: string

    • Label that will be shown in tablet. If not entered will default to identifier.

  • identifier: string

    • Same as slotName. This will identify what slot the part will be installed to. Meaning that if two different mods will be created with the same identifier, only one will be available to be installed at a time.

  • save: boolean

    • Wether or not to save the part in database. If the part would not be saved in database, it will disappear after respawning the vehicle.

  • installedByDefault: boolean

    • If set to true the part will be installed by default to all vehicles. Might be useful for standalone servers that do not have any inventory item system.

  • handlingData: table

    • Data that will be assigned to the vehicle. If key matches any valid handling field it will affect the vehicle's handling field e.g. fInitialDriveForce = {value = 0.01} will increase the fInitialdriveForce by 0.01. If key is equal to enableHandlingFields it will make the fields available for adjusting in tuning tablet. Othewise it will assign the variable to vehicle for any other desired implementation.

      • enableHandlingFields: string[]

        • Make the fields available for adjusting in tuning tablet

      • ['fTractionCurveMax' | 'fInitialDriveForce' | 'fInitialDriveMaxFlatVel' | <...>]: {value: ?number, mult: ?number}

        • Affects the handling value by value or adds the multiplier (mult) when the mod is installed.

      • [string]: any

        • Assigns the variable to vehicle.

Example

Config.CustomMods = {
    -- ...other mods
    {
        uniqueId = 'car_mod_engine_ecu',
        itemData = ,
        label = 'ECU',
        identifier = ,
        save = true,
        handlingData = {
            enableHandlingFields =
            {
                
            }, 
            ,
            
        },
    },
    -- ...other mods
}

Permissions

Permissions will let you set up what is allowed to each group or player. Currently there are a few permissions that will allow or block players from doing certain things with the script.

You can find custom mods data in config.luaunder Permissions section.

  • readXml

    • Allows player to preview XML file output in UI.

  • writeXml

    • Allows player to write XML file data to your server files.

  • writeDefault

    • Allows player to write default handlings to server database.

  • tabletCommand

    • Allows player to use chat command to open tablet. Recommended for servers that do not have inventory system.

  • driftCommand

    • Allows player to use command to enable drift mode. This will not require for player to have a small tablet. Recommended for servers that do not have inventory system.

  • ignoreLimits

    • Allows player to change values without strict limits. This is useful if player has to adjust handlings to write them to server files.

Config.Permissions

Defines what groups have which permissions. You can find the implementation of permissions in configs/server_config.lua or adjust the getPlayerPemissions function to your liking.

  • type: 'frameworkgroup' | 'identifier' | 'ace'

    • 'frameworkgroup' - selects the group that is in value.

    • 'identifier' - selects specific player by identifier. Identifier is entered in value.

    • 'ace' - selects ace permission specified in value.

  • value: string

  • permissions: Permissions

    • Defines which permissions are assigned to the selected player or group.

Configuration files

https://github.com/daZepelin/sb-handlingtuning/blob/master/configs/config.lua
Config = {}

---@type 'kmh' | 'mph' Speed unit used in tablet and handling data
Config.SpeedUnit = 'kmh'

---@type number Speed unit multiplier. 3.6 for kmh / 2.236936 for mph
Config.SpeedMultiplier = 3.6

---@type number How often the telemetry page in small tablet data is refreshed. iIn ms
Config.StatsRefreshTimeout = 200

---@type number Base Nitro Consumption rate. Liters per second
Config.BaseNitroConsumption = 0.1

---@type number Base Nitro fInitialDriveForce value.
Config.BaseNitroDriveForceMultiplier = 0.7

---@type number Base Nitro fInitialDragCoeff multiplier.
Config.BaseNitroDragCoefficientMultiplier = -0.5

-- 8888888888                                                                        888
-- 888                                                                               888
-- 888                                                                               888
-- 8888888    888d888  8888b.  88888b.d88b.   .d88b.  888  888  888  .d88b.  888d888 888  888
-- 888        888P"       "88b 888 "888 "88b d8P  Y8b 888  888  888 d88""88b 888P"   888 .88P
-- 888        888     .d888888 888  888  888 88888888 888  888  888 888  888 888     888888K
-- 888        888     888  888 888  888  888 Y8b.     Y88b 888 d88P Y88..88P 888     888 "88b
-- 888        888     "Y888888 888  888  888  "Y8888   "Y8888888P"   "Y88P"  888     888  888

---@type 'auto' | 'esx' | 'qbcore' | 'custom' If you are using custom framework, set this to 'custom' and configure the framework in client_config.lua and server_config.lua
Config.Framework = 'auto'
Config.FrameworkData = {           -- ONLY change these if you have custom names set on your framework
    SharedObjectEventNameCL = nil, -- Framework shared object event name for client side
    SharedObjectEventNameSV = nil, -- Framework shared object event name for server side
}

---@type 'framework' | 'chat' | 'custom' Notification system used for displaying messages. Can be configured in client_config.lua
Config.Notifications = 'framework'

---@type 'auto' | 'oxmysql' | 'mysql-async' | 'ghmattimysql' | 'NO'
Config.MySQLScript = 'auto'

---@type 'license' | 'steam' | 'discord' | 'ip' Identifier used to save/load player's presets in database
Config.PlayerIdentifier = 'license'

-- Recommended to keep at true. Only use false if you are not using any database.
Config.UseDatabaseHandlingSaving = true

-- Recommended to keep at true. Only use false if you are not using any database.
Config.UseDatabasePresetsSaving = true

-- Recommended to keep at true. Only use false if you are not using any database.
Config.UseDatabaseDefaultHandlingSaving = true

-- Recommended to keep at false. Only use true if you are not using any database.
Config.SaveDefaultsToFile = false

-- If true, will run database checks on server start. Recommended to keep at true.
Config.RunDatabaseChecks = true

-- If true, will automatically save missing default values to database when the vehicle handling is required.
-- WARNING: Recommended to keep at false and save all default values manually by using /admin:cars
Config.AutomaticallySaveMissingDefaultValues = false

AddEventHandler('sb-handlingtuning:frameworkLoaded', function(framework)
    if table.has({ 'standalone', 'custom' }, framework) then
        Config.SaveDefaultsToFile = true
    end

    if IsDuplicityVersion() then
        -- Owned vehicles table in database information.
        -- tableName - Name of table in which all owned vehicles are stored
        -- ownerColumn - Owned vehicles owner column in DB table
        -- plateColumn - Vehicle plate column in DB table
        if SB.FrameworkType == 'qbcore' then
            Config.OwnedVehiclesDataTable = {
                tableName = 'player_vehicles', ownerColumn = 'citizenid', plateColumn = 'plate' -- QBCore
            }
        elseif SB.FrameworkType == 'esx' then
            Config.OwnedVehiclesDataTable = {
                tableName = 'owned_vehicles', ownerColumn = 'owner', plateColumn = 'plate' -- ESX
            }
        else
            Config.OwnedVehiclesDataTable = {}
            Config.UseDatabaseHandlingSaving = false
            Config.UseDatabasePresetsSaving = false
        end

        DB_INFO = Config.OwnedVehiclesDataTable
    end
end)

if IsDuplicityVersion() then
    AddEventHandler('sb-handlingtuning:databaseScriptDetected', function(databaseScript)
        if databaseScript == 'NO' then
            Config.UseDatabaseHandlingSaving = false
            Config.UseDatabasePresetsSaving = false
            Config.SaveDefaultsToFile = true
            Config.RunDatabaseChecks = false
        end
    end)
end

-- d8888
-- d88888
-- d88P888
-- d88P 888  .d8888b  .d8888b  .d88b.  .d8888b  .d8888b
-- d88P  888 d88P"    d88P"    d8P  Y8b 88K      88K
-- d88P   888 888      888      88888888 "Y8888b. "Y8888b.
-- d8888888888 Y88b.    Y88b.    Y8b.          X88      X88
-- d88P     888  "Y8888P  "Y8888P  "Y8888   88888P'  88888P'

-- Limited editing allows for all players to use the tablet, but only adjust vehicles handling values for limited number
-- You can adjust how much of each field can be changed by editing configs/config.js Min / max > changeLimit
--
-- For example if vehicle's standard mass is 2000, Min.changeLimit is 200 and max.changeLimit is 500,
-- then player will have 1800 - 2500 to play around with, as 2000 - 200 = 1800, 2000 + 500 = 2500.

-- If value in config.js is not added, this will be used instead
Config.DefaultChangeLimit = 0.1

---@class Permissions
---@field readXml ?boolean allows to view and copy xml file in UI
---@field writeXml ?boolean allows to write xml data to server handling.meta
---@field writeDefault ?boolean allows to write default handlings data to database / file
---@field tabletCommand ?boolean allows to open tablet without having the inventory item
---@field driftCommand ?boolean allows to use drift mode without having the inventory item or required mods on car
---@field ignoreLimits ?boolean allows to ignore change limits


---@type Permissions Defines what permissions are given to each player by default
Config.DefaultPermissions = {
    readXml = false,
    writeXml = false,
    writeDefault = false,
    tabletCommand = false,
    driftCommand = false,
    ignoreLimits = false,
}


--- DO NOT EDIT THE FULL_PERMS
---@type Permissions
FULL_PERMS = {
    readXml = true,
    writeXml = true,
    writeDefault = true,
    tabletCommand = true,
    driftCommand = true,
    ignoreLimits = true,
}

--- Permissions and their conditions
--- type: 'identifier' | 'frameworkgroup' | 'ace' | 'default'
--- value: data for type e.g. framework group name, ace permission name, identifier value
--- permissions: Permissions that are assigned to selector
--- You can see `getPlayerPemissions` function in server_config.lua for implementation of permissions
---@type {type: string, value?: string, permissions: Permissions}[]
Config.Permissions = {
    { type = 'frameworkgroup', value = 'god',        permissions = FULL_PERMS },
    { type = 'frameworkgroup', value = 'superadmin', permissions = FULL_PERMS },
    { type = 'frameworkgroup', value = 'admin',      permissions = FULL_PERMS },
    -- {type = 'identifier', value = 'steam:abc123', permissions = {}},
    -- {type = 'ace', value = 'handlingtuning', permissions = {}},
}

-- Name of inventory item
-- Follor the documentation on how to add new items:
-- https://squirrel-bracket.gitbook.io/squirrel-bracket-documentation/v/handling-tuning/installation#create-inventory-items
Config.TabletItemName = 'tunertablet'
Config.SmallTabletItemName = 'smalltunertablet'

-- Command for admins full tablet access
Config.TabletCommand = 'tablet'
Config.SmallTabletCommand = 'smalltablet'

-- These fields will be disabled by default, in order to enable them, you will need to add mods to the car that enable them.
-- For example, if you add 'fSteeringLock' in Config.DisabledFields it will not be shown in tablet, unless you add a mod that enables it and install it on the car:
-- {
--     itemData = {name = 'car_mod_lock_kit', consume = true},
--     identifier = 'lockKit',
--     save = true,
--     handlingData = {enableHandlingFields = {'fSteeringLock'}}
-- }
Config.DisabledFields = {
    'nMonetaryValue',
    'fPetrolTankVolume',
    'fSteeringLock',
    'fInitialDriveForce',
    'fDownforceModifier',
    'fPetrolTankVolume',
    'fDriveInertia',
    'fRollCentreHeightRear',
    'fDriveBiasFront',
    'fPercentSubmerged',
    'fEngineDamageMult',
    'fLowSpeedTractionLossMult',
    'fWeaponDamageScaledToVehHealthMult',
    'fOilVolume',
    'fSeatOffsetDistX',
    'fTractionCurveLateral',
    'fSuspensionForce',
    'fMass',
    'fSeatOffsetDistY',
    'fSeatOffsetDistZ',
    'fTractionSpringDeltaMax',
    'nInitialDriveGears',
    'fBoostMaxSpeed',
    'fCollisionDamageMult',
    'fWeaponDamageMult',
    'vecInertiaMultiplier',
    'vecCentreOfMassOffset',
    'fSuspensionCompDamp',
    'fRocketBoostCapacity',
}

-- 8888888b.          d8b  .d888 888        888b     d888               888
-- 888  "Y88b         Y8P d88P"  888        8888b   d8888               888
-- 888    888             888    888        88888b.d88888               888
-- 888    888 888d888 888 888888 888888     888Y88888P888  .d88b.   .d88888  .d88b.
-- 888    888 888P"   888 888    888        888 Y888P 888 d88""88b d88" 888 d8P  Y8b
-- 888    888 888     888 888    888        888  Y8P  888 888  888 888  888 88888888
-- 888  .d88P 888     888 888    Y88b.      888   "   888 Y88..88P Y88b 888 Y8b.
-- 8888888P"  888     888 888     "Y888     888       888  "Y88P"   "Y88888  "Y8888

-- Master switch for drift mode
Config.DriftModeEnabled = true

-- If false the drift mode won't be able to be toggled while moving

Config.AllowMovingDriftToggle = false -- If false the drift mode won't be able to be toggled while moving

---@alias DriftFieldFormula fun(defaultValue: number, speed: number, angle: number, multiplier: number): number

---@class DriftModeField
---@field label string what is displayed in tablet under the Drift group configuration
---@field description string what is displayed in tablet under the Drift group configuration
---@field formula DriftFieldFormula formula used to calculate changes for the field when vehicle is moving sideways
---@field defaultValue ?number

---Defines data for drift mode
---@type table<string, DriftModeField>
Config.DriftModeFields = {
    fInitialDriveForce = {
        label = 'Drive Force multiplier on slide',
        description = 'How much of drive force will be increased when vehicle slides',
        formula = function(defaultValue, speed, angle, multiplier)
            local result = defaultValue + ((angle / 15) * multiplier)
            return result
        end,
        min = 0.0,
        max = 1.0,
        defaultValue = 0.5,
    },
    fSteeringLock = {
        label = 'Steering lock multiplier on slide',
        formula = function(defaultValue, speed, angle, multiplier)
            local result = math.max(defaultValue, math.min(defaultValue + (angle * multiplier), 70.0))
            return result
        end,
        min = 0.0,
        max = 1.0,
        defaultValue = 0.5,
    },
    fTractionCurveMin = {
        label = 'Traction decrease multiplier on slide',
        formula = function(defaultValue, speed, angle, multiplier)
            local result = math.max(defaultValue - angle / (17 * multiplier), 1.0)
            return result
        end,
        min = 0.0,
        max = 1.0,
        defaultValue = 0.5,
    },
    fDriveBiasFront = {
        label = 'Drive bias multiplier on slide',
        formula = function(defaultValue, speed, angle, multiplier)
            local result = math.max(defaultValue, 0.2)
            return result
        end,
        min = 0.0,
        max = 1.0,
        defaultValue = 0.5,
    },
}

-- .d88888b.                    888                               888b     d888               888
-- d88P  Y88b                   888                               8888b   d8888               888
-- 888    888                   888                               88888b.d88888               888
-- 888        888  888 .d8888b  888888  .d88b.  88888b.d88b.      888Y88888P888  .d88b.   .d88888 .d8888b
-- 888        888  888 88K      888    d88""88b 888 "888 "88b     888 Y888P 888 d88""88b d88" 888 88K
-- 888    888 888  888 "Y8888b. 888    888  888 888  888  888     888  Y8P  888 888  888 888  888 "Y8888b.
-- Y88b  d88P Y88b 888      X88 Y88b.  Y88..88P 888  888  888     888   "   888 Y88..88P Y88b 888      X88
--  "Y8888P"   "Y88888  88888P'  "Y888  "Y88P"  888  888  888     888       888  "Y88P"   "Y88888  88888P'

-- You can add more. Read more on docs:
-- https://squirrel-bracket.gitbook.io/squirrel-bracket-documentation/v/handling-tuning/configuration#custom-mods
-- Read docs on how to add inventory items:
-- https://squirrel-bracket.gitbook.io/squirrel-bracket-documentation/v/handling-tuning/installation#create-inventory-items
Config.CustomMods = {
    -- Default custom mods required for drift mode and nitro:
    {
        uniqueId = 'car_mod_nitro_kit',
        itemData = { consume = true },
        label = 'Nitro Kit',
        identifier = 'nitroKit',
        save = true,
        installedByDefault = false, -- makes the mod to be installed on all vehicles by default
        handlingData = {},          -- leave it empty
    },
    {
        uniqueId = 'car_mod_nitro_tank',
        itemData = { consume = true },
        label = 'Nitro Tank',
        identifier = 'nitroTank',
        save = true,
        allowReapply = true,
        installedByDefault = false,
        handlingData = { size = 3.0, level = 3.0 },
    },
    {
        uniqueId = 'car_mod_diff_lsd',
        itemData = { consume = true },
        label = 'LSD',
        identifier = 'differential',
        save = true,
        handlingData = { type = 'lsd' },
    },
    {
        uniqueId = 'car_mod_diff_welded',
        itemData = { consume = true },
        label = 'Welded Differential',
        identifier = 'differential',
        save = true,
        handlingData = { type = 'welded' },
    },

    -- Additional custom mods:
    {
        uniqueId = 'car_mod_tires',
        itemData = { consume = true },
        label = 'Sport tires',
        identifier = 'tires',
        save = true,
        handlingData = { fTractionCurveMax = { value = 0.3 }, fTractionCurveMin = { value = 0.3 } },
    },
    {
        uniqueId = 'car_mod_stiff_arb',
        itemData = { consume = true },
        label = 'Stiff ARB',
        identifier = 'arb',
        save = true,
        handlingData = { fAntiRollBarForce = { value = 0.2 } },
    },
    {
        uniqueId = 'car_mod_ecu',
        itemData = { consume = false },
        label = 'ECU',
        identifier = 'engine_ecu',
        save = true,
        handlingData = { enableHandlingFields = { 'fInitialDriveForce', 'fInitialDriveMaxFlatVel' }, fInitialDriveForce = { value = 0.01 }, fInitialDriveMaxFlatVel = { value = -2 } },
    },
    {
        uniqueId = 'car_mod_lock_kit',
        itemData = { consume = true },
        label = 'Steer lock kit',
        identifier = 'lockKit',
        save = true,
        handlingData = { enableHandlingFields = { 'fSteeringLock' } }
    }
}

-- 8888888888                   d8b                        .d8888b.
-- 888                          Y8P                       d88P  Y88b
-- 888                                                    Y88b.
-- 8888888    88888b.   .d88b.  888 88888b.   .d88b.       "Y888b.   888  888  888  8888b.  88888b.
-- 888        888 "88b d88P"88b 888 888 "88b d8P  Y8b         "Y88b. 888  888  888     "88b 888 "88b
-- 888        888  888 888  888 888 888  888 88888888           "888 888  888  888 .d888888 888  888
-- 888        888  888 Y88b 888 888 888  888 Y8b.         Y88b  d88P Y88b 888 d88P 888  888 888 d88P
-- 8888888888 888  888  "Y88888 888 888  888  "Y8888       "Y8888P"   "Y8888888P"  "Y888888 88888P"
--                          888                                                             888
--                     Y8b d88P                                                             888
--                      "Y88P"                                                              888

-- Implementation of engine swap feature can be edited in **/*/engine_swap.lua
Config.EngineSwap = {
    -- Master switch for engine swap feature
    enabled = true,

    -- Time it takes to install engine
    time = 20000,

    -- TODO: Add sound transfer
    -- transferSound = false,

    -- Engine swap values that will be transferred from donor engine to target vehicle
    transferValues = {
        'fInitialDragCoeff',
        'nInitialDriveGears',
        'fInitialDriveForce',
        'fDriveInertia',
        'fClutchChangeRateScaleUpShift',
        'fClutchChangeRateScaleDownShift',
        'fInitialDriveMaxFlatVel',
        'fLowSpeedTractionLossMult',
    },

    -- Config for donor engine swap (car from garage is considered to be donor)
    donorSwap = {
        enabled = true,
        swapPrice = 10000,   -- Price for donor swapping engine
        destroyDonor = true, -- If true, donor vehicle will be destroyed after engine swap
        onlyOwned = true,    -- If true, only owned vehicles can be used as donor
    },

    -- Config for engine buy feature
    buyEngine = {
        enabled = false, -- If true, players can buy engines from NPC
        availableEngines = {
            {
                model = 'nero', -- Model of the engine
                label = 'Nero', -- Label that will be displayed in the menu
                price = 100000, -- Price of the engine
            },
            {
                model = 'elegy',
                label = 'Elegy',
                price = 70000,
            }
        }
    },

    -- Engine swap NPC cfg
    npc = {
        coords = vec(472.3840, -1285.2330, 28.5610, 304.0632),
        model = 'mp_m_weapwork_01',
        spawnDist = 50.0,
        showBlip = true,
        blip = {
            sprite = 1,
            color = 1,
            label = 'Engine Swap',
        }
    }
}


-- .d8888b.  888
-- d88P  Y88b 888
-- Y88b.      888
--  "Y888b.   888888  8888b.  88888b.   .d8888b  .d88b.
--     "Y88b. 888        "88b 888 "88b d88P"    d8P  Y8b
--       "888 888    .d888888 888  888 888      88888888
-- Y88b  d88P Y88b.  888  888 888  888 Y88b.    Y8b.
--  "Y8888P"   "Y888 "Y888888 888  888  "Y8888P  "Y8888

-- Enables stance feature. Might be quite performance heavy (adds about 0.04ms for cpu per tick).
Config.EnableStancer = true

-- How many vehicles can be stanced per frame. If you have a lot of vehicles on your server, you might want to lower this number.
Config.MaxStancedVehiclesPerFrame = 3

-- 8888888888 888
-- 888        888
-- 888        888
-- 8888888    888888  .d8888b
-- 888        888    d88P"
-- 888        888    888
-- 888        Y88b.  Y88b.    d8b


Config.SendClientSideErrorsToServer = false

Config.UI = {
    -- Colors used in tablet UI
    colors = {
        primary = '#f5deb3',
        primaryText = '#f5deb3',
    },
    fullPermsColors = {
        primary = '#f5deb3',
        primaryText = '#f5deb3',
    }
}

-- If set to true, the default handling values will prioritized over handling data read from vehicle.
-- Reading data from vehicle might cause issues if you have any other scripts manipulating handling data.
Config.PrioritizeDefaultHandling = true

Last updated

Was this helpful?